1*00b67f09SDavid van Moolenbroek //
2*00b67f09SDavid van Moolenbroek // Automated Testing Framework (atf)
3*00b67f09SDavid van Moolenbroek //
4*00b67f09SDavid van Moolenbroek // Copyright (c) 2007 The NetBSD Foundation, Inc.
5*00b67f09SDavid van Moolenbroek // All rights reserved.
6*00b67f09SDavid van Moolenbroek //
7*00b67f09SDavid van Moolenbroek // Redistribution and use in source and binary forms, with or without
8*00b67f09SDavid van Moolenbroek // modification, are permitted provided that the following conditions
9*00b67f09SDavid van Moolenbroek // are met:
10*00b67f09SDavid van Moolenbroek // 1. Redistributions of source code must retain the above copyright
11*00b67f09SDavid van Moolenbroek // notice, this list of conditions and the following disclaimer.
12*00b67f09SDavid van Moolenbroek // 2. Redistributions in binary form must reproduce the above copyright
13*00b67f09SDavid van Moolenbroek // notice, this list of conditions and the following disclaimer in the
14*00b67f09SDavid van Moolenbroek // documentation and/or other materials provided with the distribution.
15*00b67f09SDavid van Moolenbroek //
16*00b67f09SDavid van Moolenbroek // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17*00b67f09SDavid van Moolenbroek // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18*00b67f09SDavid van Moolenbroek // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19*00b67f09SDavid van Moolenbroek // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20*00b67f09SDavid van Moolenbroek // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21*00b67f09SDavid van Moolenbroek // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*00b67f09SDavid van Moolenbroek // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23*00b67f09SDavid van Moolenbroek // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*00b67f09SDavid van Moolenbroek // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25*00b67f09SDavid van Moolenbroek // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26*00b67f09SDavid van Moolenbroek // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27*00b67f09SDavid van Moolenbroek // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*00b67f09SDavid van Moolenbroek //
29*00b67f09SDavid van Moolenbroek
30*00b67f09SDavid van Moolenbroek #if defined(HAVE_CONFIG_H)
31*00b67f09SDavid van Moolenbroek #include "bconfig.h"
32*00b67f09SDavid van Moolenbroek #endif
33*00b67f09SDavid van Moolenbroek
34*00b67f09SDavid van Moolenbroek extern "C" {
35*00b67f09SDavid van Moolenbroek #include <sys/types.h>
36*00b67f09SDavid van Moolenbroek #include <sys/param.h>
37*00b67f09SDavid van Moolenbroek #include <sys/mount.h>
38*00b67f09SDavid van Moolenbroek #include <sys/stat.h>
39*00b67f09SDavid van Moolenbroek
40*00b67f09SDavid van Moolenbroek #include <unistd.h>
41*00b67f09SDavid van Moolenbroek }
42*00b67f09SDavid van Moolenbroek
43*00b67f09SDavid van Moolenbroek #include <cerrno>
44*00b67f09SDavid van Moolenbroek #include <cstdlib>
45*00b67f09SDavid van Moolenbroek #include <cstring>
46*00b67f09SDavid van Moolenbroek
47*00b67f09SDavid van Moolenbroek #include "atf-c++/detail/auto_array.hpp"
48*00b67f09SDavid van Moolenbroek #include "atf-c++/detail/process.hpp"
49*00b67f09SDavid van Moolenbroek #include "atf-c++/detail/sanity.hpp"
50*00b67f09SDavid van Moolenbroek
51*00b67f09SDavid van Moolenbroek #include "fs.hpp"
52*00b67f09SDavid van Moolenbroek #include "user.hpp"
53*00b67f09SDavid van Moolenbroek
54*00b67f09SDavid van Moolenbroek namespace impl = atf::atf_run;
55*00b67f09SDavid van Moolenbroek #define IMPL_NAME "atf::atf_run"
56*00b67f09SDavid van Moolenbroek
57*00b67f09SDavid van Moolenbroek // ------------------------------------------------------------------------
58*00b67f09SDavid van Moolenbroek // Auxiliary functions.
59*00b67f09SDavid van Moolenbroek // ------------------------------------------------------------------------
60*00b67f09SDavid van Moolenbroek
61*00b67f09SDavid van Moolenbroek static void cleanup_aux(const atf::fs::path&, dev_t, bool);
62*00b67f09SDavid van Moolenbroek static void cleanup_aux_dir(const atf::fs::path&, const atf::fs::file_info&,
63*00b67f09SDavid van Moolenbroek bool);
64*00b67f09SDavid van Moolenbroek static void do_unmount(const atf::fs::path&);
65*00b67f09SDavid van Moolenbroek
66*00b67f09SDavid van Moolenbroek // The cleanup routines below are tricky: they are executed immediately after
67*00b67f09SDavid van Moolenbroek // a test case's death, and after we have forcibly killed any stale processes.
68*00b67f09SDavid van Moolenbroek // However, even if the processes are dead, this does not mean that the file
69*00b67f09SDavid van Moolenbroek // system we are scanning is stable. In particular, if the test case has
70*00b67f09SDavid van Moolenbroek // mounted file systems through fuse/puffs, the fact that the processes died
71*00b67f09SDavid van Moolenbroek // does not mean that the file system is truly unmounted.
72*00b67f09SDavid van Moolenbroek //
73*00b67f09SDavid van Moolenbroek // The code below attempts to cope with this by catching errors and either
74*00b67f09SDavid van Moolenbroek // ignoring them or retrying the actions on the same file/directory a few times
75*00b67f09SDavid van Moolenbroek // before giving up.
76*00b67f09SDavid van Moolenbroek static const int max_retries = 5;
77*00b67f09SDavid van Moolenbroek static const int retry_delay_in_seconds = 1;
78*00b67f09SDavid van Moolenbroek
79*00b67f09SDavid van Moolenbroek // The erase parameter in this routine is to control nested mount points.
80*00b67f09SDavid van Moolenbroek // We want to descend into a mount point to unmount anything that is
81*00b67f09SDavid van Moolenbroek // mounted under it, but we do not want to delete any files while doing
82*00b67f09SDavid van Moolenbroek // this traversal. In other words, we erase files until we cross the
83*00b67f09SDavid van Moolenbroek // first mount point, and after that point we only scan and unmount.
84*00b67f09SDavid van Moolenbroek static
85*00b67f09SDavid van Moolenbroek void
cleanup_aux(const atf::fs::path & p,dev_t parent_device,bool erase)86*00b67f09SDavid van Moolenbroek cleanup_aux(const atf::fs::path& p, dev_t parent_device, bool erase)
87*00b67f09SDavid van Moolenbroek {
88*00b67f09SDavid van Moolenbroek try {
89*00b67f09SDavid van Moolenbroek atf::fs::file_info fi(p);
90*00b67f09SDavid van Moolenbroek
91*00b67f09SDavid van Moolenbroek if (fi.get_type() == atf::fs::file_info::dir_type)
92*00b67f09SDavid van Moolenbroek cleanup_aux_dir(p, fi, fi.get_device() == parent_device);
93*00b67f09SDavid van Moolenbroek
94*00b67f09SDavid van Moolenbroek if (fi.get_device() != parent_device)
95*00b67f09SDavid van Moolenbroek do_unmount(p);
96*00b67f09SDavid van Moolenbroek
97*00b67f09SDavid van Moolenbroek if (erase) {
98*00b67f09SDavid van Moolenbroek if (fi.get_type() == atf::fs::file_info::dir_type)
99*00b67f09SDavid van Moolenbroek atf::fs::rmdir(p);
100*00b67f09SDavid van Moolenbroek else
101*00b67f09SDavid van Moolenbroek atf::fs::remove(p);
102*00b67f09SDavid van Moolenbroek }
103*00b67f09SDavid van Moolenbroek } catch (const atf::system_error& e) {
104*00b67f09SDavid van Moolenbroek if (e.code() != ENOENT && e.code() != ENOTDIR)
105*00b67f09SDavid van Moolenbroek throw e;
106*00b67f09SDavid van Moolenbroek }
107*00b67f09SDavid van Moolenbroek }
108*00b67f09SDavid van Moolenbroek
109*00b67f09SDavid van Moolenbroek static
110*00b67f09SDavid van Moolenbroek void
cleanup_aux_dir(const atf::fs::path & p,const atf::fs::file_info & fi,bool erase)111*00b67f09SDavid van Moolenbroek cleanup_aux_dir(const atf::fs::path& p, const atf::fs::file_info& fi,
112*00b67f09SDavid van Moolenbroek bool erase)
113*00b67f09SDavid van Moolenbroek {
114*00b67f09SDavid van Moolenbroek if (erase && ((fi.get_mode() & S_IRWXU) != S_IRWXU)) {
115*00b67f09SDavid van Moolenbroek int retries = max_retries;
116*00b67f09SDavid van Moolenbroek retry_chmod:
117*00b67f09SDavid van Moolenbroek if (chmod(p.c_str(), fi.get_mode() | S_IRWXU) == -1) {
118*00b67f09SDavid van Moolenbroek if (retries > 0) {
119*00b67f09SDavid van Moolenbroek retries--;
120*00b67f09SDavid van Moolenbroek ::sleep(retry_delay_in_seconds);
121*00b67f09SDavid van Moolenbroek goto retry_chmod;
122*00b67f09SDavid van Moolenbroek } else {
123*00b67f09SDavid van Moolenbroek throw atf::system_error(IMPL_NAME "::cleanup(" +
124*00b67f09SDavid van Moolenbroek p.str() + ")", "chmod(2) failed",
125*00b67f09SDavid van Moolenbroek errno);
126*00b67f09SDavid van Moolenbroek }
127*00b67f09SDavid van Moolenbroek }
128*00b67f09SDavid van Moolenbroek }
129*00b67f09SDavid van Moolenbroek
130*00b67f09SDavid van Moolenbroek std::set< std::string > subdirs;
131*00b67f09SDavid van Moolenbroek {
132*00b67f09SDavid van Moolenbroek bool ok = false;
133*00b67f09SDavid van Moolenbroek int retries = max_retries;
134*00b67f09SDavid van Moolenbroek while (!ok) {
135*00b67f09SDavid van Moolenbroek INV(retries > 0);
136*00b67f09SDavid van Moolenbroek try {
137*00b67f09SDavid van Moolenbroek const atf::fs::directory d(p);
138*00b67f09SDavid van Moolenbroek subdirs = d.names();
139*00b67f09SDavid van Moolenbroek ok = true;
140*00b67f09SDavid van Moolenbroek } catch (const atf::system_error& e) {
141*00b67f09SDavid van Moolenbroek retries--;
142*00b67f09SDavid van Moolenbroek if (retries == 0)
143*00b67f09SDavid van Moolenbroek throw e;
144*00b67f09SDavid van Moolenbroek ::sleep(retry_delay_in_seconds);
145*00b67f09SDavid van Moolenbroek }
146*00b67f09SDavid van Moolenbroek }
147*00b67f09SDavid van Moolenbroek INV(ok);
148*00b67f09SDavid van Moolenbroek }
149*00b67f09SDavid van Moolenbroek
150*00b67f09SDavid van Moolenbroek for (std::set< std::string >::const_iterator iter = subdirs.begin();
151*00b67f09SDavid van Moolenbroek iter != subdirs.end(); iter++) {
152*00b67f09SDavid van Moolenbroek const std::string& name = *iter;
153*00b67f09SDavid van Moolenbroek if (name != "." && name != "..")
154*00b67f09SDavid van Moolenbroek cleanup_aux(p / name, fi.get_device(), erase);
155*00b67f09SDavid van Moolenbroek }
156*00b67f09SDavid van Moolenbroek }
157*00b67f09SDavid van Moolenbroek
158*00b67f09SDavid van Moolenbroek static
159*00b67f09SDavid van Moolenbroek void
do_unmount(const atf::fs::path & in_path)160*00b67f09SDavid van Moolenbroek do_unmount(const atf::fs::path& in_path)
161*00b67f09SDavid van Moolenbroek {
162*00b67f09SDavid van Moolenbroek // At least, FreeBSD's unmount(2) requires the path to be absolute.
163*00b67f09SDavid van Moolenbroek // Let's make it absolute in all cases just to be safe that this does
164*00b67f09SDavid van Moolenbroek // not affect other systems.
165*00b67f09SDavid van Moolenbroek const atf::fs::path& abs_path = in_path.is_absolute() ?
166*00b67f09SDavid van Moolenbroek in_path : in_path.to_absolute();
167*00b67f09SDavid van Moolenbroek
168*00b67f09SDavid van Moolenbroek #if defined(HAVE_UNMOUNT)
169*00b67f09SDavid van Moolenbroek int retries = max_retries;
170*00b67f09SDavid van Moolenbroek retry_unmount:
171*00b67f09SDavid van Moolenbroek if (unmount(abs_path.c_str(), 0) == -1) {
172*00b67f09SDavid van Moolenbroek if (errno == EBUSY && retries > 0) {
173*00b67f09SDavid van Moolenbroek retries--;
174*00b67f09SDavid van Moolenbroek ::sleep(retry_delay_in_seconds);
175*00b67f09SDavid van Moolenbroek goto retry_unmount;
176*00b67f09SDavid van Moolenbroek } else {
177*00b67f09SDavid van Moolenbroek throw atf::system_error(IMPL_NAME "::cleanup(" + in_path.str() +
178*00b67f09SDavid van Moolenbroek ")", "unmount(2) failed", errno);
179*00b67f09SDavid van Moolenbroek }
180*00b67f09SDavid van Moolenbroek }
181*00b67f09SDavid van Moolenbroek #else
182*00b67f09SDavid van Moolenbroek // We could use umount(2) instead if it was available... but
183*00b67f09SDavid van Moolenbroek // trying to do so under, e.g. Linux, is a nightmare because we
184*00b67f09SDavid van Moolenbroek // also have to update /etc/mtab to match what we did. It is
185*00b67f09SDavid van Moolenbroek // satf::fser to just leave the system-specific umount(8) tool deal
186*00b67f09SDavid van Moolenbroek // with it, at least for now.
187*00b67f09SDavid van Moolenbroek
188*00b67f09SDavid van Moolenbroek const atf::fs::path prog("umount");
189*00b67f09SDavid van Moolenbroek atf::process::argv_array argv("umount", abs_path.c_str(), NULL);
190*00b67f09SDavid van Moolenbroek
191*00b67f09SDavid van Moolenbroek atf::process::status s = atf::process::exec(prog, argv,
192*00b67f09SDavid van Moolenbroek atf::process::stream_inherit(), atf::process::stream_inherit());
193*00b67f09SDavid van Moolenbroek if (!s.exited() || s.exitstatus() != EXIT_SUCCESS)
194*00b67f09SDavid van Moolenbroek throw std::runtime_error("Call to unmount failed");
195*00b67f09SDavid van Moolenbroek #endif
196*00b67f09SDavid van Moolenbroek }
197*00b67f09SDavid van Moolenbroek
198*00b67f09SDavid van Moolenbroek // ------------------------------------------------------------------------
199*00b67f09SDavid van Moolenbroek // The "temp_dir" class.
200*00b67f09SDavid van Moolenbroek // ------------------------------------------------------------------------
201*00b67f09SDavid van Moolenbroek
temp_dir(const atf::fs::path & p)202*00b67f09SDavid van Moolenbroek impl::temp_dir::temp_dir(const atf::fs::path& p)
203*00b67f09SDavid van Moolenbroek {
204*00b67f09SDavid van Moolenbroek atf::auto_array< char > buf(new char[p.str().length() + 1]);
205*00b67f09SDavid van Moolenbroek std::strcpy(buf.get(), p.c_str());
206*00b67f09SDavid van Moolenbroek if (::mkdtemp(buf.get()) == NULL)
207*00b67f09SDavid van Moolenbroek throw system_error(IMPL_NAME "::temp_dir::temp_dir(" +
208*00b67f09SDavid van Moolenbroek p.str() + ")", "mkdtemp(3) failed",
209*00b67f09SDavid van Moolenbroek errno);
210*00b67f09SDavid van Moolenbroek
211*00b67f09SDavid van Moolenbroek m_path.reset(new atf::fs::path(buf.get()));
212*00b67f09SDavid van Moolenbroek }
213*00b67f09SDavid van Moolenbroek
~temp_dir(void)214*00b67f09SDavid van Moolenbroek impl::temp_dir::~temp_dir(void)
215*00b67f09SDavid van Moolenbroek {
216*00b67f09SDavid van Moolenbroek cleanup(*m_path);
217*00b67f09SDavid van Moolenbroek }
218*00b67f09SDavid van Moolenbroek
219*00b67f09SDavid van Moolenbroek const atf::fs::path&
get_path(void) const220*00b67f09SDavid van Moolenbroek impl::temp_dir::get_path(void)
221*00b67f09SDavid van Moolenbroek const
222*00b67f09SDavid van Moolenbroek {
223*00b67f09SDavid van Moolenbroek return *m_path;
224*00b67f09SDavid van Moolenbroek }
225*00b67f09SDavid van Moolenbroek
226*00b67f09SDavid van Moolenbroek // ------------------------------------------------------------------------
227*00b67f09SDavid van Moolenbroek // Free functions.
228*00b67f09SDavid van Moolenbroek // ------------------------------------------------------------------------
229*00b67f09SDavid van Moolenbroek
230*00b67f09SDavid van Moolenbroek atf::fs::path
change_directory(const atf::fs::path & dir)231*00b67f09SDavid van Moolenbroek impl::change_directory(const atf::fs::path& dir)
232*00b67f09SDavid van Moolenbroek {
233*00b67f09SDavid van Moolenbroek atf::fs::path olddir = get_current_dir();
234*00b67f09SDavid van Moolenbroek
235*00b67f09SDavid van Moolenbroek if (olddir != dir) {
236*00b67f09SDavid van Moolenbroek if (::chdir(dir.c_str()) == -1)
237*00b67f09SDavid van Moolenbroek throw system_error(IMPL_NAME "::chdir(" + dir.str() + ")",
238*00b67f09SDavid van Moolenbroek "chdir(2) failed", errno);
239*00b67f09SDavid van Moolenbroek }
240*00b67f09SDavid van Moolenbroek
241*00b67f09SDavid van Moolenbroek return olddir;
242*00b67f09SDavid van Moolenbroek }
243*00b67f09SDavid van Moolenbroek
244*00b67f09SDavid van Moolenbroek void
cleanup(const atf::fs::path & p)245*00b67f09SDavid van Moolenbroek impl::cleanup(const atf::fs::path& p)
246*00b67f09SDavid van Moolenbroek {
247*00b67f09SDavid van Moolenbroek atf::fs::file_info fi(p);
248*00b67f09SDavid van Moolenbroek cleanup_aux(p, fi.get_device(), true);
249*00b67f09SDavid van Moolenbroek }
250*00b67f09SDavid van Moolenbroek
251*00b67f09SDavid van Moolenbroek atf::fs::path
get_current_dir(void)252*00b67f09SDavid van Moolenbroek impl::get_current_dir(void)
253*00b67f09SDavid van Moolenbroek {
254*00b67f09SDavid van Moolenbroek std::auto_ptr< char > cwd;
255*00b67f09SDavid van Moolenbroek #if defined(HAVE_GETCWD_DYN)
256*00b67f09SDavid van Moolenbroek cwd.reset(getcwd(NULL, 0));
257*00b67f09SDavid van Moolenbroek #else
258*00b67f09SDavid van Moolenbroek cwd.reset(getcwd(NULL, MAXPATHLEN));
259*00b67f09SDavid van Moolenbroek #endif
260*00b67f09SDavid van Moolenbroek if (cwd.get() == NULL)
261*00b67f09SDavid van Moolenbroek throw atf::system_error(IMPL_NAME "::get_current_dir()",
262*00b67f09SDavid van Moolenbroek "getcwd() failed", errno);
263*00b67f09SDavid van Moolenbroek
264*00b67f09SDavid van Moolenbroek return atf::fs::path(cwd.get());
265*00b67f09SDavid van Moolenbroek }
266