diff --git a/Makefile.global b/Makefile.global index a6d55daf80..bb094d4bb6 100644 --- a/Makefile.global +++ b/Makefile.global @@ -62,12 +62,11 @@ endef # Library objects required in multiple places: LIBSBASE = $(addprefix $(TOPDIR)/lib/,lib.error lib.misc) LIBHEADERS = $(addsuffix .h,$(LIBSBASE)) -LIBSOURCES = $(addsuffix .c,$(LIBSBASE)) LIBOBJECTS = $(addsuffix $(OBJEXT),$(LIBSBASE)) CFLAGS += -I$(TOPDIR)/lib -I$(TOPDIR)/etc CXXFLAGS += -I$(TOPDIR)/lib -I$(TOPDIR)/etc -$(LIBOBJECTS): %$(OBJEXT): %.c %.h +$(LIBOBJECTS): $(MAKE) -C $(TOPDIR)/lib $(notdir $@) # Default recursive targets; these should always at least call the diff --git a/judge/Makefile b/judge/Makefile index 7e3e4787d8..74b756a6b8 100644 --- a/judge/Makefile +++ b/judge/Makefile @@ -13,14 +13,14 @@ judgehost: $(TARGETS) $(SUBST_FILES) $(SUBST_FILES): %: %.in $(TOPDIR)/paths.mk $(substconfigvars) -evict: evict.cc $(LIBHEADERS) $(LIBSOURCES) - $(CXX) $(CXXFLAGS) -o $@ $< $(LIBSOURCES) +evict: evict.cc $(LIBOBJECTS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $< $(LIBOBJECTS) -runguard: runguard.cc $(LIBHEADERS) $(LIBSOURCES) $(TOPDIR)/etc/runguard-config.h - $(CXX) $(CXXFLAGS) -o $@ $< $(LIBSOURCES) $(LIBCGROUP) +runguard: runguard.cc $(LIBOBJECTS) $(TOPDIR)/etc/runguard-config.h + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $< $(LIBOBJECTS) $(LIBCGROUP) -runpipe: runpipe.cc $(LIBHEADERS) $(LIBSOURCES) - $(CXX) $(CXXFLAGS) -static -o $@ $< $(LIBSOURCES) +runpipe: runpipe.cc $(LIBOBJECTS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -static -o $@ $< $(LIBOBJECTS) install-judgehost: $(INSTALL_PROG) -t $(DESTDIR)$(judgehost_libjudgedir) \ diff --git a/judge/evict.cc b/judge/evict.cc index dc21f0b0ac..cdc1f1b103 100644 --- a/judge/evict.cc +++ b/judge/evict.cc @@ -12,9 +12,10 @@ #include #include +#include "lib.misc.h" + extern "C" { #include "lib.error.h" -#include "lib.misc.h" } #define PROGRAM "evict" diff --git a/judge/runguard.cc b/judge/runguard.cc index e7e3e590fb..460d2e680b 100644 --- a/judge/runguard.cc +++ b/judge/runguard.cc @@ -29,6 +29,8 @@ #include "config.h" +#include "lib.misc.h" + /* Some system/site specific config: VALID_USERS, CHROOT_PREFIX */ #include "runguard-config.h" @@ -346,17 +348,6 @@ void write_meta(const char *key, const char *format, ...) va_end(ap); } -void version(const char *prog, const char *vers) -{ - printf("\ -%s -- part of DOMjudge version %s\n\ -Written by the DOMjudge developers\n\n\ -DOMjudge comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n\ -are welcome to redistribute it under certain conditions. See the GNU\n\ -General Public Licence for details.\n", prog, vers); - exit(0); -} - void usage() { printf("\ diff --git a/judge/runpipe.cc b/judge/runpipe.cc index e57fd5b948..be48667d6b 100644 --- a/judge/runpipe.cc +++ b/judge/runpipe.cc @@ -66,6 +66,7 @@ #include "lib.misc.h" #include +#include #include #include #include @@ -232,22 +233,17 @@ struct process_t { // Fork and exec the child process, redirecting its standard I/O. void spawn() { - fd_t stdio[3] = {stdin_fd, stdout_fd, FDREDIR_NONE}; - - char pid_buf[12]; - vector argv; - for (size_t i = 0; i < args.size(); i++) { - argv.push_back(args[i].c_str()); - if (i == 1 && cmd == "sudo" && - args[i].find("/runguard") != string::npos) { + std::array stdio = {stdin_fd, stdout_fd, FDREDIR_NONE}; + + auto exec_args = args; + if (cmd == "sudo" && exec_args.size() > 1 && exec_args[1].find("/runguard") != string::npos) { // This is a hack, and can be improved significantly after implementing // https://docs.google.com/document/d/1WZRwdvJUamsczYC7CpP3ZIBU8xG6wNqYqrNJf7osxYs/edit#heading=h.i7kgdnmw8qd7 - argv.push_back("-U"); - sprintf(pid_buf, "%d", getpid()); - argv.push_back(pid_buf); - } + exec_args.push_back("-U"); + exec_args.push_back(std::to_string(getpid())); } - pid = execute(cmd.c_str(), argv.data(), argv.size(), stdio, 0); + + pid = execute(cmd, exec_args, stdio, false); if (pid < 0) { error(errno, "failed to execute command #%ld", index); } diff --git a/lib/Makefile b/lib/Makefile index 238684c01f..e608dbfba5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -8,7 +8,8 @@ OBJECTS = $(addsuffix $(OBJEXT),lib.error lib.misc) build: $(OBJECTS) -$(OBJECTS): %$(OBJEXT): %.c %.h +lib.error$(OBJEXT): lib.error.c lib.error.h +lib.misc$(OBJEXT): lib.misc.cc lib.misc.h clean-l: rm -f $(OBJECTS) diff --git a/lib/lib.misc.c b/lib/lib.misc.c deleted file mode 100644 index c04e7c4003..0000000000 --- a/lib/lib.misc.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Miscellaneous common functions for C/C++ programs. - * - * Part of the DOMjudge Programming Contest Jury System and licensed - * under the GNU GPL. See README and COPYING for details. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "lib.misc.h" -#include "lib.error.h" - -/* Array indices for input/output file descriptors as used by pipe() */ -#define PIPE_IN 1 -#define PIPE_OUT 0 - -const int def_stdio_fd[3] = { STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO }; - -void _alert(const char *libdir, const char *msgtype, const char *description) -{ - static char none[1] = ""; - char *cmd; - int silenced __attribute__((unused)); - - if ( description==NULL ) description = none; - - cmd = allocstr("%s/alert '%s' '%s' &",libdir,msgtype,description); - logmsg(LOG_INFO,"executing '%s'",cmd); - - /* Assign return value to temp variable to remove compiler - * warnings. We're already trying to generate a warning; there's - * no sense in generating another warning when this gives an - * error. - */ - silenced = system(cmd); - - free(cmd); -} - -int execute(const char *cmd, const char **args, int nargs, int stdio_fd[3], int err2out) -{ - pid_t pid, child_pid; - int redirect; - int status; - int pipe_fd[3][2]; - char **argv; - int i, dir; - - if ( (argv=(char **) malloc((nargs+2)*sizeof(char *)))==NULL ) return -1; - - if ( err2out ) stdio_fd[2] = FDREDIR_NONE; - - redirect = ( stdio_fd[0]!=FDREDIR_NONE || - stdio_fd[1]!=FDREDIR_NONE || - stdio_fd[2]!=FDREDIR_NONE ); - - /* Build the complete argument list for execvp. - * We can const-cast the pointers, since execvp is guaranteed - * not to modify these (or the data pointed to). - */ - argv[0] = (char *) cmd; - for(i=0; i=0 ) { - if ( dup2(stdio_fd[i],def_stdio_fd[i])<0 ) goto ret_error; - if ( close(stdio_fd[i])!=0 ) goto ret_error; - } - } - /* Redirect stderr to stdout */ - if ( err2out && dup2(STDOUT_FILENO,STDERR_FILENO)<0 ) goto ret_error; - - /* Replace child with command */ - execvp(cmd,argv); - abort(); - - default: /* parent process */ - - free(argv); - - /* Set and close file descriptors */ - for(i=0; i<3; i++) { - if ( stdio_fd[i]==FDREDIR_PIPE ) { - /* parent process output must connect to the input of - the pipe to child, and vice versa for stdout/stderr: */ - dir = (i==0 ? PIPE_IN : PIPE_OUT); - stdio_fd[i] = pipe_fd[i][dir]; - if ( close(pipe_fd[i][1-dir])!=0 ) return -1; - } - } - - /* Return if some IO is redirected to be able to read/write to child */ - if ( redirect ) return child_pid; - - /* Wait for the child command to finish */ - while ( (pid = wait(&status))!=-1 && pid!=child_pid ); - if ( pid!=child_pid ) return -1; - - /* Test whether command has finished abnormally */ - if ( ! WIFEXITED(status) ) { - if ( WIFSIGNALED(status) ) return 128+WTERMSIG(status); - if ( WIFSTOPPED (status) ) return 128+WSTOPSIG(status); - return -2; - } - return WEXITSTATUS(status); - } - - /* This should never be reached */ - return -2; - - /* Handle resources before returning on error */ - ret_error: - free(argv); - return -1; -} - -int exitsignalled; - -void sig_handler(int sig) -{ - logmsg(LOG_DEBUG, "Signal %d received", sig); - - switch ( sig ) { - case SIGTERM: - case SIGHUP: - case SIGINT: - exitsignalled = 1; - break; - } -} - -void initsignals() -{ - struct sigaction sa; - sigset_t newmask, oldmask; - - exitsignalled = 0; - - /* unmask all signals */ - memset(&newmask, 0, sizeof(newmask)); - if ( sigprocmask(SIG_SETMASK, &newmask, &oldmask)!=0 ) { - error(errno,"unmasking signals"); - } - - logmsg(LOG_DEBUG, "Installing signal handlers"); - - sa.sa_handler = &sig_handler; - sa.sa_mask = newmask; - sa.sa_flags = 0; - - if ( sigaction(SIGTERM,&sa,NULL)!=0 ) error(errno,"installing signal handler"); - if ( sigaction(SIGHUP ,&sa,NULL)!=0 ) error(errno,"installing signal handler"); - if ( sigaction(SIGINT ,&sa,NULL)!=0 ) error(errno,"installing signal handler"); -} - - -char *pidfile; - -/* Function to remove PID file at process exit. */ -void remove_pidfile() -{ - unlink(pidfile); -} - -void daemonize(const char *_pidfile) -{ - pid_t pid; - int fd, maxfd; - char str[15]; - - switch ( pid = fork() ) { - case -1: error(errno, "cannot fork daemon"); - case 0: break; /* child process: do nothing here. */ - default: _exit(0); /* parent process: exit. */ - } - - /* Check and write PID to file */ - if ( _pidfile!=NULL ) { - pidfile = strdup(_pidfile); - if ( (fd=open(pidfile, O_RDWR|O_CREAT|O_EXCL, 0640))<0 ) { - error(errno, "cannot create pidfile '%s'", pidfile); - } - sprintf(str, "%d\n", pid); - if ( write(fd, str, strlen(str))<(ssize_t)strlen(str) ) { - error(errno, "failed writing PID to file"); - } - if ( close(fd)!=0 ) error(errno, "closing pidfile '%s'", pidfile); - atexit(remove_pidfile); - } - - /* Notify user with daemon PID before detaching from TTY. */ - logmsg(LOG_NOTICE, "daemonizing with PID = %d", pid); - - /* Reopen std{in,out,err} file descriptors to /dev/null. - Closing them gives error when the daemon or a child process - tries to read/write to them. */ - if ( freopen("/dev/null", "r", stdin )!=NULL || - freopen("/dev/null", "w", stdout)!=NULL || - freopen("/dev/null", "w", stderr)!=NULL ) { - error(errno, "cannot reopen stdio files to /dev/null"); - } - - /* Close all other file descriptors. */ - maxfd = sysconf(_SC_OPEN_MAX); - for(fd=3; fd +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lib.misc.h" +#include "lib.error.h" + +/* Array indices for input/output file descriptors as used by pipe() */ +constexpr int PIPE_IN = 1; +constexpr int PIPE_OUT = 0; + +const int def_stdio_fd[3] = { STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO }; + +int execute(const std::string& cmd, const std::vector& args, + std::array& stdio_fd, bool err2out) +{ + if ( err2out ) stdio_fd[2] = FDREDIR_NONE; + + const bool redirect = ( stdio_fd[0]!=FDREDIR_NONE || + stdio_fd[1]!=FDREDIR_NONE || + stdio_fd[2]!=FDREDIR_NONE ); + + /* Build the complete argument list for execvp. + * We can const-cast the pointers, since execvp is guaranteed + * not to modify these (or the data pointed to). + */ + std::vector argv; + argv.push_back(const_cast(cmd.c_str())); + for (const auto& arg : args) { + argv.push_back(const_cast(arg.c_str())); + } + argv.push_back(nullptr); + + int pipe_fd[3][2]; + /* Open pipes for IO redirection */ + for(int i=0; i<3; i++) { + if ( stdio_fd[i]==FDREDIR_PIPE && pipe(pipe_fd[i])!=0 ) return -1; + } + + pid_t child_pid; + switch ( child_pid = fork() ) { + case -1: /* error */ + return -1; + + case 0: /* child process */ + /* Connect pipes to command stdin/stdout/stderr and close unneeded fd's */ + for(int i=0; i<3; i++) { + if ( stdio_fd[i]==FDREDIR_PIPE ) { + /* stdin must be connected to the pipe output, + stdout/stderr to the pipe input: */ + const int dir = (i==0 ? PIPE_OUT : PIPE_IN); + if ( dup2(pipe_fd[i][dir],def_stdio_fd[i])<0 ) return -1; + if ( close(pipe_fd[i][dir])!=0 ) return -1; + if ( close(pipe_fd[i][1-dir])!=0 ) return -1; + } + if ( stdio_fd[i]>=0 ) { + if ( dup2(stdio_fd[i],def_stdio_fd[i])<0 ) return -1; + if ( close(stdio_fd[i])!=0 ) return -1; + } + } + /* Redirect stderr to stdout */ + if ( err2out && dup2(STDOUT_FILENO,STDERR_FILENO)<0 ) return -1; + + /* Replace child with command */ + execvp(cmd.c_str(), argv.data()); + abort(); + + default: /* parent process */ + + /* Set and close file descriptors */ + for(int i=0; i<3; i++) { + if ( stdio_fd[i]==FDREDIR_PIPE ) { + /* parent process output must connect to the input of + the pipe to child, and vice versa for stdout/stderr: */ + const int dir = (i==0 ? PIPE_IN : PIPE_OUT); + stdio_fd[i] = pipe_fd[i][dir]; + if ( close(pipe_fd[i][1-dir])!=0 ) return -1; + } + } + + /* Return if some IO is redirected to be able to read/write to child */ + if ( redirect ) return child_pid; + + /* Wait for the child command to finish */ + int status; + pid_t pid; + while ( (pid = wait(&status))!=-1 && pid!=child_pid ); + if ( pid!=child_pid ) return -1; + + /* Test whether command has finished abnormally */ + if ( ! WIFEXITED(status) ) { + if ( WIFSIGNALED(status) ) return 128+WTERMSIG(status); + if ( WIFSTOPPED (status) ) return 128+WSTOPSIG(status); + return -2; + } + return WEXITSTATUS(status); + } + + /* This should never be reached */ + return -2; +} + + +void version(const char *prog, const char *vers) +{ + std::cout << prog << " -- part of DOMjudge version " << vers << std::endl + << "Written by the DOMjudge developers" << std::endl << std::endl + << "DOMjudge comes with ABSOLUTELY NO WARRANTY. This is free software, and you" << std::endl + << "are welcome to redistribute it under certain conditions. See the GNU" << std::endl + << "General Public Licence for details." << std::endl; + exit(0); +} diff --git a/lib/lib.misc.h b/lib/lib.misc.h index 15f34ba441..c39009f3f1 100644 --- a/lib/lib.misc.h +++ b/lib/lib.misc.h @@ -5,9 +5,9 @@ #ifndef LIB_MISC_H #define LIB_MISC_H -#ifdef __cplusplus -extern "C" { -#endif +#include +#include +#include /* I/O redirection options for execute() */ #define FDREDIR_NONE -1 @@ -17,29 +17,22 @@ extern "C" { * LIBDIR as defined in calling program. */ #define alert(msgtype,description) _alert(LIBDIR,msgtype,description) -void _alert(const char *libdir, const char *msgtype, const char *description) - __attribute__((nonnull (1, 2))); -/* Execute 'alert' plugin program to perform user configurable action - * on important system events. See default alert script for more details. - */ - -int execute(const char *, const char **, int, int[3], int) - __attribute__((nonnull (1, 2))); +int execute(const std::string& cmd, const std::vector& args, + std::array& stdio_fd, bool err2out); /* Execute a subprocess using fork and execvp and optionally perform * IO redirection of stdin/stdout/stderr. * * Arguments: - * char *cmd command to be executed (PATH is searched) - * char *args[] array of arguments to command - * int nargs number of arguments specified - * int stdio_fd[3] File descriptors for stdin, stdout and stderr respectively. - * Each can separately be set to one of the following: - * FDREDIR_NONE - don't do redirection - * FDREDIR_PIPE - connect to pipe and set value to file - * descriptor of other end of the pipe - * fd >= 0 - make this a duplicate of - * int err2out Set non-zero to redirect command stderr to stdout. When set - * the redirection of stderr by stdio_fd[2] is ignored. + * cmd command to be executed (PATH is searched) + * args vector of arguments to command + * stdio_fd File descriptors for stdin, stdout and stderr respectively. + * Each can separately be set to one of the following: + * FDREDIR_NONE - don't do redirection + * FDREDIR_PIPE - connect to pipe and set value to file + * descriptor of other end of the pipe + * fd >= 0 - make this a duplicate of + * err2out Set to true to redirect command stderr to stdout. When set + * the redirection of stderr by stdio_fd[2] is ignored. * * Returns: * On errors from system calls -1 is returned: check errno for extra information. @@ -53,36 +46,9 @@ int execute(const char *, const char **, int, int[3], int) * with the process-ID of the child command. */ -void initsignals(); -/* Installs a signal handler to gracefully terminate daemon programs - * upon receiving TERMINATE, HANGUP and INTERRUPT signals which sets - * 'extern int exitsignalled = 1'. The sleep() call will automatically - * return on receiving a signal. - */ - -void daemonize(const char *); -/* Forks and detaches the current process to run as a daemon. Similar - * to the daemon() call present in Linux and *BSD, but implemented here, - * because it is not specified by POSIX, SUSv2 or SVr4. - * - * Arguments: - * char *pidfile pidfile to check for running instances and write PID; - * set to NULL to not use a pidfile. - * - * Either returns successfully or exits with an error. - */ - -char *stripendline(char *) __attribute__((nonnull (1))); -/* Removes end-of-line characters (CR and LF) from string. Returns the - * original pointer to the modified string. */ - void version(const char *, const char *) __attribute__((nonnull (1, 2))); /* Print standard program name and version, with disclaimer and GPL * licence info. Arguments: program name and version strings. */ -#ifdef __cplusplus -} -#endif - #endif /* LIB_MISC_H */