00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00019 #ifndef BOOST_PROCESS_DETAIL_POSIX_OPS_HPP
00020 #define BOOST_PROCESS_DETAIL_POSIX_OPS_HPP
00021
00022 #include <boost/process/environment.hpp>
00023 #include <boost/process/detail/file_handle.hpp>
00024 #include <boost/process/detail/pipe.hpp>
00025 #include <boost/process/detail/stream_info.hpp>
00026 #include <boost/scoped_array.hpp>
00027 #include <boost/assert.hpp>
00028 #include <boost/system/system_error.hpp>
00029 #include <boost/throw_exception.hpp>
00030 #include <map>
00031 #include <utility>
00032 #include <string>
00033 #include <cerrno>
00034 #include <cstdlib>
00035 #include <cstring>
00036 #include <fcntl.h>
00037 #include <unistd.h>
00038
00039 namespace boost {
00040 namespace process {
00041 namespace detail {
00042
00057 template <class Arguments>
00058 inline std::pair<std::size_t, char**> collection_to_posix_argv(const Arguments &args)
00059 {
00060 std::size_t nargs = args.size();
00061 BOOST_ASSERT(nargs > 0);
00062
00063 char **argv = new char*[nargs + 1];
00064 typename Arguments::size_type i = 0;
00065 for (typename Arguments::const_iterator it = args.begin(); it != args.end(); ++it)
00066 {
00067 argv[i] = new char[it->size() + 1];
00068 std::strncpy(argv[i], it->c_str(), it->size() + 1);
00069 ++i;
00070 }
00071 argv[nargs] = 0;
00072
00073 return std::pair<std::size_t, char**>(nargs, argv);
00074 }
00075
00090 inline char **environment_to_envp(const environment &env)
00091 {
00092 char **envp = new char*[env.size() + 1];
00093
00094 environment::size_type i = 0;
00095 for (environment::const_iterator it = env.begin(); it != env.end(); ++it)
00096 {
00097 std::string s = it->first + "=" + it->second;
00098 envp[i] = new char[s.size() + 1];
00099 std::strncpy(envp[i], s.c_str(), s.size() + 1);
00100 ++i;
00101 }
00102 envp[i] = 0;
00103
00104 return envp;
00105 }
00106
00111 typedef std::map<int, stream_info> info_map;
00112
00124 struct posix_setup
00125 {
00132 std::string work_directory;
00133
00140 std::string chroot;
00141
00148 uid_t uid;
00149
00156 uid_t euid;
00157
00164 gid_t gid;
00165
00172 gid_t egid;
00173
00180 posix_setup()
00181 : uid(::getuid()),
00182 euid(::geteuid()),
00183 gid(::getgid()),
00184 egid(::getegid())
00185 {
00186 }
00187
00199 void operator()() const
00200 {
00201 if (!chroot.empty() && ::chroot(chroot.c_str()) == -1)
00202 boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: chroot(2) failed"));
00203
00204 if (gid != ::getgid() && ::setgid(gid) == -1)
00205 boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: setgid(2) failed"));
00206
00207 if (egid != ::getegid() && ::setegid(egid) == -1)
00208 boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: setegid(2) failed"));
00209
00210 if (uid != ::getuid() && ::setuid(uid) == -1)
00211 boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: setuid(2) failed"));
00212
00213 if (euid != ::geteuid() && ::seteuid(euid) == -1)
00214 boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: seteuid(2) failed"));
00215
00216 BOOST_ASSERT(!work_directory.empty());
00217 if (::chdir(work_directory.c_str()) == -1)
00218 boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_setup: chdir(2) failed"));
00219 }
00220 };
00221
00236 inline void setup_input(info_map &info, bool *closeflags, int maxdescs)
00237 {
00238 for (info_map::iterator it = info.begin(); it != info.end(); ++it)
00239 {
00240 int d = it->first;
00241 stream_info &si = it->second;
00242
00243 BOOST_ASSERT(d < maxdescs);
00244 closeflags[d] = false;
00245
00246 switch (si.type_)
00247 {
00248 case stream_info::use_file:
00249 {
00250 int fd = ::open(si.file_.c_str(), O_RDONLY);
00251 if (fd == -1)
00252 boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::setup_input: open(2) of " + si.file_ + " failed"));
00253 if (fd != d)
00254 {
00255 file_handle h(fd);
00256 h.posix_remap(d);
00257 h.release();
00258 }
00259 break;
00260 }
00261 case stream_info::use_handle:
00262 {
00263 if (si.handle_.get() != d)
00264 si.handle_.posix_remap(d);
00265 break;
00266 }
00267 case stream_info::use_pipe:
00268 {
00269 si.pipe_->wend().close();
00270 if (d != si.pipe_->rend().get())
00271 si.pipe_->rend().posix_remap(d);
00272 break;
00273 }
00274 default:
00275 {
00276 BOOST_ASSERT(si.type_ == stream_info::inherit);
00277 break;
00278 }
00279 }
00280 }
00281 }
00282
00297 inline void setup_output(info_map &info, bool *closeflags, int maxdescs)
00298 {
00299 for (info_map::iterator it = info.begin(); it != info.end(); ++it)
00300 {
00301 int d = it->first;
00302 stream_info &si = it->second;
00303
00304 BOOST_ASSERT(d < maxdescs);
00305 closeflags[d] = false;
00306
00307 switch (si.type_)
00308 {
00309 case stream_info::redirect:
00310 {
00311 break;
00312 }
00313 case stream_info::use_file:
00314 {
00315 int fd = ::open(si.file_.c_str(), O_WRONLY);
00316 if (fd == -1)
00317 boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::setup_output: open(2) of " + si.file_ + " failed"));
00318 if (fd != d)
00319 {
00320 file_handle h(fd);
00321 h.posix_remap(d);
00322 h.release();
00323 }
00324 break;
00325 }
00326 case stream_info::use_handle:
00327 {
00328 if (si.handle_.get() != d)
00329 si.handle_.posix_remap(d);
00330 break;
00331 }
00332 case stream_info::use_pipe:
00333 {
00334 si.pipe_->rend().close();
00335 if (d != si.pipe_->wend().get())
00336 si.pipe_->wend().posix_remap(d);
00337 break;
00338 }
00339 default:
00340 {
00341 BOOST_ASSERT(si.type_ == stream_info::inherit);
00342 break;
00343 }
00344 }
00345 }
00346
00347 for (info_map::const_iterator it = info.begin(); it != info.end(); ++it)
00348 {
00349 int d = it->first;
00350 const stream_info &si = it->second;
00351
00352 if (si.type_ == stream_info::redirect)
00353 file_handle::posix_dup(si.desc_to_, d).release();
00354 }
00355 }
00356
00378 template <class Executable, class Arguments>
00379 inline pid_t posix_start(const Executable &exe, const Arguments &args, const environment &env, info_map &infoin, info_map &infoout, const posix_setup &setup)
00380 {
00381 pid_t pid = ::fork();
00382 if (pid == -1)
00383 boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_start: fork(2) failed"));
00384 else if (pid == 0)
00385 {
00386 #if defined(F_MAXFD)
00387 int maxdescs = ::fcntl(-1, F_MAXFD, 0);
00388 if (maxdescs == -1)
00389 maxdescs = ::sysconf(_SC_OPEN_MAX);
00390 #else
00391 int maxdescs = ::sysconf(_SC_OPEN_MAX);
00392 #endif
00393 if (maxdescs == -1)
00394 maxdescs = 1024;
00395 try
00396 {
00397 boost::scoped_array<bool> closeflags(new bool[maxdescs]);
00398 for (int i = 0; i < maxdescs; ++i)
00399 closeflags[i] = true;
00400
00401 setup_input(infoin, closeflags.get(), maxdescs);
00402 setup_output(infoout, closeflags.get(), maxdescs);
00403
00404 for (int i = 0; i < maxdescs; ++i)
00405 {
00406 if (closeflags[i])
00407 ::close(i);
00408 }
00409
00410 setup();
00411 }
00412 catch (const boost::system::system_error &e)
00413 {
00414 ::write(STDERR_FILENO, e.what(), std::strlen(e.what()));
00415 ::write(STDERR_FILENO, "\n", 1);
00416 ::exit(EXIT_FAILURE);
00417 }
00418
00419 std::pair<std::size_t, char**> argcv = collection_to_posix_argv(args);
00420 char **envp = environment_to_envp(env);
00421
00422 ::execve(exe.c_str(), argcv.second, envp);
00423 boost::system::system_error e(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::posix_start: execve(2) failed");
00424
00425 for (std::size_t i = 0; i < argcv.first; ++i)
00426 delete[] argcv.second[i];
00427 delete[] argcv.second;
00428
00429 for (std::size_t i = 0; i < env.size(); ++i)
00430 delete[] envp[i];
00431 delete[] envp;
00432
00433 ::write(STDERR_FILENO, e.what(), std::strlen(e.what()));
00434 ::write(STDERR_FILENO, "\n", 1);
00435 ::exit(EXIT_FAILURE);
00436 }
00437
00438 BOOST_ASSERT(pid > 0);
00439
00440 for (info_map::iterator it = infoin.begin(); it != infoin.end(); ++it)
00441 {
00442 stream_info &si = it->second;
00443 if (si.type_ == stream_info::use_pipe)
00444 si.pipe_->rend().close();
00445 }
00446
00447 for (info_map::iterator it = infoout.begin(); it != infoout.end(); ++it)
00448 {
00449 stream_info &si = it->second;
00450 if (si.type_ == stream_info::use_pipe)
00451 si.pipe_->wend().close();
00452 }
00453
00454 return pid;
00455 }
00456
00472 inline file_handle posix_info_locate_pipe(info_map &info, int desc, bool out)
00473 {
00474 file_handle fh;
00475
00476 info_map::iterator it = info.find(desc);
00477 if (it != info.end())
00478 {
00479 stream_info &si = it->second;
00480 if (si.type_ == stream_info::use_pipe)
00481 {
00482 fh = out ? si.pipe_->rend().release() : si.pipe_->wend().release();
00483 BOOST_ASSERT(fh.valid());
00484 }
00485 info.erase(it);
00486 }
00487
00488 return fh;
00489 }
00490
00491 }
00492 }
00493 }
00494
00495 #endif