@@ -293,6 +293,41 @@ function read_judgehostlog(int $numLines = 20) : string
293293    return  trim (ob_get_clean ());
294294}
295295
296+ define ('DONT_CARE ' , new  class  {});
297+ /** 
298+  * Execute a shell command given an array of strings forming the command. 
299+  * The command and all its arguments are automatically escaped. 
300+  * 
301+  * @param array $command_parts         The command and its arguments (e.g., ['ls', '-l', '/tmp/']). 
302+  * @param mixed $retval                The (integer) variable to store the command's exit code. 
303+  * @param bool  $log_nonzero_exitcode  Whether non-zero exit codes should be logged. 
304+  * 
305+  * @return bool Returns true on success (exit code 0), false otherwise. 
306+  */ 
307+ function  run_command_safe (array  $ command_parts$ retvalDONT_CARE , $ log_nonzero_exitcodetrue ): bool 
308+ {
309+     if  (empty ($ command_parts
310+         logmsg (LOG_WARNING , "Need at least the command that should be called. " );
311+         $ retval1 ;
312+         return  false ;
313+     }
314+ 
315+     $ commandimplode ('  ' , array_map ('dj_escapeshellarg ' , $ command_parts
316+ 
317+     logmsg (LOG_DEBUG , "Executing command:  $ command );
318+     system ($ command$ retval_local
319+     if  ($ retvalDONT_CARE ) $ retval$ retval_local
320+ 
321+     if  ($ retval_local0 ) {
322+         if  ($ log_nonzero_exitcode
323+             logmsg (LOG_WARNING , "Command failed with exit code  $ retval_local:  $ command );
324+         }
325+         return  false ;
326+     }
327+ 
328+     return  true ;
329+ }
330+ 
296331// Fetches a new executable from database if not cached already, and runs build script to compile executable. 
297332// Returns an array with 
298333// - absolute path to run script 
@@ -354,12 +389,10 @@ function fetch_executable_internal(
354389    $ execrunjurypath$ execbuilddir'/runjury ' ;
355390    if  (!is_dir ($ execdirfile_exists ($ execdeploypath
356391        ($ combined_run_comparefile_get_contents (LIBJUDGEDIR  . '/run-interactive.sh ' )!==file_get_contents ($ execrunpath
357-         system ('rm -rf  '  . dj_escapeshellarg ($ execdir'  '  . dj_escapeshellarg ($ execbuilddir$ retval
358-         if  ($ retval0 ) {
392+         if  (!run_command_safe (['rm ' , '-rf ' , $ execdir$ execbuilddir
359393            disable ('judgehost ' , 'hostname ' , $ myhost"Deleting ' $ execdir' or ' $ execbuilddir' was unsuccessful. " );
360394        }
361-         system ('mkdir -p  '  . dj_escapeshellarg ($ execbuilddir$ retval
362-         if  ($ retval0 ) {
395+         if  (!run_command_safe (['mkdir ' , '-p ' , $ execbuilddir
363396            disable ('judgehost ' , 'hostname ' , $ myhost"Could not create directory ' $ execbuilddir' " );
364397        }
365398
@@ -469,8 +502,7 @@ function fetch_executable_internal(
469502            putenv ('SCRIPTMEMLIMIT= '   . djconfig_get_value ('script_memory_limit ' ));
470503            putenv ('SCRIPTFILELIMIT= '  . djconfig_get_value ('script_filesize_limit ' ));
471504
472-             system (LIBJUDGEDIR  . '/build_executable.sh  '  . dj_escapeshellarg ($ execdir$ retval
473-             if  ($ retval0 ) {
505+             if  (!run_command_safe ([LIBJUDGEDIR  . '/build_executable.sh ' , $ execdir
474506                return  [null , "Failed to build executable in  $ execdir. " , "$ execdir/build.log " ];
475507            }
476508            chmod ($ execrunpath0755 );
@@ -531,7 +563,11 @@ function fetch_executable_internal(
531563    exit (1 );
532564}
533565
534- $ myhosttrim (`hostname | cut -d . -f 1 `);
566+ $ hostnamegethostname ();
567+ if  ($ hostnamefalse ) {
568+     error ("Could not determine hostname. " );
569+ }
570+ $ myhostexplode ('. ' , $ hostname0 ];
535571if  (isset ($ options'daemonid ' ])) {
536572    if  (preg_match ('/^\d+$/ ' , $ options'daemonid ' ])) {
537573        $ myhost$ myhost"- "  . $ options'daemonid ' ];
@@ -662,9 +698,8 @@ function fetch_executable_internal(
662698
663699// Check basic prerequisites for chroot at judgehost startup 
664700logmsg (LOG_INFO , "🔏 Executing chroot script: ' " .CHROOT_SCRIPT ." check' " );
665- system (LIBJUDGEDIR .'/ ' .CHROOT_SCRIPT .' check ' , $ retval
666- if  ($ retval0 ) {
667-     error ("chroot validation check exited with exitcode  $ retval );
701+ if  (!run_command_safe ([LIBJUDGEDIR .'/ ' .CHROOT_SCRIPT , 'check ' ])) {
702+     error ("chroot validation check failed " );
668703}
669704
670705foreach  ($ endpointsas  $ id$ endpoint
@@ -756,8 +791,7 @@ function fetch_executable_internal(
756791                foreach  ($ candidateDirsas  $ d
757792                    $ cnt
758793                    logmsg (LOG_INFO , "  - deleting  $ d );
759-                     system ('rm -rf  '  . dj_escapeshellarg ($ d$ retval
760-                     if  ($ retval0 ) {
794+                     if  (!run_command_safe (['rm ' , '-rf ' , $ d
761795                        logmsg (LOG_WARNING , "Deleting ' $ d' was unsuccessful. " );
762796                    }
763797                    $ afterdisk_free_space (JUDGEDIR );
@@ -881,10 +915,7 @@ function fetch_executable_internal(
881915                    $ judgeTask'judgetaskid ' ]
882916                );
883917
884-                 $ debug_cmdimplode ('  ' , array_map ('dj_escapeshellarg ' ,
885-                     [$ runpath$ workdir$ tmpfile
886-                 system ($ debug_cmd$ retval
887-                 if  ($ retval0 ) {
918+                 if  (!run_command_safe ([$ runpath$ workdir$ tmpfile
888919                    disable ('run_script ' , 'run_script_id ' , $ judgeTask'run_script_id ' ], "Running ' $ runpath' failed. " );
889920                }
890921
@@ -953,8 +984,7 @@ function fetch_executable_internal(
953984    }
954985
955986
956-     system ('mkdir -p  '  . dj_escapeshellarg ("$ workdir/compile " ), $ retval
957-     if  ($ retval0 ) {
987+     if  (!run_command_safe (['mkdir ' , '-p ' , "$ workdir/compile " ])) {
958988        error ("Could not create ' $ workdir/compile' " );
959989    }
960990
@@ -967,8 +997,7 @@ function fetch_executable_internal(
967997    if  ($ lastWorkdir$ workdir
968998        // create chroot environment 
969999        logmsg (LOG_INFO , "  🔒 Executing chroot script: ' " .CHROOT_SCRIPT ." start' " );
970-         system (LIBJUDGEDIR .'/ ' .CHROOT_SCRIPT .' start ' , $ retval
971-         if  ($ retval0 ) {
1000+         if  (!run_command_safe ([LIBJUDGEDIR .'/ ' .CHROOT_SCRIPT , 'start ' ], $ retval
9721001            logmsg (LOG_ERR , "chroot script exited with exitcode  $ retval );
9731002            disable ('judgehost ' , 'hostname ' , $ myhost"chroot script exited with exitcode  $ retval on  $ myhost );
9741003            continue ;
@@ -1030,8 +1059,7 @@ function registerJudgehost(string $myhost): void
10301059
10311060    // Create directory where to test submissions 
10321061    $ workdirpathJUDGEDIR  . "/ $ myhost/endpoint- $ endpointID ;
1033-     system ('mkdir -p  '  . dj_escapeshellarg ("$ workdirpath/testcase " ), $ retval
1034-     if  ($ retval0 ) {
1062+     if  (!run_command_safe (['mkdir ' , '-p ' , "$ workdirpath/testcase " ])) {
10351063        error ("Could not create  $ workdirpath );
10361064    }
10371065    chmod ("$ workdirpath/testcase " , 0700 );
@@ -1109,8 +1137,7 @@ function cleanup_judging(string $workdir) : void
11091137
11101138    // destroy chroot environment 
11111139    logmsg (LOG_INFO , "  🔓 Executing chroot script: ' " .CHROOT_SCRIPT ." stop' " );
1112-     system (LIBJUDGEDIR .'/ ' .CHROOT_SCRIPT .' stop ' , $ retval
1113-     if  ($ retval0 ) {
1140+     if  (!run_command_safe ([LIBJUDGEDIR .'/ ' .CHROOT_SCRIPT , 'stop ' ], $ retval
11141141        logmsg (LOG_ERR , "chroot script exited with exitcode  $ retval );
11151142        disable ('judgehost ' , 'hostname ' , $ myhost"chroot script exited with exitcode  $ retval on  $ myhost );
11161143        // Just continue here: even though we might continue a current 
@@ -1120,9 +1147,8 @@ function cleanup_judging(string $workdir) : void
11201147    }
11211148
11221149    // Evict all contents of the workdir from the kernel fs cache 
1123-     system (LIBJUDGEDIR  . '/evict  '  . dj_escapeshellarg ($ workdir$ retval
1124-     if  ($ retval0 ) {
1125-         warning ("evict script exited with exitcode  $ retval );
1150+     if  (!run_command_safe ([LIBJUDGEDIR  . '/evict ' , $ workdir
1151+         warning ("evict script failed, continuing gracefully " );
11261152    }
11271153}
11281154
@@ -1131,7 +1157,7 @@ function compile(
11311157    string  $ workdir
11321158    string  $ workdirpath
11331159    array  $ compile_config
1134-     string  $ cpuset_opt 
1160+     ? string  $ daemonid 
11351161    int  $ output_storage_limit
11361162): bool  {
11371163    global  $ myhost$ EXITCODES 
@@ -1151,26 +1177,24 @@ function compile(
11511177        $ args'hostname= '  . urlencode ($ myhost
11521178        foreach  (['compiler ' , 'runner ' ] as  $ type
11531179            if  (isset ($ version_verification$ type'_version_command ' ])) {
1180+                 if  (file_exists ($ version_output_file
1181+                     unlink ($ version_output_file
1182+                 }
1183+ 
11541184                $ vcscript_content$ version_verification$ type'_version_command ' ];
11551185                $ vcscripttempnam (TMPDIR , 'version_check- ' );
11561186                file_put_contents ($ vcscript$ vcscript_content
11571187                chmod ($ vcscript0755 );
1158-                 $ version_cmdLIBJUDGEDIR  . "/version_check.sh  "  .
1159-                     implode ('  ' , array_map ('dj_escapeshellarg ' , [
1160-                         $ vcscript
1161-                         $ workdir
1162-                     ]));
11631188
1164-                 if  (file_exists ($ version_output_file
1165-                     unlink ($ version_output_file
1166-                 }
1167-                 system ($ version_cmd$ retval
1189+                 run_command_safe ([LIBJUDGEDIR  . "/version_check.sh " , $ vcscript$ workdir$ retval
1190+ 
11681191                $ versions$ typetrim (file_get_contents ($ version_output_file
11691192                if  ($ retval0 ) {
11701193                    $ versions$ type
11711194                        "Getting  $ type version failed with exit code  $ retval 
11721195                        . $ versions$ type
11731196                }
1197+ 
11741198                unlink ($ vcscript
11751199            }
11761200            if  (isset ($ versions$ type
@@ -1248,16 +1272,14 @@ function compile(
12481272    }
12491273
12501274    // Compile the program. 
1251-     $ compile_cmdLIBJUDGEDIR  . "/compile.sh  $ cpuset_opt  "  .
1252-         implode ('  ' , array_map ('dj_escapeshellarg ' , array_merge ([
1253-             $ execrunpath
1254-             $ workdir
1255-         ], $ files
1256-     logmsg (LOG_DEBUG , "Compile command:  " .$ compile_cmd
1257-     system ($ compile_cmd$ retval
1258-     if  ($ retval0 ) {
1259-         warning ("compile script exited with exitcode  $ retval );
1275+     $ compile_command_partsLIBJUDGEDIR  . '/compile.sh ' ];
1276+     if  (isset ($ daemonid
1277+         $ compile_command_parts'-n ' ;
1278+         $ compile_command_parts$ daemonid
12601279    }
1280+     array_push ($ compile_command_parts$ execrunpath$ workdir$ files
1281+     // Note that the $retval is handled further down after reading/writing metadata. 
1282+     run_command_safe ($ compile_command_parts$ retvalfalse );
12611283
12621284    $ compile_output'' ;
12631285    if  (is_readable ($ workdir'/compile.out ' )) {
@@ -1358,7 +1380,7 @@ function judge(array $judgeTask): bool
13581380    }
13591381
13601382    $ workdirjudging_directory ($ workdirpath$ judgeTask
1361-     $ compile_successcompile ($ judgeTask$ workdir$ workdirpath$ compile_config$ cpuset_opt $ output_storage_limit
1383+     $ compile_successcompile ($ judgeTask$ workdir$ workdirpath$ compile_config$ options [ ' daemonid ' ] ??  null , $ output_storage_limit
13621384    if  (!$ compile_success
13631385        return  false ;
13641386    }
@@ -1460,37 +1482,39 @@ function judge(array $judgeTask): bool
14601482        // after the first (note that $passCnt starts at 1). 
14611483        if  ($ passCnt1 ) {
14621484            $ prevPassdir$ testcasedir'/ '  . ($ passCnt1 ) . '/feedback ' ;
1463-             system ( 'cp -R   '   .  dj_escapeshellarg ( $ prevPassdir) .  '   '  .  dj_escapeshellarg ( $ passdir'/ ' ) );
1464-             system ( 'rm   '   .  dj_escapeshellarg ( $ passdir'/feedback/nextpass.in ' ) );
1485+             run_command_safe ([ 'cp '  ,  ' -R ' ,  $ prevPassdir,  $ passdir'/ ' ] );
1486+             run_command_safe ([ 'rm '  ,  $ passdir'/feedback/nextpass.in ' ] );
14651487        }
14661488
14671489        // Copy program with all possible additional files to testcase 
14681490        // dir. Use hardlinks to preserve space with big executables. 
14691491        $ programdir$ passdir'/execdir ' ;
1470-         system ('mkdir -p  '  . dj_escapeshellarg ($ programdir$ retval
1471-         if  ($ retval0 ) {
1492+         if  (!run_command_safe (['mkdir ' , '-p ' , $ programdir
14721493            error ("Could not create directory ' $ programdir' " );
14731494        }
14741495
14751496        foreach  (glob ("$ workdir/compile/* " ) as  $ compile_file
1476-             system ('cp -PRl  '  . dj_escapeshellarg ($ compile_file'  '  . dj_escapeshellarg ($ programdir$ retval
1477-             if  ($ retval0 ) {
1497+             if  (!run_command_safe (['cp ' , '-PRl ' , $ compile_file$ programdir
14781498                error ("Could not copy program to ' $ programdir' " );
14791499            }
14801500        }
14811501
14821502        $ timelimit_strimplode (': ' , $ timelimit'cpu ' ]) . ', '  . implode (': ' , $ timelimit'wall ' ]);
1483-         $ test_run_cmdLIBJUDGEDIR  . "/testcase_run.sh  $ cpuset_opt  "  .
1484-             implode ('  ' , array_map ('dj_escapeshellarg ' , [
1485-                 $ input
1486-                 $ output
1487-                 $ timelimit_str
1488-                 $ passdir
1489-                 $ run_runpath
1490-                 $ compare_runpath
1491-                 $ compare_config'compare_args ' ]
1492-             ]));
1493-         system ($ test_run_cmd$ retval
1503+         $ run_command_partsLIBJUDGEDIR  . '/testcase_run.sh ' ];
1504+         if  (isset ($ options'daemonid ' ])) {
1505+             $ run_command_parts'-n ' ;
1506+             $ run_command_parts$ options'daemonid ' ];
1507+         }
1508+         array_push ($ run_command_parts
1509+             $ input
1510+             $ output
1511+             $ timelimit_str
1512+             $ passdir
1513+             $ run_runpath
1514+             $ compare_runpath
1515+             $ compare_config'compare_args ' ]
1516+         );
1517+         run_command_safe ($ run_command_parts$ retvalfalse );
14941518
14951519        // What does the exitcode mean? 
14961520if  (!isset ($ EXITCODES $ retval
0 commit comments