2 // Automated Testing Framework (atf)
4 // Copyright (c) 2007 The NetBSD Foundation, Inc.
5 // All rights reserved.
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
10 // 1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #if defined(HAVE_CONFIG_H)
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/mount.h>
47 #include "atf-c++/detail/auto_array.hpp"
48 #include "atf-c++/detail/process.hpp"
49 #include "atf-c++/detail/sanity.hpp"
54 namespace impl
= atf::atf_run
;
55 #define IMPL_NAME "atf::atf_run"
57 // ------------------------------------------------------------------------
58 // Auxiliary functions.
59 // ------------------------------------------------------------------------
61 static void cleanup_aux(const atf::fs::path
&, dev_t
, bool);
62 static void cleanup_aux_dir(const atf::fs::path
&, const atf::fs::file_info
&,
64 static void do_unmount(const atf::fs::path
&);
66 // The cleanup routines below are tricky: they are executed immediately after
67 // a test case's death, and after we have forcibly killed any stale processes.
68 // However, even if the processes are dead, this does not mean that the file
69 // system we are scanning is stable. In particular, if the test case has
70 // mounted file systems through fuse/puffs, the fact that the processes died
71 // does not mean that the file system is truly unmounted.
73 // The code below attempts to cope with this by catching errors and either
74 // ignoring them or retrying the actions on the same file/directory a few times
76 static const int max_retries
= 5;
77 static const int retry_delay_in_seconds
= 1;
79 // The erase parameter in this routine is to control nested mount points.
80 // We want to descend into a mount point to unmount anything that is
81 // mounted under it, but we do not want to delete any files while doing
82 // this traversal. In other words, we erase files until we cross the
83 // first mount point, and after that point we only scan and unmount.
86 cleanup_aux(const atf::fs::path
& p
, dev_t parent_device
, bool erase
)
89 atf::fs::file_info
fi(p
);
91 if (fi
.get_type() == atf::fs::file_info::dir_type
)
92 cleanup_aux_dir(p
, fi
, fi
.get_device() == parent_device
);
94 if (fi
.get_device() != parent_device
)
98 if (fi
.get_type() == atf::fs::file_info::dir_type
)
103 } catch (const atf::system_error
& e
) {
104 if (e
.code() != ENOENT
&& e
.code() != ENOTDIR
)
111 cleanup_aux_dir(const atf::fs::path
& p
, const atf::fs::file_info
& fi
,
114 if (erase
&& ((fi
.get_mode() & S_IRWXU
) != S_IRWXU
)) {
115 int retries
= max_retries
;
117 if (chmod(p
.c_str(), fi
.get_mode() | S_IRWXU
) == -1) {
120 ::sleep(retry_delay_in_seconds
);
123 throw atf::system_error(IMPL_NAME
"::cleanup(" +
124 p
.str() + ")", "chmod(2) failed",
130 std::set
< std::string
> subdirs
;
133 int retries
= max_retries
;
137 const atf::fs::directory
d(p
);
140 } catch (const atf::system_error
& e
) {
144 ::sleep(retry_delay_in_seconds
);
150 for (std::set
< std::string
>::const_iterator iter
= subdirs
.begin();
151 iter
!= subdirs
.end(); iter
++) {
152 const std::string
& name
= *iter
;
153 if (name
!= "." && name
!= "..")
154 cleanup_aux(p
/ name
, fi
.get_device(), erase
);
160 do_unmount(const atf::fs::path
& in_path
)
162 // At least, FreeBSD's unmount(2) requires the path to be absolute.
163 // Let's make it absolute in all cases just to be safe that this does
164 // not affect other systems.
165 const atf::fs::path
& abs_path
= in_path
.is_absolute() ?
166 in_path
: in_path
.to_absolute();
168 #if defined(HAVE_UNMOUNT)
169 int retries
= max_retries
;
171 if (unmount(abs_path
.c_str(), 0) == -1) {
172 if (errno
== EBUSY
&& retries
> 0) {
174 ::sleep(retry_delay_in_seconds
);
177 throw atf::system_error(IMPL_NAME
"::cleanup(" + in_path
.str() +
178 ")", "unmount(2) failed", errno
);
182 // We could use umount(2) instead if it was available... but
183 // trying to do so under, e.g. Linux, is a nightmare because we
184 // also have to update /etc/mtab to match what we did. It is
185 // satf::fser to just leave the system-specific umount(8) tool deal
186 // with it, at least for now.
188 const atf::fs::path
prog("umount");
189 atf::process::argv_array
argv("umount", abs_path
.c_str(), NULL
);
191 atf::process::status s
= atf::process::exec(prog
, argv
,
192 atf::process::stream_inherit(), atf::process::stream_inherit());
193 if (!s
.exited() || s
.exitstatus() != EXIT_SUCCESS
)
194 throw std::runtime_error("Call to unmount failed");
198 // ------------------------------------------------------------------------
199 // The "temp_dir" class.
200 // ------------------------------------------------------------------------
202 impl::temp_dir::temp_dir(const atf::fs::path
& p
)
204 atf::auto_array
< char > buf(new char[p
.str().length() + 1]);
205 std::strcpy(buf
.get(), p
.c_str());
206 if (::mkdtemp(buf
.get()) == NULL
)
207 throw system_error(IMPL_NAME
"::temp_dir::temp_dir(" +
208 p
.str() + ")", "mkdtemp(3) failed",
211 m_path
.reset(new atf::fs::path(buf
.get()));
214 impl::temp_dir::~temp_dir(void)
220 impl::temp_dir::get_path(void)
226 // ------------------------------------------------------------------------
228 // ------------------------------------------------------------------------
231 impl::change_directory(const atf::fs::path
& dir
)
233 atf::fs::path olddir
= get_current_dir();
236 if (::chdir(dir
.c_str()) == -1)
237 throw system_error(IMPL_NAME
"::chdir(" + dir
.str() + ")",
238 "chdir(2) failed", errno
);
245 impl::cleanup(const atf::fs::path
& p
)
247 atf::fs::file_info
fi(p
);
248 cleanup_aux(p
, fi
.get_device(), true);
252 impl::get_current_dir(void)
254 std::auto_ptr
< char > cwd
;
255 #if defined(HAVE_GETCWD_DYN)
256 cwd
.reset(getcwd(NULL
, 0));
258 cwd
.reset(getcwd(NULL
, MAXPATHLEN
));
260 if (cwd
.get() == NULL
)
261 throw atf::system_error(IMPL_NAME
"::get_current_dir()",
262 "getcwd() failed", errno
);
264 return atf::fs::path(cwd
.get());