/* * Copyright (c) 2011, Chris Johns * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_WAIT_H #include #endif #ifndef WIFEXITED #define WIFEXITED(S) (((S) & 0xff) == 0) #endif #ifndef WEXITSTATUS #define WEXITSTATUS(S) (((S) & 0xff00) >> 8) #endif #ifndef WIFSIGNALED #define WIFSIGNALED(S) (((S) & 0xff) != 0 && ((S) & 0xff) != 0x7f) #endif #ifndef WTERMSIG #define WTERMSIG(S) ((S) & 0x7f) #endif #ifndef WIFSTOPPED #define WIFSTOPPED WIFEXITED #endif #ifndef WSTOPSIG #define WSTOPSIG WEXITSTATUS #endif #if __WIN32__ #define CREATE_MODE (S_IRUSR | S_IWUSR) #define OPEN_FLAGS (O_BINARY) #else #define CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) #define OPEN_FLAGS (0) #endif #include #include "rld.h" #include "rld-process.h" #include namespace rld { namespace process { /** * Global keep of temporary files if true. Used to help debug a system. */ bool keep_temporary_files = false; /** * The temporary files. */ temporary_files temporaries; temporary_files::temporary_files () { } temporary_files::~temporary_files () { clean_up (); } const std::string temporary_files::get (const std::string& suffix, bool keep) { char* temp = ::make_temp_file (suffix.c_str ()); if (!temp) throw rld::error ("bad temp name", "temp-file"); const std::string name = rld::find_replace (temp, RLD_PATH_SEPARATOR_STR RLD_PATH_SEPARATOR_STR, RLD_PATH_SEPARATOR_STR); tempfile_ref ref (name, keep); tempfiles.push_back (ref); return name; } void temporary_files::unlink (const tempfile_ref& ref) { if (!keep_temporary_files && !ref.keep) rld::path::unlink (ref.name); } void temporary_files::erase (const std::string& name) { for (tempfile_container::iterator tfi = tempfiles.begin (); tfi != tempfiles.end (); ++tfi) { if ((*tfi).name == name) { unlink (*tfi); tempfiles.erase (tfi); break; } } } void temporary_files::keep (const std::string& name) { for (tempfile_container::iterator tfi = tempfiles.begin (); tfi != tempfiles.end (); ++tfi) { if ((*tfi).name == name) { (*tfi).keep = true; break; } } } void temporary_files::clean_up () { for (tempfile_container::iterator tfi = tempfiles.begin (); tfi != tempfiles.end (); ++tfi) { unlink (*tfi); } } tempfile::tempfile (const std::string& suffix, bool _keep) : suffix (suffix), overridden (false), fd (-1), level (0) { _name = temporaries.get (suffix, _keep); } tempfile::~tempfile () { close (); temporaries.erase (_name); } void tempfile::open (bool writable) { if (fd < 0) { bool ok = rld::path::check_file (_name); int flags = writable ? O_RDWR : O_RDONLY; int mode = writable ? CREATE_MODE : 0; if (writable && overridden) { flags |= O_CREAT | O_TRUNC | O_APPEND; } else { if (!ok) throw rld::error ("Not found.", "tempfile open:" + _name); } level = 0; fd = ::open (_name.c_str (), flags, mode); if (fd < 0) throw rld::error (::strerror (errno), "tempfile open:" + _name); } } void tempfile::close () { if (fd != -1) { ::close (fd); fd = -1; level = 0; } } void tempfile::override (const std::string& name_) { if (fd >= 0) throw rld::error ("Already open", "tempfile override"); rld::path::unlink (_name); overridden = true; _name = name_ + suffix; } void tempfile::keep () { temporaries.keep (_name); } const std::string& tempfile::name () const { return _name; } size_t tempfile::size () { if (fd < 0) return 0; struct stat sb; if (::stat (_name.c_str (), &sb) == 0) return sb.st_size; return 0; } void tempfile::read (std::string& all) { all.clear (); if (fd != -1) { if (level) all.append (buf, level); level = 0; while (true) { int read = ::read (fd, buf, sizeof (buf) ); if (read < 0) throw rld::error (::strerror (errno), "tempfile get read:" + _name); else if (read == 0) break; else all.append (buf, read); } } } void tempfile::read_line (std::string& line) { line.clear (); if (fd != -1) { bool reading = true; while (reading) { if (level < (sizeof (buf) - 1)) { memset (buf + level, 0, sizeof (buf) - level); int read = ::read (fd, buf + level, sizeof (buf) - level - 1); if (read < 0) throw rld::error (::strerror (errno), "tempfile read:" + _name); else if (read == 0) reading = false; else level += read; } if (level) { char* lf = ::strchr (buf, '\n'); int len = level; if (lf) len = lf - &buf[0] + 1; if (lf || !reading) { line.append (buf, len); level -= len; } if (level) ::memmove (buf, &buf[len], level + 1); reading = false; } } } } void tempfile::write (const std::string& s) { const char* p = s.c_str (); size_t l = s.length (); while (l) { int written = ::write (fd, p, l); if (written < 0) throw rld::error (::strerror (errno), "tempfile write:" + _name); if (written == 0) break; l -= written; } } void tempfile::write_line (const std::string& s) { write (s); write (RLD_LINE_SEPARATOR); } void tempfile::write_lines (const rld::strings& ss) { for (rld::strings::const_iterator ssi = ss.begin (); ssi != ss.end (); ++ssi) { write_line (*ssi); } } void tempfile::output (std::ostream& out) { std::string prefix; output (prefix, out); } void tempfile::output (const std::string& prefix, std::ostream& out, bool line_numbers) { if (fd == -1) { std::string line; int lc = 0; open (); while (true) { read_line (line); ++lc; if (line.empty ()) break; if (!prefix.empty ()) out << prefix << ": "; if (line_numbers) out << lc << ": "; out << line << std::flush; } close (); } } void set_keep_temporary_files () { keep_temporary_files = true; } void temporaries_clean_up () { temporaries.clean_up (); } void args_append (arg_container& args, const std::string& str) { rld::strings ss; rld::split (ss, str); for (rld::strings::iterator ssi = ss.begin (); ssi != ss.end (); ++ssi) { args.push_back (*ssi); } } status execute (const std::string& pname, const std::string& command, const std::string& outname, const std::string& errname) { arg_container args; parse_command_line (command, args); return execute (pname, args, outname, errname); } status execute (const std::string& pname, const arg_container& args, const std::string& outname, const std::string& errname) { if (rld::verbose (RLD_VERBOSE_TRACE)) { std::cout << "execute: "; for (size_t a = 0; a < args.size (); ++a) std::cout << args[a] << ' '; std::cout << std::endl; } const char** cargs = new const char* [args.size () + 1]; for (size_t a = 0; a < args.size (); ++a) cargs[a] = args[a].c_str (); cargs[args.size ()] = 0; int err = 0; int s = 0; const char* serr = pex_one (PEX_LAST | PEX_SEARCH, args[0].c_str (), (char* const*) cargs, pname.c_str (), outname.c_str (), errname.c_str (), &s, &err); delete [] cargs; if (serr) throw rld::error ("execute: " + args[0], serr); else if (err) throw rld::error ("execute: " + args[0], ::strerror (err)); status _status; if (rld::verbose (RLD_VERBOSE_TRACE)) std::cout << "execute: status: "; if (WIFEXITED (s)) { _status.type = status::normal; _status.code = WEXITSTATUS (s); if (rld::verbose (RLD_VERBOSE_TRACE)) std::cout << _status.code << std::endl; } else if (WIFSIGNALED (s)) { _status.type = status::signal; _status.code = WTERMSIG (s); if (rld::verbose (RLD_VERBOSE_TRACE)) std::cout << "signal: " << _status.code << std::endl; } else if (WIFSTOPPED (s)) { _status.type = status::stopped; _status.code = WSTOPSIG (s); if (rld::verbose (RLD_VERBOSE_TRACE)) std::cout << "stopped: " << _status.code << std::endl; } else throw rld::error ("execute: " + args[0], "unknown status returned"); return _status; } /* * The code is based on this C file: * http://cybertiggyr.com/pcm/src/parse.c */ void parse_command_line (const std::string& command, arg_container& args) { enum pstate { pstate_discard_space, pstate_accumulate_quoted, pstate_accumulate_raw }; args.clear (); const char quote = '"'; const char escape = '\\'; pstate state = pstate_discard_space; size_t start = 0; size_t i = 0; while (i < command.size ()) { switch (state) { case pstate_discard_space: if (command[i] == quote) { ++i; start = i; state = pstate_accumulate_quoted; } else if (::isspace (command[i])) { ++i; } else /* includes escape */ { start = i; state = pstate_accumulate_raw; } break; case pstate_accumulate_quoted: if (command[i] == quote) { args.push_back (command.substr (start, i - 1)); ++i; state = pstate_discard_space; } else if ((command[i] == escape) && (command[i + 1] == quote)) { i += 2; } else /* includes space */ { ++i; } break; case pstate_accumulate_raw: if (command[i] == quote) { throw rld::error ("quote in token", "command parse"); } else if ((command[i] == escape) && (command[i + 1] == quote)) { i += 2; } else if (::isspace (command[i])) { args.push_back (command.substr (start, i - 1)); ++i; state = pstate_discard_space; } else { ++i; } break; } } } } }