diff --git a/.github/jobs/configure-checks/all.bats b/.github/jobs/configure-checks/all.bats index 6762d42433..3d0efcdd0c 100755 --- a/.github/jobs/configure-checks/all.bats +++ b/.github/jobs/configure-checks/all.bats @@ -106,7 +106,7 @@ compiler_assertions () { compile_assertions_finished () { assert_line " * CFLAGS..............: -g -O2 -Wall -Wformat -Wformat-security -pedantic -fstack-protector -fPIE -D_FORTIFY_SOURCE=2 -std=c11" - assert_line " * CXXFLAGS............: -g -O2 -Wall -Wformat -Wformat-security -pedantic -fstack-protector -fPIE -D_FORTIFY_SOURCE=2 -std=c++11" + assert_line " * CXXFLAGS............: -g -O2 -Wall -Wformat -Wformat-security -pedantic -fstack-protector -fPIE -D_FORTIFY_SOURCE=2 -std=c++20" assert_line " * LDFLAGS.............: -fPIE -pie -Wl,-z,relro -Wl,-z,now" } diff --git a/configure.ac b/configure.ac index d75637c44b..fea76ebd0a 100644 --- a/configure.ac +++ b/configure.ac @@ -72,7 +72,7 @@ if test "x$JUDGEHOST_BUILD_ENABLED" = xyes; then AX_APPEND_LINK_FLAGS([-Wl,-z,now], DEF_LDFLAGS) test "x$enable_CFLAGS_setting" = xyes && AC_SUBST(CFLAGS, "$DEF_CXFLAGS -std=c11") - test "x$enable_CXXFLAGS_setting" = xyes && AC_SUBST(CXXFLAGS, "$DEF_CXFLAGS -std=c++11") + test "x$enable_CXXFLAGS_setting" = xyes && AC_SUBST(CXXFLAGS, "$DEF_CXFLAGS -std=c++20") test "x$enable_LDFLAGS_setting" = xyes && AC_SUBST(LDFLAGS, $DEF_LDFLAGS) fi diff --git a/judge/evict.cc b/judge/evict.cc index cdc1f1b103..fc72fc2255 100644 --- a/judge/evict.cc +++ b/judge/evict.cc @@ -14,15 +14,13 @@ #include "lib.misc.h" -extern "C" { -#include "lib.error.h" -} +#include "lib.error.hpp" #define PROGRAM "evict" #define VERSION DOMJUDGE_VERSION "/" REVISION extern int errno; -const char *progname; +std::string_view progname; int be_verbose; int show_help; @@ -53,7 +51,7 @@ void evict_directory(const std::string& dirname) { dir = opendir(dirname.c_str()); if (dir != NULL) { - if (be_verbose) logmsg(LOG_INFO, "Evicting all files in directory: %s", dirname.c_str()); + if (be_verbose) logmsg(LOG_INFO, "Evicting all files in directory: {}", dirname); /* Read everything in the directory */ while ( (entry = readdir(dir)) != NULL ) { @@ -66,14 +64,14 @@ void evict_directory(const std::string& dirname) { std::string entry_path = dirname + "/" + entry->d_name; fd = open(entry_path.c_str(), O_RDONLY, 0); if (fd == -1) { - warning(errno, "Unable to open file: %s", entry_path.c_str()); + warning(errno, "Unable to open file: {}", entry_path); continue; } if (fstat(fd, &s) < 0) { - if (be_verbose) logerror(errno, "Unable to stat file/directory: %s\n", entry_path.c_str()); + if (be_verbose) logerror(errno, "Unable to stat file/directory: {}\n", entry_path); if ( close(fd)!=0 ) { - warning(errno, "Unable to close file: %s", entry_path.c_str()); + warning(errno, "Unable to close file: {}", entry_path); } continue; } @@ -83,21 +81,21 @@ void evict_directory(const std::string& dirname) { } else { /* evict this file from the cache */ if (posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED)) { - warning(errno, "Unable to evict file: %s\n", entry_path.c_str()); + warning(errno, "Unable to evict file: {}\n", entry_path); } else { - if (be_verbose) logmsg(LOG_DEBUG, "Evicted file: %s", entry_path.c_str()); + if (be_verbose) logmsg(LOG_DEBUG, "Evicted file: {}", entry_path); } } if ( close(fd)!=0 ) { - warning(errno, "Unable to close file: %s", entry_path.c_str()); + warning(errno, "Unable to close file: {}", entry_path); } } if ( closedir(dir)!=0 ) { - warning(errno, "Unable to close directory: %s", dirname.c_str()); + warning(errno, "Unable to close directory: {}", dirname); } } else { - warning(errno, "Unable to open directory: %s", dirname.c_str()); + warning(errno, "Unable to open directory: {}", dirname); } } @@ -120,10 +118,10 @@ int main(int argc, char *argv[]) break; case ':': /* getopt error */ case '?': - logmsg(LOG_ERR, "unknown option or missing argument `%c'", optopt); + logmsg(LOG_ERR, "unknown option or missing argument `{}`", (char)optopt); break; default: - logmsg(LOG_ERR, "getopt returned character code `%c' ??", (char)opt); + logmsg(LOG_ERR, "getopt returned character code `{}` ??", (char)opt); } } diff --git a/judge/runguard.cc b/judge/runguard.cc index 6cda4645b8..845a38b07f 100644 --- a/judge/runguard.cc +++ b/judge/runguard.cc @@ -29,11 +29,13 @@ #include "config.h" +#include "lib.error.hpp" #include "lib.misc.h" /* Some system/site specific config: VALID_USERS, CHROOT_PREFIX */ #include "runguard-config.h" +#include #include #include #include @@ -69,6 +71,7 @@ #include #include #include +#include #define PROGRAM "runguard" #define VERSION DOMJUDGE_VERSION "/" REVISION @@ -102,10 +105,9 @@ const struct timespec killdelay = { 0, 100000000L }; /* 0.1 seconds */ const struct timespec cg_delete_delay = { 0, 10000000L }; /* 0.01 seconds */ extern int errno; +extern int verbose; -const int exit_failure = -1; - -char *progname; +std::string_view progname; char *cmdname; char **cmdargs; char *rootdir; @@ -135,8 +137,6 @@ int outputmeta; int outputtimetype; int no_coredump; int preserve_environment; -int be_verbose; -int be_quiet; int show_help; int show_version; int in_error_handling = 0; @@ -188,71 +188,35 @@ struct option const long_opts[] = { { nullptr, 0, nullptr, 0 } }; -void warning( const char *, ...) __attribute__((format (printf, 1, 2))); -void verbose( const char *, ...) __attribute__((format (printf, 1, 2))); -void error(int, const char *, ...) __attribute__((format (printf, 2, 3))); -void write_meta(const char *, const char *, ...) __attribute__((format (printf, 2, 3))); - -void warning(const char *format, ...) -{ - va_list ap; - va_start(ap,format); - - if ( ! be_quiet ) { - fprintf(stderr,"%s: warning: ",progname); - vfprintf(stderr,format,ap); - fprintf(stderr,"\n"); - } - - va_end(ap); -} - -void verbose(const char *format, ...) -{ - va_list ap; - va_start(ap,format); - - if ( ! be_quiet && be_verbose ) { - struct timeval currtime{}; - gettimeofday(&currtime,nullptr); - double runtime = (currtime.tv_sec - progstarttime.tv_sec ) + - (currtime.tv_usec - progstarttime.tv_usec)*1E-6; - fprintf(stderr,"%s [%d @ %10.6lf]: verbose: ",progname,getpid(),runtime); - vfprintf(stderr,format,ap); - fprintf(stderr,"\n"); - } - - va_end(ap); -} +template +void write_meta(const std::string& key, std::format_string fmt, Args&&... args); // These functions are called from signal handlers, so they // must only call async-signal-safe functions. // write() is async-signal-safe, printf and variants are not. void verbose_from_signalhandler(const char* msg) { - if (!be_quiet && be_verbose) { + if (verbose >= LOG_DEBUG) { [[maybe_unused]] auto r = write(STDERR_FILENO, msg, strlen(msg)); } } void warning_from_signalhandler(const char* msg) { - if (!be_quiet) { + if (verbose >= LOG_WARNING) { // Do not include timing here, as it wouldn't be safe from a signalhandler. // TODO: Consider rewriting using clock_gettime in the future. [[maybe_unused]] auto r = write(STDERR_FILENO, msg, strlen(msg)); } } -void error(int errnum, const char *format, ...) +template +void die(int errnum, std::format_string fmt, Args&&... args) { // Silently ignore errors that happen while handling other errors. if (in_error_handling) return; in_error_handling = 1; - va_list ap; - va_start(ap,format); - /* * Make sure the signal handler for these (terminate()) does not * interfere, we are exiting now anyway. @@ -262,54 +226,43 @@ void error(int errnum, const char *format, ...) sigaddset(&sigs, SIGTERM); sigprocmask(SIG_BLOCK, &sigs, nullptr); - /* First print to string to be able to reuse the message. */ - size_t errlen = strlen(progname)+255; - if ( format!=nullptr ) errlen += strlen(format); - - char *errstr = (char *)malloc(errlen); - if ( errstr==nullptr ) abort(); - - sprintf(errstr,"%s",progname); - size_t errpos = strlen(errstr); - - if ( format!=nullptr ) { - snprintf(errstr+errpos,errlen-errpos,": "); - errpos += 2; - vsnprintf(errstr+errpos,errlen-errpos,format,ap); - errpos += strlen(errstr+errpos); + std::string errstr(progname); + errstr += ": "; + try { + errstr += std::format(fmt, std::forward(args)...); + } catch (const std::exception& e) { + errstr += "Error formatting error message: " + std::string(e.what()); } - if ( errnum!=0 ) { + + if (errnum == 0) { + errstr += ": unknown error"; + } else { /* Special case libcgroup error codes. */ if ( errnum==ECGOTHER ) { - snprintf(errstr+errpos,errlen-errpos,": libcgroup"); - errpos += strlen(errstr+errpos); + errstr += ": libcgroup"; errnum = errno; } if ( errnum>=ECGROUPNOTCOMPILED && errnum<=ECGROUPNOTCOMPILED ) { - snprintf(errstr+errpos,errlen-errpos,": %s",cgroup_strerror(errnum)); + errstr += ": "; + errstr += cgroup_strerror(errnum); } else { - snprintf(errstr+errpos,errlen-errpos,": %s",strerror(errnum)); + errstr += ": "; + errstr += strerror(errnum); } - errpos += strlen(errstr+errpos); - } - if ( format==nullptr && errnum==0 ) { - snprintf(errstr+errpos,errlen-errpos,": unknown error"); } - fprintf(stderr,"%s\nTry `%s --help' for more information.\n",errstr,progname); - va_end(ap); + std::cerr << errstr << std::endl; - write_meta("internal-error","%s",errstr); + write_meta("internal-error","{}", errstr); if ( outputmeta && metafile != nullptr && fclose(metafile)!=0 ) { fprintf(stderr,"\nError writing to metafile '%s'.\n",metafilename); } /* Make sure that all children are killed before terminating */ if ( child_pid > 0) { - verbose("sending SIGKILL"); + logmsg(LOG_DEBUG, "sending SIGKILL"); if ( kill(-child_pid,SIGKILL)!=0 && errno!=ESRCH ) { - fprintf(stderr,"unable to send SIGKILL to children while terminating " - "due to previous error: %s\n", strerror(errno)); + logmsg(LOG_ERR, "unable to send SIGKILL to children while terminating due to previous error: {}", strerror(errno)); /* * continue, there is not much we can do here. * In the worst case, this will trigger an error @@ -325,27 +278,26 @@ void error(int errnum, const char *format, ...) exit(exit_failure); } -void write_meta(const char *key, const char *format, ...) +template +void write_meta(const std::string& key, std::format_string fmt, Args&&... args) { if ( !outputmeta ) return; - va_list ap; - va_start(ap,format); - - if ( fprintf(metafile,"%s: ",key)<=0 ) { - outputmeta = 0; - error(0,"cannot write to file `%s'",metafilename); - } - if ( vfprintf(metafile,format,ap)<0 ) { + if ( fprintf(metafile,"%s: ",key.c_str())<=0 ) { outputmeta = 0; - error(0,"cannot write to file `%s'(vfprintf)",metafilename); + die(0,"cannot write to file `{}'",metafilename); } - if ( fprintf(metafile,"\n")<=0 ) { + + try { + std::string value = std::format(fmt, std::forward(args)...); + if ( fprintf(metafile, "%s\n", value.c_str()) <= 0 ) { + outputmeta = 0; + die(0,"cannot write to file `{}'", metafilename); + } + } catch (const std::exception& e) { outputmeta = 0; - error(0,"cannot write to file `%s'",metafilename); + die(0, "Error formatting meta value for key {}: {}", key, e.what()); } - - va_end(ap); } void usage() @@ -353,7 +305,7 @@ void usage() printf("\ Usage: %s [OPTION]... COMMAND...\n\ Run COMMAND with restrictions.\n\ -\n", progname); +\n", progname.data()); printf("\ -r, --root=ROOT run COMMAND with root directory set to ROOT\n\ -u, --user=USER run COMMAND as user with username or ID USER\n\ @@ -397,11 +349,11 @@ real user ID.\n"); void output_exit_time(int exitcode, double cpudiff) { - verbose("command exited with exitcode %d",exitcode); - write_meta("exitcode","%d",exitcode); + logmsg(LOG_DEBUG, "command exited with exitcode {}",exitcode); + write_meta("exitcode","{}",exitcode); if (received_signal != -1) { - write_meta("signal", "%d", received_signal); + write_meta("signal", "{}", (int)received_signal); } double walldiff = (endtime.tv_sec - starttime.tv_sec ) + @@ -411,22 +363,22 @@ void output_exit_time(int exitcode, double cpudiff) double userdiff = (double)(endticks.tms_cutime - startticks.tms_cutime) / ticks_per_second; double sysdiff = (double)(endticks.tms_cstime - startticks.tms_cstime) / ticks_per_second; - write_meta("wall-time","%.3f", walldiff); - write_meta("user-time","%.3f", userdiff); - write_meta("sys-time", "%.3f", sysdiff); - write_meta("cpu-time", "%.3f", cpudiff); + write_meta("wall-time","{:.3f}", walldiff); + write_meta("user-time","{:.3f}", userdiff); + write_meta("sys-time", "{:.3f}", sysdiff); + write_meta("cpu-time", "{:.3f}", cpudiff); - verbose("runtime is %.3f seconds real, %.3f user, %.3f sys", + logmsg(LOG_DEBUG, "runtime is {:.3f} seconds real, {:.3f} user, {:.3f} sys", walldiff, userdiff, sysdiff); if ( use_walltime && walldiff > walltimelimit[0] ) { walllimit_reached |= soft_timelimit; - warning("timelimit exceeded (soft wall time)"); + warning(0, "timelimit exceeded (soft wall time)"); } if ( use_cputime && cpudiff > cputimelimit[0] ) { cpulimit_reached |= soft_timelimit; - warning("timelimit exceeded (soft cpu time)"); + warning(0, "timelimit exceeded (soft cpu time)"); } int timelimit_reached = 0; @@ -440,7 +392,7 @@ void output_exit_time(int exitcode, double cpudiff) timelimit_reached = cpulimit_reached; break; default: - error(0,"cannot write unknown time type `%d' to file",outputtimetype); + die(0,"cannot write unknown time type `{}' to file",outputtimetype); } /* Hard limitlimit reached always has precedence. */ @@ -448,7 +400,7 @@ void output_exit_time(int exitcode, double cpudiff) timelimit_reached |= hard_timelimit; } - write_meta("time-result","%s",output_timelimit_str[timelimit_reached]); + write_meta("time-result","{}",output_timelimit_str[timelimit_reached]); } std::set parse_cpuset(std::string cpus) @@ -464,14 +416,14 @@ std::set parse_cpuset(std::string cpus) std::string token2 = token.substr(split+1); size_t len; unsigned cpu1 = std::stoul(token1, &len); - if ( len parse_cpuset(std::string cpus) std::set read_cpuset(const char *path) { FILE *file = fopen(path, "r"); - if (file == nullptr) error(errno, "opening file `%s'", path); + if (file == nullptr) die(errno, "opening file `{}'", path); char cpuset[1024]; - if (fgets(cpuset, 1024, file) == nullptr) error(errno, "reading from file `%s'", path); + if (fgets(cpuset, 1024, file) == nullptr) die(errno, "reading from file `{}'", path); size_t len = strlen(cpuset); if (len > 0 && cpuset[len-1] == '\n') cpuset[len-1] = 0; - if (fclose(file) != 0) error(errno, "closing file `%s'", path); + if (fclose(file) != 0) die(errno, "closing file `{}'", path); return parse_cpuset(cpuset); } @@ -502,50 +454,50 @@ void check_remaining_procs() FILE *file = fopen(path, "r"); if (file == nullptr) { - error(errno, "opening cgroups file `%s'", path); + die(errno, "opening cgroups file `{}'", path); } fseek(file, 0L, SEEK_END); if (ftell(file) > 0) { - error(0, "found left-over processes in cgroup controller, please check!"); + die(0, "found left-over processes in cgroup controller, please check!"); } - if (fclose(file) != 0) error(errno, "closing file `%s'", path); + if (fclose(file) != 0) die(errno, "closing file `{}'", path); } void output_cgroup_stats(double *cputime) { struct cgroup *cg; - if ( (cg = cgroup_new_cgroup(cgroupname))==nullptr ) error(0,"cgroup_new_cgroup"); + if ( (cg = cgroup_new_cgroup(cgroupname))==nullptr ) die(0,"cgroup_new_cgroup"); int ret; - if ((ret = cgroup_get_cgroup(cg)) != 0) error(ret,"get cgroup information"); + if ((ret = cgroup_get_cgroup(cg)) != 0) die(ret,"get cgroup information"); struct cgroup_controller *cg_controller = cgroup_get_controller(cg, "memory"); int64_t max_usage = 0; ret = cgroup_get_value_int64(cg_controller, "memory.peak", &max_usage); if ( ret == ECGROUPVALUENOTEXIST ) { - error(ret, "kernel too old and does not support memory.peak"); + die(ret, "kernel too old and does not support memory.peak"); } else if ( ret!=0 ) { - error(ret,"get cgroup value memory.peak"); + die(ret,"get cgroup value memory.peak"); } // There is no need to check swap usage, as we limit it to 0. - verbose("total memory used: %" PRId64 " kB", max_usage/1024); - write_meta("memory-bytes","%" PRId64, max_usage); + logmsg(LOG_DEBUG, "total memory used: {} kB", max_usage/1024); + write_meta("memory-bytes","{}", max_usage); struct cgroup_stat stat; void *handle; ret = cgroup_read_stats_begin("cpu", cgroupname, &handle, &stat); while (ret == 0) { - verbose("cpu.stat: %s = %s", stat.name, stat.value); + logmsg(LOG_DEBUG, "cpu.stat: {} = {}", stat.name, stat.value); if (strcmp(stat.name, "usage_usec") == 0) { long long usec = strtoll(stat.value, nullptr, 10); *cputime = usec / 1e6; } ret = cgroup_read_stats_next(&handle, &stat); } - if ( ret!=ECGEOF ) error(ret,"get cgroup value cpu.stat"); + if ( ret!=ECGEOF ) die(ret,"get cgroup value cpu.stat"); cgroup_read_stats_end(&handle); cgroup_free(&cg); @@ -554,19 +506,19 @@ void output_cgroup_stats(double *cputime) /* Temporary shorthand define for error handling. */ #define cgroup_add_value(type,name,value) \ ret = cgroup_add_value_ ## type(cg_controller, name, value); \ - if ( ret!=0 ) error(ret,"set cgroup value " #name); + if ( ret!=0 ) die(ret,"set cgroup value " #name); void cgroup_create() { struct cgroup *cg; cg = cgroup_new_cgroup(cgroupname); - if (!cg) error(0,"cgroup_new_cgroup"); + if (!cg) die(0,"cgroup_new_cgroup"); /* Set up the memory restrictions; these two options limit ram use and ram+swap use. They are the same so no swapping can occur */ struct cgroup_controller *cg_controller; if ( (cg_controller = cgroup_add_controller(cg, "memory"))==nullptr ) { - error(0,"cgroup_add_controller memory"); + die(0,"cgroup_add_controller memory"); } int ret; @@ -584,7 +536,7 @@ void cgroup_create() no limits on memory nodes */ if ( cpuset!=nullptr && strlen(cpuset)>0 ) { if ( (cg_controller = cgroup_add_controller(cg, "cpuset"))==nullptr ) { - error(0,"cgroup_add_controller cpuset"); + die(0,"cgroup_add_controller cpuset"); } /* To make a cpuset exclusive, some additional setup outside of domjudge is required, so for now, we will leave this commented out. */ @@ -592,14 +544,14 @@ void cgroup_create() cgroup_add_value(string, "cpuset.mems", "0"); cgroup_add_value(string, "cpuset.cpus", cpuset); } else { - verbose("cpuset undefined"); + logmsg(LOG_DEBUG, "cpuset undefined"); } /* Perform the actual creation of the cgroup */ - if ( (ret = cgroup_create_cgroup(cg, 1))!=0 ) error(ret,"creating cgroup"); + if ( (ret = cgroup_create_cgroup(cg, 1))!=0 ) die(ret,"creating cgroup"); cgroup_free(&cg); - verbose("created cgroup '%s'",cgroupname); + logmsg(LOG_DEBUG, "created cgroup `{}'",cgroupname); } #undef cgroup_setval @@ -613,7 +565,7 @@ void cgroup_kill() do { pid_t* pids; int ret = cgroup_get_procs(cgroupname, mem_controller, &pids, &size); - if (ret != 0) error(ret, "cgroup_get_procs"); + if(ret != 0) die(ret, "cgroup_get_procs"); for(int i = 0; i < size; i++) { kill(pids[i], SIGKILL); } @@ -625,23 +577,23 @@ void cgroup_delete() { struct cgroup *cg; cg = cgroup_new_cgroup(cgroupname); - if (!cg) error(0,"cgroup_new_cgroup"); + if (!cg) die(0,"cgroup_new_cgroup"); - if (cgroup_add_controller(cg, "cpu") == nullptr) error(0, "cgroup_add_controller cpu"); - if ( cgroup_add_controller(cg, "memory")==nullptr ) error(0,"cgroup_add_controller memory"); + if (cgroup_add_controller(cg, "cpu") == nullptr) die(0, "cgroup_add_controller cpu"); + if ( cgroup_add_controller(cg, "memory")==nullptr ) die(0,"cgroup_add_controller memory"); if ( cpuset!=nullptr && strlen(cpuset)>0 ) { - if ( cgroup_add_controller(cg, "cpuset")==nullptr ) error(0,"cgroup_add_controller cpuset"); + if ( cgroup_add_controller(cg, "cpuset")==nullptr ) die(0,"cgroup_add_controller cpuset"); } /* Clean up our cgroup */ nanosleep(&cg_delete_delay,nullptr); int ret = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION | CGFLAG_DELETE_RECURSIVE); // TODO: is this actually benign to ignore ECGOTHER here? - if ( ret!=0 && ret!=ECGOTHER ) error(ret,"deleting cgroup"); + if ( ret!=0 && ret!=ECGOTHER ) die(ret,"deleting cgroup"); cgroup_free(&cg); - verbose("deleted cgroup '%s'",cgroupname); + logmsg(LOG_DEBUG, "deleted cgroup `{}'",cgroupname); } void terminate(int sig) @@ -736,7 +688,7 @@ char *username() struct passwd *pwd; pwd = getpwuid(getuid()); - if ( pwd==nullptr || errno ) error(errno,"failed to get username"); + if ( pwd==nullptr || errno ) die(errno,"failed to get username"); errno = saved_errno; return pwd->pw_name; @@ -749,7 +701,7 @@ long read_optarg_int(const char *desc, long minval, long maxval) errno = 0; long arg = strtol(optarg,&ptr,10); if ( errno || *ptr!='\0' || argmaxval ) { - error(errno,"invalid %s specified: `%s'",desc,optarg); + die(errno,"invalid {} specified: `{}'",desc,optarg); } return arg; @@ -758,7 +710,7 @@ long read_optarg_int(const char *desc, long minval, long maxval) void read_optarg_time(const char *desc, double *times) { char *optcopy; - if ( (optcopy=strdup(optarg))==nullptr ) error(0,"strdup() failed"); + if ( (optcopy=strdup(optarg))==nullptr ) die(0,"strdup() failed"); /* Check for soft:hard limit separator and cut string. */ char *sep; @@ -768,7 +720,7 @@ void read_optarg_time(const char *desc, double *times) errno = 0; times[0] = strtod(optcopy,&ptr); if ( errno || *ptr!='\0' || !finite(times[0]) || times[0]<=0 ) { - error(errno,"invalid %s specified: `%s'",desc,optarg); + die(errno,"invalid {} specified: `{}'",desc,optarg); } /* And repeat for hard limit if we found the ':' separator. */ @@ -776,10 +728,10 @@ void read_optarg_time(const char *desc, double *times) errno = 0; times[1] = strtod(sep+1,&ptr); if ( errno || *(sep+1)=='\0' || *ptr!='\0' || !finite(times[1]) || times[1]<=0 ) { - error(errno,"invalid %s specified: `%s'",desc,optarg); + die(errno,"invalid {} specified: `{}'",desc,optarg); } if ( times[1]0 ) { @@ -1246,7 +1197,7 @@ int main(int argc, char **argv) for(unsigned cpu : cpus) { if ( !online_cpus.count(cpu) ) { - error(0, "requested pinning on CPU %u which is not online", cpu); + die(0, "requested pinning on CPU {} which is not online", cpu); } } } @@ -1254,7 +1205,7 @@ int main(int argc, char **argv) /* Make libcgroup ready for use */ ret = cgroup_init(); if ( ret!=0 ) { - error(0,"libcgroup initialization failed: %s(%d)\n", cgroup_strerror(ret), ret); + die(0,"libcgroup initialization failed: {}({})\n", cgroup_strerror(ret), ret); } /* Define the cgroup name that we will use and make sure it will * be unique. Note: group names must have slashes! @@ -1270,7 +1221,7 @@ int main(int argc, char **argv) cgroup_create(); if ( unshare(CLONE_FILES|CLONE_FS|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|CLONE_NEWUTS|CLONE_SYSVSEM)!=0 ) { - error(errno, "calling unshare"); + die(errno, "calling unshare"); } /* Check if any Linux Out-Of-Memory killer adjustments have to @@ -1281,68 +1232,68 @@ int main(int argc, char **argv) FILE *fp = nullptr; const char *oom_score_path = "/proc/self/oom_score_adj"; if ( (fp = fopen(oom_score_path, "r+"))!=nullptr ) { - if ( fscanf(fp,"%d", &ret)!=1 ) error(errno,"cannot read from `%s'", oom_score_path); + if ( fscanf(fp,"%d", &ret)!=1 ) die(errno,"cannot read from `{}'", oom_score_path); if ( ret<0 ) { int oom_reset_value = 0; - verbose("resetting `%s' from %d to %d", oom_score_path, ret, oom_reset_value); + die(0, "resetting `{}' from {} to {}", oom_score_path, ret, oom_reset_value); rewind(fp); if ( fprintf(fp,"%d\n", oom_reset_value) <= 0 ) { - error(errno, "cannot write to `%s'", oom_score_path); + die(errno, "cannot write to `{}'", oom_score_path); } } - if ( fclose(fp)!=0 ) error(errno, "closing file `%s'", oom_score_path); + if ( fclose(fp)!=0 ) die(errno, "closing file `{}'", oom_score_path); } switch ( child_pid = fork() ) { case -1: /* error */ - error(errno,"cannot fork"); + die(errno,"cannot fork"); case 0: /* run controlled command */ /* Apply all restrictions for child process. */ setrestrictions(); - verbose("setrestrictions() done"); + logmsg(LOG_DEBUG, "setrestrictions() done"); /* Connect pipes to command (stdin/)stdout/stderr and close * unneeded fd's. Do this after setting restrictions to let * any messages not go to command stderr pipe. */ for(int i=1; i<=2; i++) { if ( dup2(child_pipefd[i][PIPE_IN],i)<0 ) { - error(errno,"redirecting child fd %d",i); + die(errno,"redirecting child fd {}",i); } if ( close(child_pipefd[i][PIPE_IN] )!=0 || close(child_pipefd[i][PIPE_OUT])!=0 ) { - error(errno,"closing pipe for fd %d",i); + die(errno,"closing pipe for fd {}",i); } } - verbose("pipes closed in child"); + logmsg(LOG_DEBUG, "pipes closed in child"); if ( outputmeta ) { if ( fclose(metafile)!=0 ) { - error(errno,"closing file `%s'",metafilename); + die(errno,"closing file `{}'",metafilename); } - verbose("metafile closed in child"); + logmsg(LOG_DEBUG, "metafile closed in child"); } /* And execute child command. */ execvp(cmdname,cmdargs); - error(errno,"cannot start `%s' as user `%s'", cmdname, username()); + die(errno,"cannot start `{}' as user `{}'", cmdname, username()); default: /* become watchdog */ - verbose("child pid = %d", child_pid); + logmsg(LOG_DEBUG, "child pid = {}", child_pid); /* Shed privileges, only if not using a separate child uid, because in that case we may need root privileges to kill the child process. Do not use Linux specific setresuid() call with saved set-user-ID. */ if ( !use_user ) { - if ( setuid(getuid())!=0 ) error(errno, "setting watchdog uid"); - verbose("watchdog using user ID `%d'",getuid()); + if ( setuid(getuid())!=0 ) die(errno, "setting watchdog uid"); + logmsg(LOG_DEBUG, "watchdog using user ID `{}'",getuid()); } - if ( gettimeofday(&starttime,nullptr) ) error(errno,"getting time"); + if ( gettimeofday(&starttime,nullptr) ) die(errno,"getting time"); /* Close unused file descriptors */ for(int i=1; i<=2; i++) { if ( close(child_pipefd[i][PIPE_IN])!=0 ) { - error(errno,"closing pipe for fd %i",i); + die(errno,"closing pipe for fd {}",i); } } @@ -1355,24 +1306,24 @@ int main(int argc, char **argv) if ( redir_stdout ) { child_redirfd[STDOUT_FILENO] = creat(stdoutfilename, S_IRUSR | S_IWUSR); if ( child_redirfd[STDOUT_FILENO]<0 ) { - error(errno,"opening file '%s'",stdoutfilename); + die(errno,"opening file `{}'",stdoutfilename); } } if ( redir_stderr ) { child_redirfd[STDERR_FILENO] = creat(stderrfilename, S_IRUSR | S_IWUSR); if ( child_redirfd[STDERR_FILENO]<0 ) { - error(errno,"opening file '%s'",stderrfilename); + die(errno,"opening file `{}'",stderrfilename); } } - verbose("redirection done in parent"); + logmsg(LOG_DEBUG, "redirection done in parent"); - if ( sigemptyset(&emptymask)!=0 ) error(errno,"creating empty signal mask"); + if ( sigemptyset(&emptymask)!=0 ) die(errno,"creating empty signal mask"); /* Construct one-time signal handler to terminate() for TERM and ALRM signals. */ sigset_t sigmask = emptymask; if ( sigaddset(&sigmask,SIGALRM)!=0 || - sigaddset(&sigmask,SIGTERM)!=0 ) error(errno,"setting signal mask"); + sigaddset(&sigmask,SIGTERM)!=0 ) die(errno,"setting signal mask"); sigact.sa_handler = terminate; sigact.sa_flags = SA_RESETHAND | SA_RESTART; @@ -1380,13 +1331,13 @@ int main(int argc, char **argv) /* Kill child command when we receive SIGTERM */ if ( sigaction(SIGTERM,&sigact,nullptr)!=0 ) { - error(errno,"installing signal handler"); + die(errno,"installing signal handler"); } if ( use_walltime ) { /* Kill child when we receive SIGALRM */ if ( sigaction(SIGALRM,&sigact,nullptr)!=0 ) { - error(errno,"installing signal handler"); + die(errno,"installing signal handler"); } /* Trigger SIGALRM via setitimer: */ @@ -1396,13 +1347,13 @@ int main(int argc, char **argv) itimer.it_value.tv_usec = (int)(modf(walltimelimit[1],&tmpd) * 1E6); if ( setitimer(ITIMER_REAL,&itimer,nullptr)!=0 ) { - error(errno,"setting timer"); + die(errno,"setting timer"); } - verbose("setting hard wall-time limit to %.3f seconds",walltimelimit[1]); + logmsg(LOG_DEBUG, "setting hard wall-time limit to {:.3f} seconds",walltimelimit[1]); } if ( times(&startticks)==(clock_t) -1 ) { - error(errno,"getting start clock ticks"); + die(errno,"getting start clock ticks"); } /* Wait for child data or exit. @@ -1427,14 +1378,14 @@ int main(int argc, char **argv) } int r = pselect(nfds+1, &readfds, nullptr, nullptr, nullptr, &emptymask); - if ( r==-1 && errno!=EINTR ) error(errno,"waiting for child data"); + if ( r==-1 && errno!=EINTR ) die(errno,"waiting for child data"); if (error_in_signalhandler) { - error(errno, "error in signal handler, exiting"); + die(errno, "error in signal handler, exiting"); } if ( received_SIGCHLD || received_signal == SIGALRM ) { pid_t pid; - if ( (pid = wait(&status))<0 ) error(errno,"waiting on child"); + if ( (pid = wait(&status))<0 ) die(errno,"waiting on child"); if ( pid==child_pid ) break; } @@ -1448,11 +1399,11 @@ int main(int argc, char **argv) FD_SET(child_pipefd[i][PIPE_OUT],&readfds); int r = fcntl(child_pipefd[i][PIPE_OUT], F_GETFL); if (r == -1) { - error(errno, "fcntl, getting flags"); + die(errno, "fcntl, getting flags"); } r = fcntl(child_pipefd[i][PIPE_OUT], F_SETFL, r ^ O_NONBLOCK); if (r == -1) { - error(errno, "fcntl, setting flags"); + die(errno, "fcntl, setting flags"); } } } @@ -1465,14 +1416,14 @@ int main(int argc, char **argv) /* Close the output files */ for(int i=1; i<=2; i++) { ret = close(child_redirfd[i]); - if( ret!=0 ) error(errno,"closing output fd %d", i); + if( ret!=0 ) die(errno,"closing output fd {}", i); } if ( times(&endticks)==(clock_t) -1 ) { - error(errno,"getting end clock ticks"); + die(errno,"getting end clock ticks"); } - if ( gettimeofday(&endtime,nullptr) ) error(errno,"getting time"); + if ( gettimeofday(&endtime,nullptr) ) die(errno,"getting time"); /* Test whether command has finished abnormally */ int exitcode = 0; @@ -1480,22 +1431,22 @@ int main(int argc, char **argv) if ( WIFSIGNALED(status) ) { if ( WTERMSIG(status)==SIGXCPU ) { cpulimit_reached |= hard_timelimit; - warning("timelimit exceeded (hard cpu time)"); + warning(0, "timelimit exceeded (hard cpu time)"); } else { - warning("command terminated with signal %d",WTERMSIG(status)); + warning(0, "command terminated with signal {}",WTERMSIG(status)); } exitcode = 128+WTERMSIG(status); } else if ( WIFSTOPPED(status) ) { - warning("command stopped with signal %d",WSTOPSIG(status)); + warning(0, "command stopped with signal {}",WSTOPSIG(status)); exitcode = 128+WSTOPSIG(status); } else { - error(0,"command exit status unknown: %d",status); + die(0,"command exit status unknown: {}",status); } } else { exitcode = WEXITSTATUS(status); } - verbose("child exited with exit code %d", exitcode); + logmsg(LOG_DEBUG, "child exited with exit code {}", exitcode); if ( use_walltime ) { /* Disarm timer we set previously so if any of the @@ -1507,7 +1458,7 @@ int main(int argc, char **argv) itimer.it_value.tv_usec = 0; if ( setitimer(ITIMER_REAL,&itimer,nullptr)!=0 ) { - error(errno,"disarming timer"); + die(errno,"disarming timer"); } } @@ -1519,7 +1470,7 @@ int main(int argc, char **argv) cgroup_delete(); /* Drop root before writing to output file(s). */ - if ( setuid(getuid())!=0 ) error(errno,"dropping root privileges"); + if ( setuid(getuid())!=0 ) die(errno,"dropping root privileges"); output_exit_time(exitcode, cputime); @@ -1534,15 +1485,15 @@ int main(int argc, char **argv) if ( ptr!=str ) ptr = stpcpy(ptr,","); ptr = stpcpy(ptr,"stderr"); } - write_meta("output-truncated","%s",str); + write_meta("output-truncated","{}",str); } - write_meta("stdin-bytes", "%zu",data_read[0]); - write_meta("stdout-bytes","%zu",data_read[1]); - write_meta("stderr-bytes","%zu",data_read[2]); + write_meta("stdin-bytes", "{}",data_read[0]); + write_meta("stdout-bytes","{}",data_read[1]); + write_meta("stderr-bytes","{}",data_read[2]); if ( outputmeta && fclose(metafile)!=0 ) { - error(errno,"closing file `%s'",metafilename); + die(errno,"closing file `{}'",metafilename); } /* Return the exitstatus of the command */ @@ -1550,5 +1501,5 @@ int main(int argc, char **argv) } /* This should never be reached */ - error(0,"unexpected end of program"); + die(0,"unexpected end of program"); } diff --git a/judge/runpipe.cc b/judge/runpipe.cc index be48667d6b..cb8849fb30 100644 --- a/judge/runpipe.cc +++ b/judge/runpipe.cc @@ -62,7 +62,7 @@ #include "config.h" -#include "lib.error.h" +#include "lib.error.hpp" #include "lib.misc.h" #include @@ -88,13 +88,13 @@ using namespace std; using fd_t = int; -const char *progname; +std::string_view progname; // Set the NONBLOCK flag for a file descriptor. void set_non_blocking(fd_t fd) { int flags = fcntl(fd, F_GETFL, 0); if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { - error(errno, "failed to set fd %d to non blocking", fd); + error(errno, "failed to set fd {} to non blocking", fd); } } @@ -117,28 +117,28 @@ void resize_pipe(int fd) { FILE *f = nullptr; if ((f = fopen(PROC_MAX_PIPE_SIZE, "r")) == nullptr) { max_pipe_size = FAILED; - warning(errno, "could not open '%s'", PROC_MAX_PIPE_SIZE); + warning(errno, "could not open '{}'", PROC_MAX_PIPE_SIZE); return; } if (fscanf(f, "%d", &max_pipe_size) != 1) { max_pipe_size = FAILED; - warning(errno, "could not read from '%s'", PROC_MAX_PIPE_SIZE); + warning(errno, "could not read from '{}'", PROC_MAX_PIPE_SIZE); if (fclose(f) != 0) { - warning(errno, "could not close '%s'", PROC_MAX_PIPE_SIZE); + warning(errno, "could not close '{}'", PROC_MAX_PIPE_SIZE); } return; } if (fclose(f) != 0) { - warning(errno, "could not close '%s'", PROC_MAX_PIPE_SIZE); + warning(errno, "could not close '{}'", PROC_MAX_PIPE_SIZE); } } int new_size = fcntl(fd, F_SETPIPE_SZ, max_pipe_size); if (new_size == -1) { - warning(errno, "could not change pipe size of %d", fd); + warning(errno, "could not change pipe size of {}", fd); } - logmsg(LOG_DEBUG, "set pipe fd %d to size %d", fd, new_size); + logmsg(LOG_DEBUG, "set pipe fd {} to size {}", fd, new_size); } // Write all the data into the file descriptor. It is assumed that the file @@ -245,9 +245,9 @@ struct process_t { pid = execute(cmd, exec_args, stdio, false); if (pid < 0) { - error(errno, "failed to execute command #%ld", index); + error(errno, "failed to execute command #{}", index); } - logmsg(LOG_DEBUG, "started #%ld, pid %d", index, pid); + logmsg(LOG_DEBUG, "started #{}, pid {}", index, pid); // Do not leak these file descriptors, otherwise we cannot detect if the // process has closed stdout. close(stdin_fd); @@ -264,7 +264,7 @@ struct process_t { // (i.e. proxy -> process). void close_input_fd() { if (proxy_to_process != -1) { - logmsg(LOG_DEBUG, "closing fd: %d (proxy -> process) of %d", + logmsg(LOG_DEBUG, "closing fd: {} (proxy -> process) of {}", proxy_to_process, pid); close(proxy_to_process); } @@ -274,7 +274,7 @@ struct process_t { // (i.e. process -> proxy). void close_output_fd() { if (process_to_proxy != -1) { - logmsg(LOG_DEBUG, "closing fd: %d (process -> proxy) of %d", + logmsg(LOG_DEBUG, "closing fd: {} (process -> proxy) of {}", process_to_proxy, pid); close(process_to_proxy); } @@ -318,7 +318,7 @@ struct output_file_t { output_file = open(path.c_str(), O_CREAT | O_CLOEXEC | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (output_file == -1) { - error(errno, "failed to create proxy output file at %s", path.c_str()); + error(errno, "failed to create proxy output file at {}", path); } } @@ -362,7 +362,7 @@ struct output_file_t { time_millis, size, direction); // Check that snprintf didn't truncate the header. if (header_len >= static_cast(HEADER_SIZE)) { - error(0, "header size too small: %d > %ld", header_len, HEADER_SIZE); + error(0, "header size too small: {} > {}", header_len, HEADER_SIZE); } write_all(output_file, header, header_len); @@ -376,7 +376,7 @@ void usage() { Usage: %s [OPTION]... COMMAND1 [ARGS...] = COMMAND2 [ARGS...]\n\ Run two commands with stdin/stdout bi-directionally connected.\n\ \n", - progname); + progname.data()); printf("\ -o, --outprog=FILE write stdout from second program to FILE\n\ -M, --outmeta=FILE write metadata (runtime, exit_code, etc.) of first program to FILE\n\ @@ -456,22 +456,21 @@ struct state_t { break; case 'o': /* outprog option */ args.output_file = optarg; - logmsg(LOG_DEBUG, "writing interactions to '%s'", - args.output_file.c_str()); + logmsg(LOG_DEBUG, "writing interactions to '{}'", args.output_file); break; case 'M': /* outmeta option */ args.meta_file = optarg; - logmsg(LOG_DEBUG, "writing metadata to '%s'", args.meta_file.c_str()); + logmsg(LOG_DEBUG, "writing metadata to '{}'", args.meta_file); break; case 'h': args.show_help = 1; break; case ':': /* getopt error */ case '?': - error(0, "unknown option or missing argument `%c'", optopt); + error(0, "unknown option or missing argument `{:c}'", optopt); break; default: - error(0, "getopt returned character code `%c' ??", (char)opt); + error(0, "getopt returned character code `{:c}' ??", opt); } } @@ -534,7 +533,7 @@ struct state_t { if (args.verbose) { logmsg(LOG_DEBUG, "Processes:"); for (size_t i = 0; i < processes.size(); i++) { - logmsg(LOG_DEBUG, " #%ld: %s", i, processes[i].debug().c_str()); + logmsg(LOG_DEBUG, " #{}: {}", i, processes[i].debug()); } } } @@ -602,18 +601,18 @@ struct state_t { fd_t read_end = fds[0]; static fd_t write_end = -1; if (write_end != -1) { - error(0, "attempted to install signal handler for %d twice", signum); + error(0, "attempted to install signal handler for {} twice", signum); } write_end = fds[1]; - logmsg(LOG_DEBUG, "exit handler will send event using %d -> %d", write_end, + logmsg(LOG_DEBUG, "exit handler will send event using {} -> {}", write_end, read_end); signal(signum, [](int) { // TODO: Decide whether to keep some logging as the line below. We can't // use logmsg here since that will in turn call syslog which is not safe // to do in a signal handler (see also `man signal-safety`). - // logmsg(LOG_DEBUG, "caught signal %d", signum); + // logmsg(LOG_DEBUG, "caught signal {}", signum); // Notify the main loop that a child exited by sending a message via // the pipe. @@ -668,21 +667,21 @@ struct state_t { // Use two pipes for the given direction with the // proxy in between. tie(read_end, write_end) = make_pipe(); - logmsg(LOG_DEBUG, "setting up pipe #%ld (fd %d) -> proxy (fd %d)", i, + logmsg(LOG_DEBUG, "setting up pipe #{} (fd {}) -> proxy (fd {})", i, write_end, read_end); process.stdout_fd = write_end; process.process_to_proxy = read_end; set_non_blocking(process.process_to_proxy); tie(read_end, write_end) = make_pipe(); - logmsg(LOG_DEBUG, "setting up pipe proxy (fd %d) -> #%ld (fd %d)", + logmsg(LOG_DEBUG, "setting up pipe proxy (fd {}) -> #{} (fd {})", write_end, j, read_end); other.proxy_to_process = write_end; other.stdin_fd = read_end; } else { // No proxy: direct communication. tie(read_end, write_end) = make_pipe(); - logmsg(LOG_DEBUG, "setting up pipe #%ld (fd %d) -> #%ld (fd %d)", i, + logmsg(LOG_DEBUG, "setting up pipe #{} (fd {}) -> #{} (fd {})", i, write_end, j, read_end); process.stdout_fd = write_end; other.stdin_fd = read_end; @@ -698,12 +697,12 @@ struct state_t { } auto add_fd = [&](fd_t fd) { - logmsg(LOG_DEBUG, "epoll will listen for fd %d", fd); + logmsg(LOG_DEBUG, "epoll will listen for fd {}", fd); epoll_event ev{}; ev.data.fd = fd; ev.events = EPOLLIN; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev)) { - error(errno, "failed to add fd %d to epoll", fd); + error(errno, "failed to add fd {} to epoll", fd); } }; @@ -751,7 +750,7 @@ struct state_t { return false; } - logmsg(LOG_DEBUG, "child with pid %d exited", pid); + logmsg(LOG_DEBUG, "child with pid {} exited", pid); // Only set the first process if runguard didn't tell us about a TLE. if (first_process_exit_id == -1 && !child_indicated_timelimit) { @@ -777,7 +776,7 @@ struct state_t { } if (!found) { - error(0, "unknown child with pid %d exited", pid); + error(0, "unknown child with pid {} exited", pid); } return true; @@ -811,7 +810,7 @@ struct state_t { sprintf(eofbuf, "[%3d.%03ds/%ld]%c", time_sec, time_millis, 0L, direction); write_all(output_file.output_file, eofbuf, strlen(eofbuf)); - warning(0, "EOF from process #%ld", from.index); + warning(0, "EOF from process #{}", from.index); // The process closed stdout, we need to close the pipe's file // descriptors as well. to.close_input_fd(); @@ -823,7 +822,7 @@ struct state_t { if (errno == EAGAIN || errno == EWOULDBLOCK) { return; } - error(errno, "failed to read from pipe of #%ld", from.index); + error(errno, "failed to read from pipe of #{}", from.index); } // We've read nread bytes, write them to the other process' pipe. write_all(to.proxy_to_process, buffer, nread); @@ -873,8 +872,8 @@ struct state_t { // happening and the communication with the other process may be very // broken. if (main_process().has_exited_with_signal()) { - logmsg(LOG_WARNING, "the first process crashed! %s", - processes[0].exit_info_to_string().c_str()); + logmsg(LOG_WARNING, "the first process crashed! {}", + processes[0].exit_info_to_string()); } continue; } @@ -910,7 +909,7 @@ struct state_t { finish: logmsg(LOG_DEBUG, "all processes exited"); if (!args.output_file.empty()) { - logmsg(LOG_INFO, "total communication amount: %ld KiB", + logmsg(LOG_INFO, "total communication amount: {} KiB", total_bytes_transferred / 1024); } } @@ -925,7 +924,7 @@ struct state_t { ofstream meta(args.meta_file); if (meta.fail()) { - error(errno, "failed to open meta file at %s", args.meta_file.c_str()); + error(errno, "failed to open meta file at {}", args.meta_file); } meta << "exitcode: " << main_process().exit_code() << endl; meta << "bytes-transferred: " << total_bytes_transferred << endl; @@ -964,8 +963,8 @@ int main(int argc, char **argv) { if (state.args.verbose) { logmsg(LOG_DEBUG, "Exit statuses:"); for (const auto &proc : state.processes) { - logmsg(LOG_DEBUG, " #%ld: %s", proc.index, - proc.exit_info_to_string().c_str()); + logmsg(LOG_DEBUG, " #{}: {}", proc.index, + proc.exit_info_to_string()); } } @@ -977,6 +976,6 @@ int main(int argc, char **argv) { } // The first command exited with a signal. - error(0, "the first process crashed! %s", - main_process.exit_info_to_string().c_str()); + error(0, "the first process crashed! {}", + main_process.exit_info_to_string()); } diff --git a/lib/Makefile b/lib/Makefile index e608dbfba5..0dabc2cb27 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -8,7 +8,7 @@ OBJECTS = $(addsuffix $(OBJEXT),lib.error lib.misc) build: $(OBJECTS) -lib.error$(OBJEXT): lib.error.c lib.error.h +lib.error$(OBJEXT): lib.error.cc lib.error.hpp lib.misc$(OBJEXT): lib.misc.cc lib.misc.h clean-l: diff --git a/lib/lib.error.c b/lib/lib.error.c deleted file mode 100644 index 6ecee19198..0000000000 --- a/lib/lib.error.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Error handling and logging functions - * - * Part of the DOMjudge Programming Contest Jury System and licensed - * under the GNU GPL. See README and COPYING for details. - */ - -/* Implementation details: - * - * All argument-list based functions call their va_list (v..) counterparts - * to avoid doubled code. Furthermore, all functions use 'vlogmsg' to do the - * actual writing of the logmessage and all error/warning functions use - * 'vlogerror' to generate the error-logmessage from the input. vlogerror in - * turn calls errorstring to generate the actual error message; - */ - -#include "config.h" - -#include "lib.error.h" - -#include -#include -#include -#include -#include - -/* Define va_copy macro if not available (ANSI C99 only). - * memcpy() is fallback suggested by the autoconf manual, but doesn't - * work with g++ on AMD 64bit platform. - * FIXME: Replace by autoconf test? - */ -#ifndef va_copy -#ifdef __va_copy -#define va_copy(dest, src) __va_copy(dest,src) -#else -#define va_copy(dest, src) memcpy(&dest, &src, sizeof(va_list)) -#endif -#endif - -/* Use program name in syslogging if defined */ -#ifndef PROGRAM -#define PROGRAM NULL -#endif - -const int exit_failure = -1; - -/* Variables defining logmessages verbosity to stderr/logfile */ -int verbose = LOG_NOTICE; -int loglevel = LOG_DEBUG; - -/* Variables for tracking logging facilities */ -FILE *stdlog = NULL; -int syslog_open = 0; - -char *printf_escape(const char *str) -{ - char *escaped; - char c; - size_t str_pos, esc_pos; - - escaped = (char *)malloc(2*strlen(str)+1); - esc_pos = 0; - - for(str_pos=0; str_poslen ) error(0,"cannot write all of string"); - - return str; -} - -/* Argument-list wrapper function around vallocstr */ -char *allocstr(const char *mesg, ...) -{ - va_list ap; - char *str; - - va_start(ap,mesg); - str = vallocstr(mesg,ap); - va_end(ap); - - return str; -} diff --git a/lib/lib.error.cc b/lib/lib.error.cc new file mode 100644 index 0000000000..5912bd0664 --- /dev/null +++ b/lib/lib.error.cc @@ -0,0 +1,82 @@ +/* + * Error handling and logging functions + * + * Part of the DOMjudge Programming Contest Jury System and licensed + * under the GNU GPL. See README and COPYING for details. + */ + +#include "config.h" + +#include "lib.error.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Use program name in syslogging if defined */ +#ifndef PROGRAM +#define PROGRAM NULL +#endif + +const int exit_failure = -1; + +/* Variables defining logmessages verbosity to stderr/logfile */ +int verbose = LOG_NOTICE; +int loglevel = LOG_DEBUG; + +/* Variables for tracking logging facilities */ +static std::ofstream stdlog; +int syslog_open = 0; + +/* Core logging implementation that accepts a fully formatted string */ +void logmsg_str(int msglevel, const std::string& mesg) +{ + std::string buffer; + char *str, *endptr; + int syslog_fac; + + /* Try to open logfile if it is defined */ +#ifdef LOGFILE + if (!stdlog.is_open()) { + stdlog.open(LOGFILE, std::ios::out | std::ios::app); + } +#endif + + /* Try to open syslog if it is defined */ + if ( ! syslog_open && (str=getenv("DJ_SYSLOG"))!=NULL ) { + syslog_fac = strtol(str,&endptr,10); + if ( *endptr==0 ) { + openlog(PROGRAM, LOG_NDELAY | LOG_PID, syslog_fac); + syslog_open = 1; + } + } + + auto now = std::chrono::system_clock::now(); + auto millis = std::chrono::duration_cast(now.time_since_epoch()) % 1000; + std::string timestring = std::format("{:%b %d %H:%M:%S}.{:03d}", + std::chrono::floor(now), millis.count()); + + // Construct the format string: "[time] progname[pid]: message\n" + buffer += "[" + timestring + "] "; + buffer += progname; + buffer += "[" + std::to_string(getpid()) + "]: "; + buffer += mesg; + + if ( msglevel<=verbose ) { + std::cerr << buffer << std::endl; + } + if ( msglevel<=loglevel && stdlog.is_open() ) { + stdlog << buffer << std::endl; + } + + if ( msglevel<=loglevel && syslog_open ) { + syslog(msglevel, "%s", mesg.c_str()); + } +} diff --git a/lib/lib.error.h b/lib/lib.error.h deleted file mode 100644 index ffc2afc466..0000000000 --- a/lib/lib.error.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Error handling and logging functions - */ - -#ifndef LIB_ERROR_H -#define LIB_ERROR_H - -#include -#include -#include -#ifdef HAVE_SYSLOG_H -#include -#else -#error "Syslog header file not available." -#endif - -#define ERRSTR "error" -#define ERRMATCH ERRSTR": " - -#define WARNSTR "warning" -#define WARNMATCH WARNSTR": " - -#ifdef __cplusplus -extern "C" { -#endif - -extern const int exit_failure; - -/* Import from the main program for logging purposes */ -extern const char *progname; - -/* Variables defining logmessages verbosity to stderr/logfile */ -extern int verbose; -extern int loglevel; -extern FILE *stdlog; - -void logmsg(int, const char *, ...) __attribute__((format (printf, 2, 3))); -void vlogmsg(int, const char *, va_list); -/* Logging functions (vlogmsg uses va_list instead of argument list): - * Logs a message to stderr and/or logfile, including date and program name, - * depending on the loglevel threshold values. - * - * Arguments: - * int loglevel syslog loglevel of this log-message - * char *mesg message, may include printf output format characters '%' - * ... or va_list optional arguments for format characters - */ - -char *errorstring(const char *, int, const char *); -/* Error string generating function: - * Returns a pointer to a dynamically allocated string containing the error - * message. - * - * Arguments: - * char *type string of type of message: ERRSTR, WARNSTR or custom. - * int errnum 'errno' value to use for error string output, set 0 to skip - * char *mesg optional accompanying message to display, set NULL to skip - * - * Returns a char pointer to the allocated string. - */ - -void logerror (int, const char *, ...) __attribute__((format (printf, 2, 3))); -void error (int, const char *, ...) __attribute__((format (printf, 2, 3), noreturn)); -void warning (int, const char *, ...) __attribute__((format (printf, 2, 3))); -void vlogerror(int, const char *, va_list) __attribute__((nonnull (2))); -void verror (int, const char *, va_list) __attribute__((nonnull (2), noreturn)); -void vwarning (int, const char *, va_list) __attribute__((nonnull (2))); -/* Error and warning functions (v.. uses va_list instead of argument list): - * Logs an error message including error string from 'errno'. - * logerror only logs the error message (non-fatal error) - * error log the message and exits with exit_failure - * warning same as 'logerror', but prints 'warning' instead of 'error' - * - * Arguments: - * int errnum 'errno' value to use for error string output, set 0 to skip - * char *mesg message, may include printf output format characters '%' - * ... or va_list optional arguments for format characters - */ - -char *allocstr(const char *, ...) __attribute__((format (printf, 1, 2))); -char *vallocstr(const char *, va_list); -/* Create a c-string by allocating memory for it and writing to it, - * using printf type format characters. - * - * Arguments: - * char *mesg message, may include printf output format characters '%' - * ... or va_list optional arguments for format characters - * - * Returns a pointer to the allocated string - */ - -#ifdef __cplusplus -} -#endif - -#endif /* LIB_ERROR_H */ diff --git a/lib/lib.error.hpp b/lib/lib.error.hpp new file mode 100644 index 0000000000..72dc48421b --- /dev/null +++ b/lib/lib.error.hpp @@ -0,0 +1,91 @@ +#ifndef LIB_ERROR_HPP +#define LIB_ERROR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYSLOG_H +#include +#else +#error "Syslog header file not available." +#endif + +#define ERRSTR "error" +#define ERRMATCH ERRSTR": " + +#define WARNSTR "warning" +#define WARNMATCH WARNSTR": " + +extern const int exit_failure; + +/* Import from the main program for logging purposes */ +extern std::string_view progname; + +/* Variables defining logmessages verbosity to stderr/logfile */ +extern int verbose; +extern int loglevel; + +// Internal backend function (exposed for templates) +void logmsg_str(int msglevel, const std::string& mesg); + +/** + * These functions accept std::format style strings and arguments. + * Example: logmsg(LOG_INFO, "Value: {}", 42); + */ + +template +void logmsg(int level, std::format_string fmt, Args&&... args) { + try { + std::string msg = std::format(fmt, std::forward(args)...); + logmsg_str(level, msg); + } catch (const std::format_error& e) { + logmsg_str(LOG_ERR, std::string("Format error in logmsg: ") + e.what()); + } +} + +// Helper for error/warning/logerror +template +void log_helper(int level, const char* prefix, int errnum, std::format_string fmt, Args&&... args) { + try { + std::string user_msg = std::format(fmt, std::forward(args)...); + std::string err_descr; + + if (errnum != 0) { + err_descr = std::strerror(errnum); + } + + std::string buffer; + if (prefix) buffer += prefix; + buffer += user_msg; + if (!user_msg.empty() && !err_descr.empty()) buffer += ": "; + if (!err_descr.empty()) buffer += err_descr; + + logmsg_str(level, buffer); + } catch (const std::exception& e) { + logmsg_str(LOG_ERR, std::string("Format error: ") + e.what()); + } +} + +template +void error(int errnum, std::format_string fmt, Args&&... args) { + log_helper(LOG_ERR, "error: ", errnum, fmt, std::forward(args)...); + std::exit(exit_failure); +} + +template +void warning(int errnum, std::format_string fmt, Args&&... args) { + log_helper(LOG_WARNING, "warning: ", errnum, fmt, std::forward(args)...); +} + +template +void logerror(int errnum, std::format_string fmt, Args&&... args) { + log_helper(LOG_ERR, "error: ", errnum, fmt, std::forward(args)...); +} + +#endif // LIB_ERROR_HPP diff --git a/lib/lib.misc.cc b/lib/lib.misc.cc index 9a498001df..07c42872ba 100644 --- a/lib/lib.misc.cc +++ b/lib/lib.misc.cc @@ -18,7 +18,7 @@ #include #include "lib.misc.h" -#include "lib.error.h" +#include "lib.error.hpp" /* Array indices for input/output file descriptors as used by pipe() */ constexpr int PIPE_IN = 1; diff --git a/misc-tests/test_compare.sh b/misc-tests/test_compare.sh index 297685e96d..b4200982b3 100755 --- a/misc-tests/test_compare.sh +++ b/misc-tests/test_compare.sh @@ -88,7 +88,7 @@ test_invalid_float_extra_chars() { any_test_failed=0 -g++ -g -O2 -Wall -std=c++17 ../sql/files/defaultdata/compare/compare.cc -o $COMPARE_EXECUTABLE +g++ -g -O2 -Wall -std=c++20 ../sql/files/defaultdata/compare/compare.cc -o $COMPARE_EXECUTABLE for func in $(compgen -o nosort -A function test_); do fail=0