--- launchtool-0.8.orig/debian/changelog +++ launchtool-0.8/debian/changelog @@ -0,0 +1,93 @@ +launchtool (0.8-2build1) bionic; urgency=high + + * No change rebuild to pick up -fPIE compiler default + + -- Balint Reczey Tue, 03 Apr 2018 12:31:37 +0000 + +launchtool (0.8-2) unstable; urgency=low + + * Orphaned package + + -- Enrico Zini Tue, 02 Nov 2010 12:37:20 +0000 + +launchtool (0.8-1) unstable; urgency=low + + * New upstream version + - Cleaned up autotools packaging + - Updated autotools files + - Updated README + - Fixed dashes in manpage + * Acknowledge NMU + * Switch to very simple CDBS debian/rules + * Use debhelper 5 + * Updated standards-version + - Moved home page from the Description to the Homepage header + (and point to new home page) + * Updated debian/copyright + + -- Enrico Zini Wed, 08 Jul 2009 12:15:03 +0100 + +launchtool (0.7-1.1) unstable; urgency=low + + * Non-maintainer upload. + * Include proper headers to fix FTBFS with gcc-4.3. (Closes: #417320) + + -- James Vega Sun, 09 Mar 2008 01:18:01 -0500 + +launchtool (0.7-1) unstable; urgency=low + + * New upstream release (now compiles with gcc 3.2) Closes: bug#179796 + + -- Enrico Zini Fri, 17 Jan 2003 16:32:22 +0800 + +launchtool (0.6-3) unstable; urgency=low + + * Build-depend on libpopt-dev (Closes: bug#173527) + + -- Enrico Zini Wed, 18 Dec 2002 13:35:39 +0100 + +launchtool (0.6-2) unstable; urgency=low + + * Uploaded in unstable (Closes: bug#170677) + + -- Enrico Zini Fri, 13 Dec 2002 16:09:56 +0100 + +launchtool (0.6-1) local; urgency=low + + * New upstream release (fixed parser of syslog logging parameters) + + -- Enrico Zini Mon, 25 Nov 2002 12:18:32 +0100 + +launchtool (0.5-1) local; urgency=low + + * New upstream release (fix algorithm for reading child output) + + -- Enrico Zini Thu, 17 Oct 2002 10:26:37 +0200 + +launchtool (0.4-1) local; urgency=low + + * New upstream release (does not hand reading output after the child ends) + + -- Enrico Zini Tue, 8 Oct 2002 11:58:29 +0200 + +launchtool (0.3-1) local; urgency=low + + * New upstream release, with name change to avoid a clash with another + existing "launcher" program + + -- Enrico Zini Thu, 3 Oct 2002 22:50:13 +0200 + +launcher (0.2-1) local; urgency=low + + * New upstream release + * Changed the package description + * Corrected the package depends + + -- Enrico Zini Thu, 3 Oct 2002 21:37:07 +0200 + +launcher (0.1-1) local; urgency=low + + * Initial release + + -- Enrico Zini Tue, 17 Sep 2002 15:17:34 +0200 + --- launchtool-0.8.orig/debian/compat +++ launchtool-0.8/debian/compat @@ -0,0 +1 @@ +5 --- launchtool-0.8.orig/debian/control +++ launchtool-0.8/debian/control @@ -0,0 +1,26 @@ +Source: launchtool +Section: admin +Priority: optional +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Debian QA Group +Build-Depends: debhelper (>> 5.0.0), cdbs, libpopt-dev +Standards-Version: 3.8.2.0 +Homepage: http://www.enricozini.org/sw/launchtool/ + +Package: launchtool +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: command launcher and supervisor + Runs a user-supplied command supervising its execution in + many ways: + . + * controlling the environment + * blocking signals + * logging the output + * changing user and group permissions + * limiting resource usage + * running it chrooted + * restarting it if it fails + * logging execution statistics + * running it continuously + * turning it into a daemon --- launchtool-0.8.orig/debian/copyright +++ launchtool-0.8/debian/copyright @@ -0,0 +1,20 @@ +This package was debianized by Enrico Zini on +Tue, 23 Oct 2001 16:09:46 +0200. + +Upstream Author: Enrico Zini + +Copyright (C) 2002--2009 Enrico Zini + +License: + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +On Debian systems, the complete text of the GNU General Public License can be +found in /usr/share/common-licenses/GPL file. --- launchtool-0.8.orig/debian/dirs +++ launchtool-0.8/debian/dirs @@ -0,0 +1 @@ +usr/bin --- launchtool-0.8.orig/debian/docs +++ launchtool-0.8/debian/docs @@ -0,0 +1,2 @@ +NEWS +README --- launchtool-0.8.orig/debian/rules +++ launchtool-0.8/debian/rules @@ -0,0 +1,4 @@ +#!/usr/bin/make -f + +include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/class/autotools.mk --- launchtool-0.8.orig/src/common/ChildProcess.cc.org +++ launchtool-0.8/src/common/ChildProcess.cc.org @@ -0,0 +1,747 @@ +#include "ChildProcess.h" + +#include + +#include // fork, waitpid, kill, open, getpw*, getgr*, initgroups +#include // open +#include // getrlimit, setrlimit +#include // fork, dup2, pipe, close, setsid, _exit, chdir +#include // open +#include // waitpid +#include // kill +#include // flockfile, funlockfile +#include // is* +#include // getpw* +#include // getgr*, initgroups +#include + +#undef LOGTAG +#define LOGTAG "ChildProcess" + +using namespace std; +using namespace stringf; + +void Process::detachFromTTY() throw (SystemException) +{ + int devnull = open("/dev/null", O_RDWR); + if (devnull == -1) throw FileException(errno, "opening /dev/null for read and write access"); + if (dup2(devnull, 0) == -1) throw SystemException(errno, "redirecting stdin to /dev/null"); + if (dup2(devnull, 1) == -1) throw SystemException(errno, "redirecting stdout to /dev/null"); + if (setsid() == -1) throw SystemException(errno, "trying to become session leader"); + if (dup2(devnull, 2) == -1) throw SystemException(errno, "redirecting stderr to /dev/null"); + close(devnull); +} + +string Process::formatStatus(int status) throw () +{ + string b_status; + + bool exited_normally = WIFEXITED(status); + int exit_code = exited_normally ? WEXITSTATUS(status) : -1; + bool dumped_core = status & 128; + bool signaled = WIFSIGNALED(status); + int signal = signaled ? WTERMSIG(status) : 0; + + if (exited_normally) + if (exit_code == 0) + b_status += "terminated successfully"; + else + addf(b_status, "exited with code %d", exit_code); + else + { + b_status += "was interrupted"; + addf(b_status, ", killed by signal %d", signal); + if (dumped_core) b_status += " (core dumped)"; + } + + return b_status; +} + +void Process::chdir(const string& dir) throw (SystemException) +{ + if (::chdir(dir.c_str()) == -1) + throw SystemException(errno, "changing working directory to " + dir); +} + +void Process::chroot(const string& dir) throw (SystemException) +{ + if (::chroot(dir.c_str()) == -1) + throw SystemException(errno, "changing root directory to " + dir); +} + +mode_t Process::umask(mode_t mask) throw () +{ + return ::umask(mask); +} + +static struct passwd* getUserInfo(const string& user) +{ + if (isdigit(user[0])) + return getpwuid(atoi(user.c_str())); + else + return getpwnam(user.c_str()); +} + +static struct group* getGroupInfo(const string& group) +{ + if (isdigit(group[0])) + return getgrgid(atoi(group.c_str())); + else + return getgrnam(group.c_str()); +} + +static void initGroups(const string& name, gid_t gid) throw (SystemException) +{ + if (::initgroups(name.c_str(), gid) == -1) + throw SystemException(errno, "initializing group access list for user " + + name + " with additional group " + fmt(gid)); +} + +static void set_perms(const string& user, uid_t uid, const string& group, gid_t gid) + throw (SystemException) +{ + initGroups(user, gid); + + if (setgid(gid) == -1) + throw SystemException(errno, "setting group id to " + fmt(gid) + " (" + group + ")"); + + if (setegid(gid) == -1) + throw SystemException(errno, "setting effective group id to " + fmt(gid) + " (" + group + ")"); + + if (setuid(uid) == -1) + throw SystemException(errno, "setting user id to " + fmt(uid) + " (" + user + ")"); + + if (seteuid(uid) == -1) + throw SystemException(errno, "setting effective user id to " + fmt(uid) + " (" + user + ")"); +} + +void Process::setPerms(const string& user) + throw (ConsistencyCheckException, SystemException) +{ + struct passwd* pw = getUserInfo(user); + if (!pw) + throw ConsistencyCheckException("User " + user + " does not exist on this system"); + struct group* gr = getgrgid(pw->pw_gid); + if (!gr) + throw ConsistencyCheckException("Group " + fmt(pw->pw_gid) + + " (primary group of user " + user + ") does not exist on this system"); + + ::set_perms(user, pw->pw_uid, gr->gr_name, gr->gr_gid); +} + +void Process::setPerms(const string& user, const string& group) + throw (ConsistencyCheckException, SystemException) +{ + struct passwd* pw = getUserInfo(user); + if (!pw) + throw ConsistencyCheckException("User " + user + " does not exist on this system"); + struct group* gr = getGroupInfo(group); + if (!gr) + throw ConsistencyCheckException("Group " + group + " does not exist on this system"); + + ::set_perms(user, pw->pw_uid, group, gr->gr_gid); +} + +void Process::setPerms(uid_t user) + throw (ConsistencyCheckException, SystemException) +{ + struct passwd* pw = getpwuid(user); + if (!pw) + throw ConsistencyCheckException("User " + fmt(user) + " does not exist on this system"); + struct group* gr = getgrgid(pw->pw_gid); + if (!gr) + throw ConsistencyCheckException("Group " + fmt(pw->pw_gid) + + " (primary group of user " + fmt(user) + ") does not exist on this system"); + + ::set_perms(pw->pw_name, pw->pw_uid, gr->gr_name, gr->gr_gid); +} + +void Process::setPerms(uid_t user, gid_t group) + throw (ConsistencyCheckException, SystemException) +{ + struct passwd* pw = getpwuid(user); + if (!pw) + throw ConsistencyCheckException("User " + fmt(user) + " does not exist on this system"); + struct group* gr = getgrgid(group); + if (!gr) + throw ConsistencyCheckException("Group " + fmt(group) + " does not exist on this system"); + + ::set_perms(pw->pw_name, pw->pw_uid, gr->gr_name, gr->gr_gid); +} + + +static string describe_rlimit_res_t(__rlimit_resource_t rlim) +{ + switch (rlim) + { + case RLIMIT_CPU: return "CPU time in seconds"; + case RLIMIT_FSIZE: return "Maximum filesize"; + case RLIMIT_DATA: return "max data size"; + case RLIMIT_STACK: return "max stack size"; + case RLIMIT_CORE: return "max core file size"; + case RLIMIT_RSS: return "max resident set size"; + case RLIMIT_NPROC: return "max number of processes"; + case RLIMIT_NOFILE: return "max number of open files"; + case RLIMIT_MEMLOCK: return "max locked-in-memory address spac"; + case RLIMIT_AS: return "address space (virtual memory) limit"; + default: return "unknown"; + } +} + +static void setLimit(__rlimit_resource_t rlim, int val) throw (SystemException) +{ + struct rlimit lim; + if (getrlimit(rlim, &lim) == -1) + throw SystemException(errno, "Getting " + describe_rlimit_res_t(rlim) + " limit"); + lim.rlim_cur = val; + if (setrlimit(rlim, &lim) == -1) + throw SystemException(errno, "Setting " + describe_rlimit_res_t(rlim) + + " limit to " + fmt(val)); +} + +static int getLimit(__rlimit_resource_t rlim, int* max = 0) throw (SystemException) +{ + struct rlimit lim; + if (getrlimit(rlim, &lim) == -1) + throw SystemException(errno, "Getting " + describe_rlimit_res_t(rlim) + " limit"); + if (max) + *max = lim.rlim_max; + return lim.rlim_cur; +} + +int Process::getCPUTimeLimit(int* max = 0) throw (SystemException) { return getLimit(RLIMIT_CPU, max); } +int Process::getFileSizeLimit(int* max = 0) throw (SystemException) { return getLimit(RLIMIT_FSIZE, max); } +int Process::getDataMemoryLimit(int* max = 0) throw (SystemException) { return getLimit(RLIMIT_DATA, max); } +int Process::getCoreSizeLimit(int* max = 0) throw (SystemException) { return getLimit(RLIMIT_CORE, max); } +int Process::getChildrenLimit(int* max = 0) throw (SystemException) { return getLimit(RLIMIT_NPROC, max); } +int Process::getOpenFilesLimit(int* max = 0) throw (SystemException) { return getLimit(RLIMIT_NOFILE, max); } + +void Process::setCPUTimeLimit(int value) throw (SystemException) { setLimit(RLIMIT_CPU, value); } +void Process::setFileSizeLimit(int value) throw (SystemException) { setLimit(RLIMIT_FSIZE, value); } +void Process::setDataMemoryLimit(int value) throw (SystemException) { setLimit(RLIMIT_DATA, value); } +void Process::setCoreSizeLimit(int value) throw (SystemException) { setLimit(RLIMIT_CORE, value); } +void Process::setChildrenLimit(int value) throw (SystemException) { setLimit(RLIMIT_NPROC, value); } +void Process::setOpenFilesLimit(int value) throw (SystemException) { setLimit(RLIMIT_NOFILE, value); } + + +pid_t ChildProcess::fork(int* stdinfd, int* stdoutfd, int* stderrfd) throw (SystemException) +{ + int pipes[3][2]; + + if (stdinfd) + { + if (pipe(pipes[0]) == -1) + throw SystemException(errno, "trying to create the pipe to connect to child standard input"); + *stdinfd = pipes[0][1]; + } + if (stdoutfd) + { + if (pipe(pipes[1]) == -1) + throw SystemException(errno, "trying to create the pipe to connect to child standard output"); + *stdoutfd = pipes[1][0]; + if (stderrfd == stdoutfd) + *stderrfd = pipes[1][0]; + } + + if (stderrfd && stderrfd != stdoutfd) + { + if (pipe(pipes[2]) == -1) + throw SystemException(errno, "trying to create the pipe to connect to child standard error"); + *stderrfd = pipes[2][0]; + } + + flockfile(stdin); + flockfile(stdout); + flockfile(stderr); + + pid_t pid; + if ((pid = ::fork()) == 0) + { + // Tell the logging system we're in a new process + Log::Logger::instance()->setupForkedChild(); + + // Child process + try { + if (stdinfd) + { + // Redirect input from the parent to stdin + if (close(pipes[0][1]) == -1) + throw SystemException(errno, "closing write end of parent stdin pipe"); + if (dup2(pipes[0][0], 0) == -1) + throw SystemException(errno, "dup2-ing parent stdin pipe to stdin"); + if (close(pipes[0][0]) == -1) + throw SystemException(errno, "closing original read end of parent stdin pipe"); + } + + if (stdoutfd) + { + // Redirect output to the parent stdout fd + if (close(pipes[1][0]) == -1) + throw SystemException(errno, "closing read end of parent stdout pipe"); + if (dup2(pipes[1][1], 1) == -1) + throw SystemException(errno, "dup2-ing stdout to parent stdout pipe"); + if (stderrfd == stdoutfd) + if (dup2(pipes[1][1], 2) == -1) + throw SystemException(errno, "dup2-ing stderr to parent stdout/stderr pipe"); + if (close(pipes[1][1]) == -1) + throw SystemException(errno, "closing original write end of parent stdout pipe"); + } + + if (stderrfd && stderrfd != stdoutfd) + { + // Redirect all output to the parent + if (close(pipes[2][0]) == -1) + throw SystemException(errno, "closing read end of parent stderr pipe"); + if (dup2(pipes[2][1], 2) == -1) + throw SystemException(errno, "dup2-ing stderr to parent stderr pipe"); + if (close(pipes[2][1]) == -1) + throw SystemException(errno, "closing original write end of parent stderr pipe"); + } + + // Call the process main function + int res = _proc->main(); + // Allow the child to do its cleanup + delete _proc; + // Return the exit status + _exit(res); + } catch (Exception& e) { + log_err(string(e.type()) + ": " + e.desc()); + } + _exit(EXIT_FAILURE); + } else if (pid < 0) { + funlockfile(stdin); + funlockfile(stderr); + funlockfile(stdout); + if (stdinfd) + { + close(pipes[0][0]); + close(pipes[0][1]); + } + if (stdoutfd) + { + close(pipes[1][0]); + close(pipes[1][1]); + } + if (stderrfd && stderrfd != stdoutfd) + { + close(pipes[2][0]); + close(pipes[2][1]); + } + throw SystemException(errno, "trying to fork the child process to run action script"); + } else { + funlockfile(stdin); + funlockfile(stderr); + funlockfile(stdout); + + // Parent process + _pid = pid; + try { + if (stdinfd) + if (close(pipes[0][0]) == -1) + throw SystemException(errno, "closing read end of stdin child pipe"); + if (stdoutfd) + if (close(pipes[1][1]) == -1) + throw SystemException(errno, "closing write end of stdout child pipe"); + if (stderrfd && stderrfd != stdoutfd) + if (close(pipes[2][1]) == -1) + throw SystemException(errno, "closing write end of stderr child pipe"); + return pid; + } catch (SystemException& e) { + // Try to kill the child process if any errors occurs here + ::kill(pid, 15); + throw e; + } + } +} + +int ChildProcess::wait() throw (SystemException, InterruptedException) +{ + if (_pid == -1) + { + log_debug("Child already finished"); + return -1; // FIXME: for lack of better ideas + } + + int status; + if (waitpid(_pid, &status, 0) == -1) + if (errno == EINTR) + throw InterruptedException("waiting for child termination"); + else + throw SystemException(errno, "waiting for child termination"); + _pid = -1; + return status; +} + +int ChildProcess::wait(struct rusage* ru) throw (SystemException, InterruptedException) +{ + if (_pid == -1) + { + log_debug("Child already finished"); + return -1; // FIXME: for lack of better ideas + } + + int status; + if (wait4(_pid, &status, 0, ru) == -1) + if (errno == EINTR) + throw InterruptedException("waiting for child termination"); + else + throw SystemException(errno, "waiting for child termination"); + _pid = -1; + return status; +} + + +#if 0 +#define _GNU_SOURCE + +#include "runner.h" + +#include // fork, waitpid, kill, getpid, open +#include // open +#include // fork, dup2, execve, pipe, close, read, getpid +#include // open +#include // waitpid +#include // kill +#include // malloc, free, realloc +#include // strndup +#include // asprintf +#include // is* +#include + +#include +#include "userdb.h" + +#undef LOGTAG +#define LOGTAG "runner" + +using namespace stringf; + +extern char **environ; + +///// RunnerException + +RunnerException::RunnerException(const string& context) throw () + : ContextException(context) +{ + pid = getpid(); +} + +string RunnerException::desc() const throw () +{ + return _context + " in process " + fmt(pid); +} + + +///// ChildProcess + +static void set_perms(const char* user, uid_t uid, const char* group, gid_t gid) +{ + if (geteuid() == 0) + { + userdb.initgroups(user, gid); + + if (setgid(gid) == -1) + throw SystemException(errno, fmt("setting group id to %d (%s)", gid, group)); + + if (setegid(gid) == -1) + throw SystemException(errno, fmt("setting effective group id to %d (%s)", gid, group)); + + if (setuid(uid) == -1) + throw SystemException(errno, fmt("setting user id to %d (%s)", uid, user)); + + if (seteuid(uid) == -1) + throw SystemException(errno, fmt("setting effective user id to %d (%s)", uid, user)); + } else + throw RunnerException("missing the necessary privileges to change permissions"); +} + +void ChildProcess::set_perms(const char* usr) +{ + string user; + string group; + uid_t uid = (uid_t)-1; + gid_t gid = (gid_t)-1; + + try { + if (isdigit(usr[0])) + { + uid = atoi(usr); + user = userdb.user_name(uid); + } else { + uid = userdb.user_id(usr); + user = usr; + } + + gid = userdb.user_group(uid); + string group = userdb.group_name(gid); + + } catch (UIDNotFoundException& e) { + throw RunnerException(fmt("user `%s' not found on system", usr)); + } catch (UserNotFoundException& e) { + throw RunnerException(fmt("user `%s' not found on system", usr)); + } catch (GIDNotFoundException& e) { + throw RunnerException(fmt("group `%d' (default group for user" + " %d (%s)) not found on system", + gid, uid, usr)); + } + ::set_perms(user.c_str(), uid, group.c_str(), gid); +} + +void ChildProcess::set_perms(const char* usr, const char* grp) +{ + string user; + string group; + uid_t uid; + gid_t gid; + + try { + if (isdigit(usr[0])) + { + uid = atoi(usr); + user = userdb.user_name(uid); + } else { + uid = userdb.user_id(usr); + user = usr; + } + + if (isdigit(grp[0])) + { + gid = atoi(grp); + group = userdb.group_name(gid); + } else { + gid = userdb.group_id(grp); + group = grp; + } + } catch (UIDNotFoundException& e) { + throw RunnerException(fmt("user `%s' not found on system", usr)); + } catch (UserNotFoundException& e) { + throw RunnerException(fmt("user `%s' not found on system", usr)); + } catch (GIDNotFoundException& e) { + throw RunnerException(fmt("group `%s' not found on system", grp)); + } catch (GroupNotFoundException& e) { + throw RunnerException(fmt("group `%s' not found on system", grp)); + } + + ::set_perms(user.c_str(), uid, group.c_str(), gid); +} + + +pid_t ChildProcess::run() +{ + int pstdin[2]; + int poutput[2]; + + if (_opt_want_child_stdin) + if (pipe(pstdin) == -1) + throw SystemException(errno, "trying to create the pipe to connect to child standard input"); + + if (_opt_want_child_output) + if (pipe(poutput) == -1) + throw SystemException(errno, "trying to create the pipe to read the script output"); + flockfile(stdout); + flockfile(stderr); + + fflush(stdout); + fflush(stderr); + + UserDBLock udblock(userdb); + + if ((pid = fork()) == 0) + { + // Child process + try { + udblock.reset(); + Logger::configure(*this); + + set_permissions(); + + if (_opt_want_child_stdin) + { + // Redirect input from the parent to stdin + if (close(pstdin[1]) == -1) + throw SystemException(errno, "closing write end of parent pipe"); + if (dup2(pstdin[0], 0) == -1) + throw SystemException(errno, "dup2-ing parent pipe to stdin"); + } + + if (_opt_want_child_output) + { + // Redirect all output to the parent + if (close(poutput[0]) == -1) + throw SystemException(errno, "closing read end of parent pipe"); + if (dup2(poutput[1], 1) == -1) + throw SystemException(errno, "dup2-ing stdout to parent pipe"); + if (dup2(poutput[1], 2) == -1) + throw SystemException(errno, "dup2-ing stderr to parent pipe"); + } + + _exit(main()); + } catch (Exception& e) { + log_err(string(e.type()) + ": " + e.desc()); + } + _exit(EXIT_FAILURE); + } else if (pid < 0) { + funlockfile(stderr); + funlockfile(stdout); + throw SystemException(errno, "trying to fork the child process to run action script"); + } else { + funlockfile(stderr); + funlockfile(stdout); + + // Parent process + try { + if (_opt_want_child_stdin) + { + if (close(pstdin[0]) == -1) + throw SystemException(errno, "closing read end of stdin child pipe"); + child_stdin = pstdin[1]; + } + + if (_opt_want_child_output) + { + if (close(poutput[1]) == -1) + throw SystemException(errno, "closing write end of output child pipe"); + child_output = poutput[0]; + } + + return pid; + } catch (Exception& e) { + // Try to kill the child process if any errors occurr here + ::kill(pid, 15); + throw e; + } + } +} + +// Read the child output +vector ChildProcess::read_output(int max_output_size = 0) +{ + if (pid == 0) + throw RunnerException("read_output was called but the child was not started"); + + if (!_opt_want_child_output) + return vector(); + + // Get the child output + // Don't read more than about 64Kb of output, to avoid + // filling up memory with scripts that write too much + vector output; + FILE* in = fdopen(child_output, "r"); + if (!in) + throw SystemException(errno, "calling fdopen on child output file descriptor"); + + string line; + int byte_count = 0; + int c; + while ((c = getc(in)) != EOF) + if (max_output_size == 0 || byte_count < max_output_size) + { + byte_count++; + if (c != '\n') + line += c; + else + { + output.push_back(line); + line = ""; + } + } else if (max_output_size != 0 && byte_count == max_output_size) { + output.push_back("Output too long: truncated here."); + byte_count++; + } + if (fclose(in) == EOF) + throw SystemException(errno, "closing read end of output child pipe"); + + return output; +} + +// Wait for child termination +int ChildProcess::wait() +{ + if (pid == 0) + throw RunnerException("wait was called but the child was not started"); + + int status; + if (waitpid(pid, &status, 0) == -1) + throw SystemException(errno, "waiting for child termination"); + pid = 0; + return status; +} + +void ChildProcess::kill(int sig) +{ + if (pid == 0) + throw RunnerException("kill was called but the child was not started"); + + if (::kill(pid, sig) == -1) + throw SystemException(errno, fmt("killing process %d with signal %d", pid, sig)); +} + + +///// ExecChildProcess::strlist + +void ExecChildProcess::strlist::expand() +{ + size *= 2; + ptr = (char**) realloc(ptr, size * sizeof(char*)); +} + +ExecChildProcess::strlist::strlist(int start_size) : cur(0), size(start_size) +{ + ptr = (char**) malloc(size * sizeof(char*)); +} + +ExecChildProcess::strlist::~strlist() +{ + for (int i = 0; i < cur; i++) + if (ptr[i]) + free(ptr[i]); + free(ptr); +} + +void ExecChildProcess::strlist::addf(const char* fmt, ...) ATTR_PRINTF(1, 2) +{ + if (cur >= size) + expand(); + va_list ap; + va_start(ap, fmt); + vasprintf(&ptr[cur++], fmt, ap); + va_end(ap); +} +void ExecChildProcess::strlist::add(const char* str) +{ + if (cur >= size) + expand(); + ptr[cur++] = strdup(str); +} +void ExecChildProcess::strlist::add(const string& str) +{ + if (cur >= size) + expand(); + ptr[cur++] = strndup(str.data(), str.size()); +} + + +///// ExecChildProcess + +void ExecChildProcess::copy_environ(strlist& env) +{ + for (int i = 0; environ[i]; i++) + env.add(environ[i]); +} + +int ExecChildProcess::main() +{ + strlist argv; + build_argv(argv); + + strlist env; + build_env(env); + + // Execute the command + if (execve(argv0(), argv.get(), env.get()) == -1) + throw SystemException(errno, string("Executing ") + argv0()); + + return EXIT_FAILURE; +} +#endif +// vim:set ts=4 sw=4: --- launchtool-0.8.orig/src/common/Config.cc.org +++ launchtool-0.8/src/common/Config.cc.org @@ -0,0 +1,518 @@ +#include "Config.h" + +#include +//#include +#include +#include +#include +#include +#include +#include +#include // access +#include // errno + +using namespace std; +using namespace stringf; + +//#undef LOGTAG +//#define LOGTAG "conffile" + +#define log_pedant(a...) do { } while(0) +#define log_info(a...) do { } while(0) +#define log_error(a...) do { } while(0) + +/* +#define log_pedant(a...) do { printf(##a); putchar('\n'); } while (0) +#define log_info(a...) do { printf(##a); putchar('\n'); } while (0) +#define log_error(a...) do { printf(##a); putchar('\n'); } while (0) +*/ + +static string substr(const string& s, const regmatch_t& r) throw() +{ + return s.substr(r.rm_so, r.rm_eo - r.rm_so); +} + +template class map; +template class map; + +class ConfigNode +{ +protected: + /// Reference count to this node + int _ref; + + /// Type for the child map + typedef map cmap_t; + + /// Type for the values map + typedef map vmap_t; + + /// Child map + cmap_t cmap; + + /// Values map + vmap_t vmap; + + /// Increase the reference count + void ref() throw () { ++_ref; } + + /// Decrease the reference count, returning true if it gets to 0 + bool unref() throw () { return (--_ref == 0); } + + /// Set the value of node `key' to `val' + void set(const string& key, ConfigNode* val) throw () + { + if (val) + val->ref(); // Do it early to correctly handle the case of x = x; + ConfigNode*& dest = cmap[key]; + if (dest && dest->unref()) + delete dest; + dest = val; + } + + /// Set the value of key `key' to `val' + void set(const string& key, const string& val) throw () + { + vmap[key] = val; + } + + /// Get the value of key, throwing an exception if not found + ConfigNode* node(const string& key) const + throw (ConfigKeyNotFoundException) + { + cmap_t::const_iterator i = cmap.find(key); + if (i == cmap.end()) + return 0; + return i->second; + } + + /// Get the value of key, creating it if it does not exist + bool value(const string& key, string& dest) const + throw (ConfigKeyNotFoundException) + { + vmap_t::const_iterator i = vmap.find(key); + if (i == vmap.end()) + return false; + dest = i->second; + return true; + } + + ConfigNode* findNode(const string& path) const throw () + { + size_t sep = path.find('/'); + if (sep == string::npos) + { + log_pedant("Local %.*s", PFSTR(path)); + return node(path); + } else { + log_pedant("Sub %.*s / %.*s", PFSTR(path.substr(0, sep)), PFSTR(path.substr(sep + 1))); + //for (cmap_t::const_iterator i = cmap.begin(); i != cmap.end(); ++i) + //log_pedant(" Avl: %.*s.", PFSTR(i->first)); + ConfigNode* sub = node(path.substr(0, sep)); + if (sub) + return sub->findNode(path.substr(sep + 1)); + else + return 0; + } + } + + /// Get the Config present at `path'/`path1` in dest. Return true if the + /// value was found, else false. If false is returned, dest is left + /// untouched. + bool findValue(const string& path, string& dest) const + throw () + { + size_t sep = path.find('/'); + if (sep == string::npos) + return value(path, dest); + else + { + ConfigNode* sub = findNode(path.substr(0, sep)); + if (sub) + return sub->value(path.substr(sep + 1), dest); + else + return false; + } + } + + ConfigNode() throw () : _ref(0) {} + virtual ~ConfigNode() + { + for (cmap_t::iterator i = cmap.begin(); + i != cmap.end(); i++) + if (i->second->unref()) + delete i->second; + } + + friend class Config; + friend class ConfFileParser; +}; + +// +// Config +// + +Config::Config() throw () : node(new ConfigNode) { node->ref(); } +Config::Config(ConfigNode* n) throw () : node(n) { node->ref(); } +Config::Config(const Config& c) throw () +{ + if (c.node) + c.node->ref(); + node = c.node; +} +Config::~Config() throw () +{ + if (node->unref()) + delete node; +} +Config& Config::operator=(const Config& c) +{ + if (c.node) + c.node->ref(); // Do it early to correctly handle the case of x = x; + if (node && node->unref()) + delete node; + node = c.node; + return *this; +} + +Config Config::getSection(const string& path) const + throw (ConfigKeyNotFoundException) +{ + ConfigNode* sub = node->findNode(path); + if (sub) + return sub; + else + throw ConfigKeyNotFoundException(path); +} + +string Config::getString(const string& path) const + throw (ConfigKeyNotFoundException) +{ + string res; + if (node->findValue(path, res)) + return res; + else + throw ConfigKeyNotFoundException(path); +} + +string Config::getString(const string& path, const string& dflt) const throw () +{ + string res; + if (node->findValue(path, res)) + return res; + else + return dflt; +} + +string Config::getString(const string& path, const string& path1, + const string& dflt) const throw () +{ + ConfigNode* sub = node->findNode(path); + if (sub) + { + string res; + if (sub->value(path1, res)) + return res; + else + return dflt; + } else + return dflt; +} + +int Config::getInt(const string& path) const + throw (ConfigKeyNotFoundException) +{ + return atoi(getString(path).c_str()); +} + +int Config::getInt(const string& path, int dflt) const throw () +{ + string res; + if (node->findValue(path, res)) + return atoi(res.c_str()); + else + return dflt; +} + +int Config::getInt(const string& path, const string& path1, int dflt) const throw () +{ + ConfigNode* sub = node->findNode(path); + if (sub) + { + string res; + if (sub->value(path1, res)) + return atoi(res.c_str()); + else + return dflt; + } else + return dflt; +} + +void Config::visitNodes(NodeVisitor& v) const throw (Exception) +{ + for (ConfigNode::cmap_t::iterator i = node->cmap.begin(); + i != node->cmap.end(); i++) + if (!v.visitNode(i->first, i->second)) + break; +} + +void Config::visitValues(ValueVisitor& v) const throw (Exception) +{ + for (ConfigNode::vmap_t::iterator i = node->vmap.begin(); + i != node->vmap.end(); i++) + if (!v.visitValue(i->first, i->second)) + break; +} + +class Printer : public Config::NodeVisitor, public Config::ValueVisitor +{ +protected: + string root; + +public: + Printer(const string& root) throw () : root(root) {} + virtual ~Printer() throw () {} + + virtual bool visitNode(const string& str, const Config& node) throw (Exception) + { + Printer p(root + '/' + str); + node.visitNodes(p); + node.visitValues(p); + return true; + } + + virtual bool visitValue(const string& str, const string& val) throw (Exception) + { + printf("%.*s/%.*s = %.*s\n", PFSTR(root), PFSTR(str), PFSTR(val)); + return true; + } +}; + +void Config::dump(const string& base = "") const throw () +{ + Printer p(base); + visitNodes(p); + visitValues(p); +} + + +// +// ConfFileParser +// + +class ConfFileParser +{ +protected: + struct input + { + string name; + string base_path; + FILE* stream; + }; + string line; + stack in; + + Regex empty_re; + Regex include_re; + Regex alias_re; + Regex bopen_re; + Regex bclose_re; + Regex assign_re; + Regex assign1_re; + + bool eof; + + string nextLine() throw (); + bool pushFile(const string& file) throw (FileException); + bool popFile() throw (); + ConfigNode* parse_node() throw (RegexException); + +public: + ConfFileParser() throw(RegexException) : line(), in(), + empty_re("^[[:blank:]]*(#.+)?$", REG_EXTENDED | REG_NOSUB), + include_re("^[[:blank:]]*include[[:blank:]]+([^[:blank:]]+)[[:blank:]]*(#.+)?$", REG_EXTENDED), + alias_re( "^[[:blank:]]*alias[[:blank:]]+([^[:blank:]]+)[[:blank:]]+([^[:blank:]]+)[[:blank:]]*(#.+)?$", REG_EXTENDED), + bopen_re( "^[[:blank:]]*([^{=]+[^[:blank:]{=])[[:blank:]]*\\{[[:blank:]]*(#.+)?$", REG_EXTENDED), + bclose_re( "^[[:blank:]]*\\}[[:blank:]]*(#.+)?$", REG_EXTENDED | REG_NOSUB), + assign_re( "^[[:blank:]]*([^=]+[^[:blank:]=])[[:blank:]]*=[[:blank:]]*\"((\\\\.|[^\"])*)\"[[:blank:]]*(#.+)?$", REG_EXTENDED), + assign1_re( "^[[:blank:]]*([^=]+[^[:blank:]=])[[:blank:]]*=[[:blank:]]*([^#]*[^[:blank:]#])[[:blank:]]*(#.+)?$", REG_EXTENDED), eof(false) {} + + Config parse(const string& file) + throw (FileException, RegexException); +}; + +string ConfFileParser::nextLine() throw () +{ + if (!in.size()) + { + eof = true; + return string(); + } + + FILE* stm = in.top().stream; + if (feof(stm)) + { + if (popFile()) + return nextLine(); + eof = true; + return string(); + } + + line = ""; + int c; + while ((c = getc(stm)) != '\n' && c != EOF) + line += c; + + regmatch_t matches[2]; + if (include_re.match(line.c_str(), 2, matches) + && matches[1].rm_so != -1) + { + string fname = in.top().base_path; + fname += "/"; + fname += line.substr(matches[1].rm_so, matches[1].rm_eo); + //fprintf(stderr, "File: %s, base: %s, result: %s\n", + // line.substr(matches[1].rm_so, matches[1].rm_eo).c_str(), + // in.top().base_path.c_str(), + // fname.c_str()); + pushFile(fname.c_str()); + return nextLine(); + } + + return line; +} + +bool ConfFileParser::pushFile(const string& file) throw (FileException) +{ + // Must not alter t.stream if the file can't be open due to lack of + // permissions + FILE* nstream = fopen(file.c_str(), "rt"); + + //if (access(file, R_OK) == -1) + if (nstream == 0) + if (errno == EACCES) + { + log_info("Skipped inclusion of unreadable file \"%.*s\"", PFSTR(file)); + return false; + } else + throw FileException(errno, "opening " + file); + + input t; + //t.stream = fopen(file, "rt"); + t.stream = nstream; + log_info("Including file %.*s", PFSTR(file)); + + t.name = file; + unsigned int bend = t.name.rfind('/'); + if (bend == string::npos) + t.base_path = "."; + else + t.base_path = t.name.substr(0, bend); + in.push(t); + return true; +} + +bool ConfFileParser::popFile() throw () +{ + if (!in.size()) + return false; + + fclose(in.top().stream); + in.pop(); + return true; +} + +struct alias +{ + string a; + string b; + alias (const string& sa, const string& sb) : a(sa), b(sb) {} +}; + +template class deque; + +ConfigNode* ConfFileParser::parse_node() throw (RegexException) +{ + deque aqueue; + + ConfigNode* node = new ConfigNode(); + + while (!eof) + { + string s = nextLine(); + regmatch_t matches[4]; + if (empty_re.match(s.c_str())) + continue; + if (alias_re.match(s.c_str(), 4, matches)) + { + aqueue.push_back(new alias(substr(s, matches[1]), + substr(s, matches[2]))); + log_pedant("Alias: %.*s (%.*s -> %.*s)", PFSTR(s), + PFSTR(aqueue.back()->a), + PFSTR(aqueue.back()->b)); + } else if (assign_re.match(s.c_str(), 4, matches)) { + log_pedant("Assign: %.*s", PFSTR(s)); + node->set(substr(s, matches[1]), + substr(s, matches[2])); + } else if (assign1_re.match(s.c_str(), 4, matches)) { + log_pedant("Assign unquoted: %.*s", PFSTR(s)); + node->set(substr(s, matches[1]), + substr(s, matches[2])); + } else if (bopen_re.match(s.c_str(), 4, matches)) { + log_pedant("Bopen: %.*s", PFSTR(s)); + string path = substr(s, matches[1]); + node->set(path, parse_node()); + } else if (bclose_re.match(s.c_str())) { + log_pedant("Bclose: %.*s", PFSTR(s)); + break; + } else { + fprintf(stderr, "Parse error on line \"%.*s\": ignored\n", PFSTR(s)); + } + } + + // Apply path aliases + while (aqueue.size()) + { + alias* anode = aqueue.front(); + log_pedant("Applying alias `%.*s' -> `%.*s'", + PFSTR(anode->a), + PFSTR(anode->b)); + ConfigNode* dest = node->findNode(anode->b); + if (dest) + node->set(anode->a, dest); + else { + string val; + if (node->findValue(anode->b, val)) + node->set(anode->a, val); + else + fprintf(stderr, "Missing target for mapping %.*s -> %.*s\n", + PFSTR(anode->a), PFSTR(anode->b)); + } + aqueue.pop_front(); + } + + return node; +} + +Config ConfFileParser::parse(const string& file) + throw (FileException, RegexException) +{ + pushFile(file); + return parse_node(); +} + +// +// Config +// + +Config Config::parse(const string& file) throw (FileException) +{ + try { + ConfFileParser parser; + return parser.parse(file); + } catch (RegexException& e) { + fprintf(stderr, "%s: %.*s", e.type(), PFSTR(e.desc())); + return Config(); + } +} + +// vim:set ts=4 sw=4: --- launchtool-0.8.orig/src/common/Exception.cc.org +++ launchtool-0.8/src/common/Exception.cc.org @@ -0,0 +1,59 @@ +#include "Exception.h" + +#include // strerror_r + +#include + +#include + +#include + +using namespace std; + +void DefaultUnexpected() +{ + try { + const int trace_size = 50; + void *addrs[trace_size]; + size_t size = backtrace (addrs, trace_size); + char **strings = backtrace_symbols (addrs, size); + + fprintf(stderr, "Caught unexpected exception, %zd stack frames unwound:\n", size); + for (size_t i = 0; i < size; i++) + fprintf (stderr, " %s\n", strings[i]); + free (strings); + throw; + } catch (Exception& e) { + fprintf(stderr, "Exception was: %s: %.*s.\n", e.type(), PFSTR(e.desc())); + throw; + } catch (exception& e) { + fprintf(stderr, "Exception was: %s: %s\n", typeid(e).name(), e.what()); + throw; + } catch (...) { + fprintf(stderr, "Exception was: unknown object\n"); + throw; + } +} + +InstallUnexpected::InstallUnexpected(void (*func)()) +{ + old = set_unexpected(func); +} + +InstallUnexpected::~InstallUnexpected() +{ + set_unexpected(old); +} + +///// SystemException + +string SystemException::system_desc() const throw () +{ + const int buf_size = 100; + char* buf = new char[buf_size]; + string res(strerror_r(_code, buf, buf_size)); + delete buf; + return res; +} + +// vim:set ts=4 sw=4: --- launchtool-0.8.orig/src/common/Exec.cc.org +++ launchtool-0.8/src/common/Exec.cc.org @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2002 Enrico Zini + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE + +#include "Exec.h" + +#include // execve +#include // malloc, free, realloc +#include // strndup +#include + +using namespace stringf; +using namespace std; + +///// Exec::strlist + +void Exec::strlist::expand() throw () +{ + size *= 2; + ptr = (char**) realloc(ptr, size * sizeof(char*)); +} + +Exec::strlist::strlist(int start_size) throw () : cur(0), size(start_size) +{ + ptr = (char**) malloc(size * sizeof(char*)); +} + +Exec::strlist::~strlist() throw () +{ + for (int i = 0; i < cur; i++) + if (ptr[i]) + free(ptr[i]); + free(ptr); +} + +void Exec::strlist::add(const char* str) throw () +{ + if (cur >= size) + expand(); + ptr[cur++] = strdup(str); +} + +void Exec::strlist::add(const string& str) throw () +{ + if (cur >= size) + expand(); + ptr[cur++] = strndup(str.data(), str.size()); +} + +void Exec::exec() throw (SystemException) +{ + execve(program.c_str(), argv.get(), env.get()); + throw SystemException(errno, "Executing " + program); +} + +// vim:set ts=4 sw=4: --- launchtool-0.8.orig/src/common/LoggerMethods.cc.org +++ launchtool-0.8/src/common/LoggerMethods.cc.org @@ -0,0 +1,231 @@ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "LoggerMethods.h" + +#include + +#include +#include +#include + +//#include // isatty + +using namespace Log; +using namespace std; + +// +// FileMethod +// + +FileMethod::FileMethod(const string& file) throw (FileException) : out(0), fname(file) +{ + out = fopen(fname.c_str(), "at"); + if (!out) + throw FileException(errno, "opening logfile " + fname); +} + +FileMethod::~FileMethod() throw () +{ + if (out) + fclose((FILE*)out); +} + +void FileMethod::output(LogLevel level, const char* tag, const char* func, + const string& msg) throw () +{ + time_t now = time(NULL); + struct tm pnow; + localtime_r(&now, &pnow); + char timebuf[20]; + /* + * Strftime specifiers used here: + * %b The abbreviated month name according to the current locale. + * %d The day of the month as a decimal number (range 01 to 31). + * %e Like %d, the day of the month as a decimal number, but a + * leading zero is replaced by a space. (SU) + * %T The time in 24-hour notation (%H:%M:%S). (SU) + */ + strftime(timebuf, 20, "%b %e %T", &pnow); + fprintf((FILE*)out, "%s %s %s: %.*s\n", timebuf, tag, func, PFSTR(msg)); + if (level >= WARN) + fflush((FILE*)out); +} + +// +// StderrMethod +// + +void StderrMethod::output(LogLevel level, const char* tag, const char* func, + const string& msg) throw () +{ + fprintf(stderr, "%s %s: %.*s\n", tag, func, PFSTR(msg)); +} + +// +// SyslogMethod +// + +SyslogMethod::SyslogMethod(const string& ident, int facility = LOG_DAEMON) throw () + : ident(ident), facility(facility) +{ + openlog(ident.c_str(), LOG_PID, facility); +} + +SyslogMethod::~SyslogMethod() throw () +{ + closelog(); +} + +void SyslogMethod::output(LogLevel level, const char* tag, const char* func, + const string& msg) throw () +{ + int prio; + switch (level) + { + case DEBUG: prio = LOG_DEBUG; break; + case INFO: prio = LOG_INFO; break; + case UNUSUAL: prio = LOG_NOTICE; break; + case WARN: prio = LOG_WARNING; break; + case ERR: prio = LOG_ERR; break; + case CRIT: prio = LOG_CRIT; break; + //LOG_ALERT, LOG_EMERG + default: prio = LOG_WARNING; break; + } + syslog(prio, "%s %s: %.*s", tag, func, PFSTR(msg)); +} + +void SyslogMethod::setupForkedChild() throw () +{ + closelog(); + openlog(ident.c_str(), LOG_PID, facility); +} + + +/* + +static enum stderr_state_type stderr_state = Yes; +static bool debug_output = false; + +void set_debug(bool val) +{ + debug_output = val; +} + +void set_stderr(enum stderr_state_type val) +{ + switch (val) + { + case Yes: + call_closelog(); break; + case No: + call_openlog(); break; + case Restricted: + call_openlog(); break; + } + stderr_state = val; +} + +void log_debug(const char* fmt, ...) ATTR_PRINTF(1, 2) +{ + if (debug_output) + { + va_list ap; + va_start(ap, fmt); + switch (stderr_state) + { + case Yes: + //if (isatty(fileno(stderr))) + //{ + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + //} + break; + case No: + case Restricted: + vsyslog(LOG_DEBUG, fmt, ap); + break; + } + va_end(ap); + } +} + +void log_info(const char* fmt, ...) ATTR_PRINTF(1, 2) +{ + va_list ap; + va_start(ap, fmt); + switch (stderr_state) + { + case Yes: + //if (isatty(fileno(stderr))) + //{ + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + //} + break; + case No: + case Restricted: + vsyslog(LOG_INFO, fmt, ap); + break; + } + va_end(ap); +} + +void log_warn(const char* fmt, ...) ATTR_PRINTF(1, 2) +{ + va_list ap; + va_start(ap, fmt); + switch (stderr_state) + { + case Yes: + case Restricted: + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + break; + case No: + vsyslog(LOG_WARNING, fmt, ap); + break; + } + va_end(ap); +} + +void log_error(const char* fmt, ...) ATTR_PRINTF(1, 2) +{ + va_list ap; + va_start(ap, fmt); + switch (stderr_state) + { + case Yes: + case Restricted: + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + break; + case No: + vsyslog(LOG_ERR, fmt, ap); + break; + } + va_end(ap); +} + +void log_crit(const char* fmt, ...) ATTR_PRINTF(1, 2) +{ + va_list ap; + va_start(ap, fmt); + switch (stderr_state) + { + case Yes: + case Restricted: + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + break; + case No: + vsyslog(LOG_CRIT, fmt, ap); + break; + } + va_end(ap); +} + +*/ + +// vim:set ts=4 sw=4: --- launchtool-0.8.orig/src/common/Regex.cc.org +++ launchtool-0.8/src/common/Regex.cc.org @@ -0,0 +1,60 @@ +#include "Regex.h" + +using namespace stringf; +using namespace std; + +////// RegexException + +RegexException::RegexException(const regex_t& re, int code, + const string& context) throw () + : SystemException(code, context) +{ + int size = 64; + char* msg = new char[size]; + int nsize = regerror(code, &re, msg, size); + if (nsize < size) + { + delete msg; + msg = new char[nsize]; + regerror(code, &re, msg, nsize); + } + _message = msg; + delete msg; +} + + +////// Regex + +Regex::Regex(const char* expr, int flags = 0) + throw(RegexException) +{ + int res = regcomp(&re, expr, flags); + if (res) + throw RegexException(re, res, fmt("Compiling regexp \"%s\"", expr)); +} + +bool Regex::match(const char* str, int flags = 0) + throw(RegexException) +{ + int res = regexec(&re, str, 0, 0, flags); + switch (res) + { + case 0: return true; + case REG_NOMATCH: return false; + default: throw RegexException(re, res, fmt("Matching string \"%s\"", str)); + } +} + +bool Regex::match(const char* str, int nmatch, regmatch_t pmatch[], int flags = 0) + throw(RegexException) +{ + int res = regexec(&re, str, nmatch, pmatch, flags); + switch (res) + { + case 0: return true; + case REG_NOMATCH: return false; + default: throw RegexException(re, res, fmt("Matching string \"%s\"", str)); + } +} + +// vim:set ts=4 sw=4: --- launchtool-0.8.orig/src/test.log +++ launchtool-0.8/src/test.log @@ -0,0 +1,335 @@ +out: Starting +err: Starting +out: Mon Sep 30 16:17:57 2002 +err: Mon Sep 30 16:17:57 2002 +out: Mon Sep 30 16:17:58 2002 +err: Mon Sep 30 16:17:58 2002 +out: Mon Sep 30 16:17:59 2002 +err: Mon Sep 30 16:17:59 2002 +out: Mon Sep 30 16:18:00 2002 +err: Mon Sep 30 16:18:00 2002 +out: Mon Sep 30 16:18:01 2002 +err: Mon Sep 30 16:18:01 2002 +out: Mon Sep 30 16:18:02 2002 +err: Mon Sep 30 16:18:02 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:02 2002 +err: Mon Sep 30 16:18:02 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:03 2002 +err: Mon Sep 30 16:18:03 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:04 2002 +err: Mon Sep 30 16:18:04 2002 +out: Mon Sep 30 16:18:05 2002 +err: Mon Sep 30 16:18:05 2002 +out: Mon Sep 30 16:18:06 2002 +err: Mon Sep 30 16:18:06 2002 +out: Mon Sep 30 16:18:07 2002 +err: Mon Sep 30 16:18:07 2002 +out: Mon Sep 30 16:18:08 2002 +err: Mon Sep 30 16:18:08 2002 +out: Mon Sep 30 16:18:09 2002 +err: Mon Sep 30 16:18:09 2002 +out: Mon Sep 30 16:18:10 2002 +err: Mon Sep 30 16:18:10 2002 +out: Mon Sep 30 16:18:11 2002 +err: Mon Sep 30 16:18:11 2002 +out: Mon Sep 30 16:18:12 2002 +err: Mon Sep 30 16:18:12 2002 +out: Mon Sep 30 16:18:13 2002 +err: Mon Sep 30 16:18:13 2002 +out: Mon Sep 30 16:18:14 2002 +err: Mon Sep 30 16:18:14 2002 +out: Mon Sep 30 16:18:15 2002 +err: Mon Sep 30 16:18:15 2002 +out: Mon Sep 30 16:18:16 2002 +err: Mon Sep 30 16:18:16 2002 +out: Mon Sep 30 16:18:17 2002 +err: Mon Sep 30 16:18:17 2002 +out: Mon Sep 30 16:18:18 2002 +err: Mon Sep 30 16:18:18 2002 +out: Mon Sep 30 16:18:19 2002 +err: Mon Sep 30 16:18:19 2002 +out: Mon Sep 30 16:18:20 2002 +err: Mon Sep 30 16:18:20 2002 +out: Mon Sep 30 16:18:21 2002 +err: Mon Sep 30 16:18:21 2002 +out: Mon Sep 30 16:18:22 2002 +err: Mon Sep 30 16:18:22 2002 +out: Mon Sep 30 16:18:23 2002 +err: Mon Sep 30 16:18:23 2002 +out: Mon Sep 30 16:18:24 2002 +err: Mon Sep 30 16:18:24 2002 +out: Mon Sep 30 16:18:25 2002 +err: Mon Sep 30 16:18:25 2002 +out: Mon Sep 30 16:18:26 2002 +err: Mon Sep 30 16:18:26 2002 +out: Mon Sep 30 16:18:27 2002 +err: Mon Sep 30 16:18:27 2002 +out: Mon Sep 30 16:18:28 2002 +err: Mon Sep 30 16:18:28 2002 +out: Mon Sep 30 16:18:29 2002 +err: Mon Sep 30 16:18:29 2002 +out: Mon Sep 30 16:18:30 2002 +err: Mon Sep 30 16:18:30 2002 +out: Mon Sep 30 16:18:31 2002 +err: Mon Sep 30 16:18:31 2002 +out: Mon Sep 30 16:18:32 2002 +err: Mon Sep 30 16:18:32 2002 +out: Mon Sep 30 16:18:33 2002 +err: Mon Sep 30 16:18:33 2002 +out: Mon Sep 30 16:18:34 2002 +err: Mon Sep 30 16:18:34 2002 +out: Mon Sep 30 16:18:35 2002 +err: Mon Sep 30 16:18:35 2002 +out: Mon Sep 30 16:18:36 2002 +err: Mon Sep 30 16:18:36 2002 +out: Mon Sep 30 16:18:37 2002 +err: Mon Sep 30 16:18:37 2002 +out: Mon Sep 30 16:18:38 2002 +err: Mon Sep 30 16:18:38 2002 +out: Mon Sep 30 16:18:39 2002 +err: Mon Sep 30 16:18:39 2002 +out: Mon Sep 30 16:18:40 2002 +err: Mon Sep 30 16:18:40 2002 +out: Mon Sep 30 16:18:41 2002 +err: Mon Sep 30 16:18:41 2002 +out: Mon Sep 30 16:18:42 2002 +err: Mon Sep 30 16:18:42 2002 +out: Mon Sep 30 16:18:44 2002 +err: Mon Sep 30 16:18:44 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:44 2002 +err: Mon Sep 30 16:18:44 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:44 2002 +err: Mon Sep 30 16:18:44 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:45 2002 +err: Mon Sep 30 16:18:45 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:46 2002 +err: Mon Sep 30 16:18:46 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:47 2002 +err: Mon Sep 30 16:18:47 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:47 2002 +err: Mon Sep 30 16:18:47 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:48 2002 +err: Mon Sep 30 16:18:48 2002 +err: Received signal 10 (User defined signal 1) +out: Mon Sep 30 16:18:48 2002 +err: Mon Sep 30 16:18:48 2002 +out: Mon Sep 30 16:18:49 2002 +err: Mon Sep 30 16:18:49 2002 +out: Mon Sep 30 16:18:50 2002 +err: Mon Sep 30 16:18:50 2002 +out: Mon Sep 30 16:18:52 2002 +err: Mon Sep 30 16:18:52 2002 +out: Mon Sep 30 16:18:53 2002 +err: Mon Sep 30 16:18:53 2002 +out: Mon Sep 30 16:18:54 2002 +err: Mon Sep 30 16:18:54 2002 +out: Mon Sep 30 16:18:55 2002 +err: Mon Sep 30 16:18:55 2002 +out: Mon Sep 30 16:18:56 2002 +err: Mon Sep 30 16:18:56 2002 +out: Mon Sep 30 16:18:57 2002 +err: Mon Sep 30 16:18:57 2002 +out: Mon Sep 30 16:18:58 2002 +err: Mon Sep 30 16:18:58 2002 +out: Mon Sep 30 16:18:59 2002 +err: Mon Sep 30 16:18:59 2002 +out: Mon Sep 30 16:19:00 2002 +err: Mon Sep 30 16:19:00 2002 +out: Mon Sep 30 16:19:01 2002 +err: Mon Sep 30 16:19:01 2002 +out: Mon Sep 30 16:19:02 2002 +err: Mon Sep 30 16:19:02 2002 +out: Mon Sep 30 16:19:03 2002 +err: Mon Sep 30 16:19:03 2002 +out: Mon Sep 30 16:19:04 2002 +err: Mon Sep 30 16:19:04 2002 +out: Mon Sep 30 16:19:05 2002 +err: Mon Sep 30 16:19:05 2002 +out: Mon Sep 30 16:19:06 2002 +err: Mon Sep 30 16:19:06 2002 +out: Mon Sep 30 16:19:07 2002 +err: Mon Sep 30 16:19:07 2002 +out: Mon Sep 30 16:19:08 2002 +err: Mon Sep 30 16:19:08 2002 +out: Mon Sep 30 16:19:09 2002 +err: Mon Sep 30 16:19:09 2002 +out: Mon Sep 30 16:19:10 2002 +err: Mon Sep 30 16:19:10 2002 +out: Mon Sep 30 16:19:11 2002 +err: Mon Sep 30 16:19:11 2002 +out: Mon Sep 30 16:19:12 2002 +err: Mon Sep 30 16:19:12 2002 +out: Mon Sep 30 16:19:13 2002 +err: Mon Sep 30 16:19:13 2002 +out: Mon Sep 30 16:19:14 2002 +err: Mon Sep 30 16:19:14 2002 +out: Mon Sep 30 16:19:15 2002 +err: Mon Sep 30 16:19:15 2002 +out: Mon Sep 30 16:19:16 2002 +err: Mon Sep 30 16:19:16 2002 +out: Mon Sep 30 16:19:17 2002 +err: Mon Sep 30 16:19:17 2002 +out: Mon Sep 30 16:19:18 2002 +err: Mon Sep 30 16:19:18 2002 +out: Mon Sep 30 16:19:19 2002 +err: Mon Sep 30 16:19:19 2002 +out: Mon Sep 30 16:19:20 2002 +err: Mon Sep 30 16:19:20 2002 +out: Mon Sep 30 16:19:21 2002 +err: Mon Sep 30 16:19:21 2002 +out: Mon Sep 30 16:19:22 2002 +err: Mon Sep 30 16:19:22 2002 +out: Mon Sep 30 16:19:23 2002 +err: Mon Sep 30 16:19:23 2002 +out: Mon Sep 30 16:19:24 2002 +err: Mon Sep 30 16:19:24 2002 +out: Mon Sep 30 16:19:25 2002 +err: Mon Sep 30 16:19:25 2002 +out: Mon Sep 30 16:19:26 2002 +err: Mon Sep 30 16:19:26 2002 +out: Mon Sep 30 16:19:27 2002 +err: Mon Sep 30 16:19:27 2002 +out: Mon Sep 30 16:19:28 2002 +err: Mon Sep 30 16:19:28 2002 +out: Mon Sep 30 16:19:29 2002 +err: Mon Sep 30 16:19:29 2002 +out: Mon Sep 30 16:19:30 2002 +err: Mon Sep 30 16:19:30 2002 +out: Mon Sep 30 16:19:31 2002 +err: Mon Sep 30 16:19:31 2002 +out: Mon Sep 30 16:19:32 2002 +err: Mon Sep 30 16:19:32 2002 +out: Mon Sep 30 16:19:33 2002 +err: Mon Sep 30 16:19:33 2002 +out: Mon Sep 30 16:19:34 2002 +err: Mon Sep 30 16:19:34 2002 +out: Mon Sep 30 16:19:35 2002 +err: Mon Sep 30 16:19:35 2002 +out: Mon Sep 30 16:19:36 2002 +err: Mon Sep 30 16:19:36 2002 +out: Mon Sep 30 16:19:37 2002 +err: Mon Sep 30 16:19:37 2002 +out: Mon Sep 30 16:19:38 2002 +err: Mon Sep 30 16:19:38 2002 +out: Mon Sep 30 16:19:39 2002 +err: Mon Sep 30 16:19:39 2002 +out: Mon Sep 30 16:19:40 2002 +err: Mon Sep 30 16:19:40 2002 +out: Mon Sep 30 16:19:41 2002 +err: Mon Sep 30 16:19:41 2002 +out: Mon Sep 30 16:19:42 2002 +err: Mon Sep 30 16:19:42 2002 +out: Mon Sep 30 16:19:43 2002 +err: Mon Sep 30 16:19:43 2002 +out: Mon Sep 30 16:19:44 2002 +err: Mon Sep 30 16:19:44 2002 +out: Mon Sep 30 16:19:45 2002 +err: Mon Sep 30 16:19:45 2002 +out: Mon Sep 30 16:19:46 2002 +err: Mon Sep 30 16:19:46 2002 +out: Mon Sep 30 16:19:47 2002 +err: Mon Sep 30 16:19:47 2002 +out: Mon Sep 30 16:19:48 2002 +err: Mon Sep 30 16:19:48 2002 +out: Mon Sep 30 16:19:49 2002 +err: Mon Sep 30 16:19:49 2002 +out: Mon Sep 30 16:19:50 2002 +err: Mon Sep 30 16:19:50 2002 +out: Mon Sep 30 16:19:51 2002 +err: Mon Sep 30 16:19:51 2002 +out: Mon Sep 30 16:19:52 2002 +err: Mon Sep 30 16:19:52 2002 +out: Mon Sep 30 16:19:53 2002 +err: Mon Sep 30 16:19:53 2002 +out: Mon Sep 30 16:19:54 2002 +err: Mon Sep 30 16:19:54 2002 +out: Mon Sep 30 16:19:55 2002 +err: Mon Sep 30 16:19:55 2002 +out: Mon Sep 30 16:19:56 2002 +err: Mon Sep 30 16:19:56 2002 +out: Mon Sep 30 16:19:57 2002 +err: Mon Sep 30 16:19:57 2002 +out: Mon Sep 30 16:19:58 2002 +out: Starting +err: Starting +out: Thu Oct 3 20:43:34 2002 +err: Thu Oct 3 20:43:34 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 20:45:12 2002 +err: Thu Oct 3 20:45:12 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 20:45:29 2002 +err: Thu Oct 3 20:45:29 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 21:29:35 2002 +err: Thu Oct 3 21:29:35 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 21:29:37 2002 +err: Thu Oct 3 21:29:37 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 21:29:39 2002 +err: Thu Oct 3 21:29:39 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 21:29:41 2002 +err: Thu Oct 3 21:29:41 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 21:29:48 2002 +err: Thu Oct 3 21:29:48 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 21:29:51 2002 +err: Thu Oct 3 21:29:51 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 21:29:53 2002 +err: Thu Oct 3 21:29:53 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 21:29:55 2002 +err: Thu Oct 3 21:29:55 2002 +out: Ending +err: Ending +out: Starting +err: Starting +out: Thu Oct 3 21:29:57 2002 +err: Thu Oct 3 21:29:57 2002 +err: Received signal 2 (Interrupt) +out: Ending +err: Ending --- launchtool-0.8.orig/stamp-h.in +++ launchtool-0.8/stamp-h.in @@ -0,0 +1 @@ +timestamp