/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ /* * ------------------------------------------------------- * This routine starts a new child process and passes the * job parameters to it. * ------------------------------------------------------- */ #include "mdef.h" #include #include #include #include "gtm_fcntl.h" #include "gtm_stdlib.h" #include "gtm_unistd.h" #include "gtm_stdio.h" #include "gtm_stat.h" #include "gtm_string.h" #if defined(SYS_ERRLIST_INCLUDE) && !defined(__CYGWIN__) #include SYS_ERRLIST_INCLUDE #endif #include "job.h" #include "error.h" #include #include "io.h" #include "iosp.h" #include "eintr_wrappers.h" #include "compiler.h" #include "job_addr.h" #include "util.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gtmio.h" #define MAX_JOB_LEN 8192 /* Arbitrary length maximum used for checking job arguments and parameters */ #define MAX_PATH 128 /* Maximum file path length */ #define MAX_LAB_LEN 32 /* Maximum Label string length */ #define MAX_RTN_LEN 32 /* Maximum Routine string length */ #define TEMP_BUFF_SIZE 1024 #define PARM_STRING_SIZE 9 #define MAX_NUM_LEN 10 /* Maximum length number will be when converted to string */ #define MAX_JOB_QUALS 12 /* Maximum environ variables set for job qualifiers */ #define MUMPS_EXE_STR "/mumps" #define MUMPS_DIRECT_STR "-direct" #define GTMJ_FMT "gtmj%03d=" #define PARM_STR "gtmj000=" static int joberr = joberr_gen; static boolean_t job_launched = FALSE; GBLREF bool jobpid; /* job's output files should have the pid appended to them. */ GBLREF volatile boolean_t ojtimeout; GBLREF boolean_t job_try_again; GBLREF uint4 process_id; GBLREF boolean_t gtm_pipe_child; #ifndef SYS_ERRLIST_INCLUDE /* currently either stdio.h or errno.h both of which are included above */ /* needed by TIMEOUT_ERROR in jobsp.h */ #if !defined(__sun) && !defined(___MVS__) GBLREF int sys_nerr; #endif #endif #ifdef __osf__ /* environ is from the O/S which only uses 64-bit pointers on OSF/1. */ #pragma pointer_size (save) #pragma pointer_size (long) #endif GBLREF char **environ; #ifdef __osf__ #pragma pointer_size (restore) #endif error_def(ERR_JOBFAIL); error_def(ERR_JOBPARTOOLONG); error_def(ERR_LOGTOOLONG); error_def(ERR_TEXT); #define KILL_N_REAP(PROCESS_ID, SIGNAL, RET_VAL) \ { \ if (-1 != (RET_VAL = kill(PROCESS_ID, SIGNAL))) \ { \ /* reap the just killed child, so there wont be any zombies */ \ WAITPID(PROCESS_ID, &wait_status, 0, done_pid); \ assert(done_pid == PROCESS_ID); \ } \ } /* Note that this module uses _exit instead of exit to avoid running the inherited exit handlers which this mid-level process does not want to run */ /* if we get an error assembling the child environment, we don't want a rogue child running loose */ void job_term_handler(int sig); static CONDITION_HANDLER(bad_child) { PRN_ERROR; _exit(joberr); } #define FAILED_TO_LAUNCH 1 /* This is to close the window of racing condition where the timeout occurs and actually * by that time, the middle process had already successfully forked the job */ void job_term_handler(int sig){ if (job_launched) _exit(0); else _exit(FAILED_TO_LAUNCH); } /* * --------------------------------------------------------------------------------------------------------------------- * The current process (P) FORKs a middle child process (M) that tests various job parameters. It then forks off the * actual Job (J) and exits, culminating the parent's (P) wait. The Job process (J) sets up its env and exexs mumps. * * Arguments * First argument is a pointer to the structure holding Job parameter values. * Second argument is the number of parameters being passed. * The third boolean argument indicates to the caller if the return from this function was due to an exit from the * middle process or due to reasons other than that. It is set to true for the latter case of return. * Fourth argument is the pair of file descriptors [opened by pipe] for the child process (M) to write PID * of the jobbed off process (J). * * Return: * Exit status of child (that the parent gets by WAITing) in case the return was after an exit from the middle process. * errno in other cases with the third argument set to TRUE and returned by pointer. * TIMEOUT_ERROR in case a timeout occured. * Return zero indicates success. * --------------------------------------------------------------------------------------------------------------------- */ int ojstartchild (job_params_type *jparms, int argcnt, boolean_t *non_exit_return, int pipe_fds[]) { char cbuff[TEMP_BUFF_SIZE], pbuff[TEMP_BUFF_SIZE]; char tbuff[MAX_JOB_LEN], tbuff2[MAX_JOB_LEN]; char *pgbldir_str; char *transfer_addr; int4 index, environ_count, string_len, temp; int wait_status, save_errno, kill_ret; int rc; bool status; pid_t par_pid, child_pid, done_pid; job_parm *jp; rhdtyp *base_addr; struct sigaction act, old_act; int pipe_status, env_len; #ifdef __osf__ /* These must be O/S-compatible 64-bit pointers for OSF/1. */ #pragma pointer_size (save) #pragma pointer_size (long) #endif char *c1, *c2, **c3; char *argv[3]; char **env_ary, **env_ind; char **new_env_cur, **new_env_top, **old_env_cur, **old_env_top, *env_end; #ifdef __osf__ #pragma pointer_size (restore) #endif job_launched = FALSE; par_pid = process_id; if (-1 == (child_pid = fork())) /* BYPASSOK: we die after creating a child, no FORK_CLEAN needed */ { if (EAGAIN == errno || ENOMEM == errno) job_try_again = TRUE; *non_exit_return = TRUE; return (errno); } if (child_pid == 0) { /* This is a child process (middle process, M) */ /* Test out various parameters and setup everything possible for the actual Job (J), so it(J) can * start off without much hitch. If any error occurs during this, exit with appropriate status so * the waiting parent can diagnose. */ /* set to TRUE so any child process associated with a pipe device will know it is not the parent in iorm_close() */ gtm_pipe_child = TRUE; joberr = joberr_gen; ESTABLISH_RET(bad_child, 0); sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = job_term_handler; sigaction(SIGTERM, &act, &old_act); if (!jobpid) /* if the Job pid need not be appended to the std-in/out/err file names */ { joberr = joberr_io; /* attempt to open output files */ /* this also redirects stdin/out/err, so any error messages by this process during * the creation of the Job will get redirected */ if (!(status = ojchildioset(jparms))) { if (job_try_again) joberr += joberr_tryagain; rts_error(VARLSTCNT(6) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Failed to set STDIN/OUT/ERR for the job")); } } /* else, all the errors during the creation of Job go to the STDOUT/ERR of the parent process */ joberr = joberr_cd; /* pass current directory to child */ if (jparms->directory.len != 0) { /* If directory is specified, change it */ if (jparms->directory.len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); strncpy(pbuff, jparms->directory.addr, jparms->directory.len); *(pbuff + jparms->directory.len) = '\0'; if (CHDIR(pbuff) != 0) { if (ETIMEDOUT == errno) /* atleast on AIX */ joberr += joberr_tryagain; rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Error changing directory for the Job."),errno); } } joberr = joberr_rtn; job_addr(&jparms->routine, &jparms->label, jparms->offset, (char **)&base_addr, &transfer_addr); joberr = joberr_syscall; if (-1 == setsid()) rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Error setting session id for the Job."), errno); joberr = joberr_frk; /* clone self and exit */ if (0 != (child_pid = fork())) /* BYPASSOK: we exec immediately, no FORK_CLEAN needed */ { /* This is still the middle process. */ if (0 > child_pid) { if (EAGAIN == errno || ENOMEM == errno) joberr += joberr_tryagain; rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Error forking the Job."), errno); } else { job_launched = TRUE; assert(SIZEOF(pid_t) == SIZEOF(child_pid)); /* write child_pid into pipe to be read by parent process(P) for $ZJOB */ DOWRITERC(pipe_fds[1], &child_pid, SIZEOF(child_pid), pipe_status); if (0 != pipe_status) { rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Error writing to pipe"), errno); } _exit(0); } } /* This is now the grandchild process (actual Job process) -- an orphan as soon as the exit(0) above occurs. */ /* set up the environment and exec */ sigaction(SIGTERM, &old_act, 0); /* restore the SIGTERM handler */ joberr = joberr_io; /* Run down any open flat files to reclaim their file descriptors */ io_rundown(RUNDOWN_EXCEPT_STD); /* release the pipe opened by grand parent (P) */ CLOSEFILE_RESET(pipe_fds[0], pipe_status); /* resets "pipe_fds[0]" to FD_INVALID */ CLOSEFILE_RESET(pipe_fds[1], pipe_status); /* resets "pipe_fds[1]" to FD_INVALID */ /* do common cleanup in child */ ojchildioclean(); /* Count the number of environment variables. */ for (environ_count = 0, c3 = environ, c2 = *c3; c2; c3++, c2 = *c3) environ_count++; #ifdef __osf__ /* Since we're creating an array of pointers for the O/S, make sure SIZEOF(char *) is correct for 64-bit pointers for OSF/1. */ #pragma pointer_size (save) #pragma pointer_size (long) #endif /* the environment array passed to the grandchild is constructed by prefixing the job related environment * variables ahead of the current environment (pointed to by the "environ" variable) * * e.g. if the current environment has only two environment variables env1=one and env2=two, * and the job command is as follows * job ^x(1,2):(output="x.mjo":error="x.mje") * * then the environment array passed is as follows * gtmj0= // parent pid * gtmgbldir=mumps.gld // current global directory * gtmjb= // startup parameter to job command * gtmj3=/dev/null // input file parameter to job command * gtmj4=x.mjo // output file parameter to job command * gtmj5=x.mje // error file parameter to job command * gtmj7=x // routine name to job off * gtmj8= // label name to job off * gtmj9=0 // offset to job off * gtmja= // base priority; * gtmj000=1 // parameter 1 to routine ^x * gtmj001=2 // parameter 2 to routine ^x * gtmjcnt=2 // number of parameters to routine ^x * env1=one // old environment * env2=two // old environment * * those parameters that are NULL or 0 are not passed. * each line above is an entry in the environment array. */ env_ind = env_ary = (char **)malloc((environ_count + MAX_JOB_QUALS + argcnt + 1)*SIZEOF(char *)); #ifdef __osf__ #pragma pointer_size (restore) #endif string_len = STRLEN("%s=%d") + STRLEN(CHILD_FLAG_ENV) + MAX_NUM_LEN - 4; if (string_len > MAX_JOB_LEN) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_NUM(c1, CHILD_FLAG_ENV, par_pid, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif /* Pass all information about the job via shell's environment. * The grandchild will get those variables to obtain the info about the job. */ /* pass global directory to child */ if (jparms->gbldir.len != 0) { if (jparms->gbldir.len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); strncpy(pbuff, jparms->gbldir.addr, jparms->gbldir.len); *(pbuff + jparms->gbldir.len) = '\0'; string_len = STRLEN("%s=%s") + STRLEN(GBLDIR_ENV) + STRLEN(pbuff) - 4; if (string_len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_STR(c1, GBLDIR_ENV, pbuff, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif } /* pass startup program to child */ if (jparms->startup.len != 0) { if (jparms->startup.len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); strncpy(pbuff, jparms->startup.addr, jparms->startup.len); *(pbuff + jparms->startup.len) = '\0'; string_len = STRLEN("%s=%s") + STRLEN(STARTUP_ENV) + STRLEN(pbuff) - 4; if (string_len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_STR(c1, STARTUP_ENV, pbuff, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif } /* pass input file to child */ if (jparms->input.len != 0) { if (jparms->input.len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); strncpy(pbuff, jparms->input.addr, jparms->input.len); *(pbuff + jparms->input.len) = '\0'; string_len = STRLEN("%s=%s") + STRLEN(IN_FILE_ENV) + STRLEN(pbuff) - 4; if (string_len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_STR(c1, IN_FILE_ENV, pbuff, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif } /* pass output file to child */ if (jparms->output.addr != 0) { if (jparms->output.len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); strncpy(pbuff, jparms->output.addr, jparms->output.len); *(pbuff + jparms->output.len) = '\0'; if (jobpid) SPRINTF(&pbuff[jparms->output.len], ".%d", getpid()); string_len = STRLEN("%s=%s") + STRLEN(OUT_FILE_ENV) + STRLEN(pbuff) - 4; if (string_len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_STR(c1, OUT_FILE_ENV, pbuff, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif } /* pass error file to child */ if (jparms->error.len != 0) { if (jparms->error.len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); strncpy(pbuff, jparms->error.addr, jparms->error.len); *(pbuff + jparms->error.len) = '\0'; if (jobpid) SPRINTF(&pbuff[jparms->error.len], ".%d", getpid()); string_len = STRLEN("%s=%s") + STRLEN(ERR_FILE_ENV) + STRLEN(pbuff) - 4; if (string_len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_STR(c1, ERR_FILE_ENV, pbuff, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif } /* pass routine name to child */ if (jparms->routine.len != 0) { if (jparms->routine.len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); strncpy(pbuff, jparms->routine.addr, jparms->routine.len); *(pbuff + jparms->routine.len) = '\0'; string_len = STRLEN("%s=%s") + STRLEN(ROUTINE_ENV) + STRLEN(pbuff) - 4; if (string_len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_STR(c1, ROUTINE_ENV, pbuff, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif } /* pass label name to child */ if (jparms->label.len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); strncpy(pbuff, jparms->label.addr, jparms->label.len); *(pbuff + jparms->label.len) = '\0'; string_len = STRLEN("%s=%s") + STRLEN(LABEL_ENV) + STRLEN(pbuff) - 4; if (string_len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_STR(c1, LABEL_ENV, pbuff, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif /* pass the offset */ string_len = STRLEN("%s=%ld") + STRLEN(OFFSET_ENV) + MAX_NUM_LEN - 5; if (string_len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_NUM(c1, OFFSET_ENV, jparms->offset, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif /* pass Priority to child */ if (jparms->baspri != 0) { string_len = STRLEN("%s=%ld") + STRLEN(PRIORITY_ENV) + MAX_NUM_LEN - 5; if (string_len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_NUM(c1, PRIORITY_ENV, jparms->baspri, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif } for (index = 0, jp = jparms->parms; jp ; index++, jp = jp->next) { if (jp->parm->str.len > MAX_JOB_LEN - 2) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); if (0 != jp->parm->mvtype) { MV_FORCE_STR(jp->parm); string_len = STRLEN(PARM_STR) + jp->parm->str.len + 1; if (string_len > MAX_JOB_LEN) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len); # ifdef KEEP_zOS_EBCDIC __getEstring1_a_copy(c1, STR_AND_LEN(PARM_STRING)); __getEstring1_a_copy(c1 + strlen(PARM_STRING), jp->parm->str.addr, jp->parm->str.len); # else SPRINTF(c1, GTMJ_FMT, index); memcpy(c1 + strlen(PARM_STR), jp->parm->str.addr, jp->parm->str.len); # endif *(c1 + string_len - 1) = 0; *env_ind++ = c1; } } string_len = STRLEN("%s=%ld") + STRLEN(GTMJCNT_ENV) + MAX_NUM_LEN - 5; if (string_len > TEMP_BUFF_SIZE) rts_error(VARLSTCNT(1) ERR_JOBPARTOOLONG); c1 = (char *)malloc(string_len + 1); #ifdef KEEP_zOS_EBCDIC #pragma convlit(suspend) #endif SPRINTF_ENV_NUM(c1, GTMJCNT_ENV, index, env_ind); #ifdef KEEP_zOS_EBCDIC #pragma convlit(resume) #endif #ifdef __osf__ /* Make sure SIZEOF(char *) is correct. */ #pragma pointer_size (save) #pragma pointer_size (long) #endif /* before appending the old environment into the environment array, do not add those * lines that correspond to any of the above already initialized environment variables. * this prevents indefinite growing of the environment array with nesting of job commands * which otherwise would show up eventually as an "Arg list too long" error from EXECVE() below. */ new_env_top = env_ind; old_env_top = &environ[environ_count]; DEBUG_ONLY( /* check that all new environment variables begin with the string "gtm". * this assumption is used later in the for loop below. */ for (new_env_cur = env_ary; new_env_cur < new_env_top; new_env_cur++) assert(!STRNCMP_LIT(*new_env_cur, "gtm")); ) for (old_env_cur = environ; old_env_cur < old_env_top; old_env_cur++) { env_end = strchr(*old_env_cur, '='); if ((NULL != env_end) && !STRNCMP_LIT(*old_env_cur, "gtm")) { env_len = (int)(env_end - *old_env_cur + 1); /* include the '=' too */ assert(env_len <= strlen(*old_env_cur)); for (new_env_cur = env_ary; new_env_cur < new_env_top; new_env_cur++) { if (0 == strncmp(*new_env_cur, *old_env_cur, env_len)) break; } if (new_env_cur < new_env_top) continue; } *env_ind++ = *old_env_cur; } *env_ind = NULL; /* null terminator required by execve() */ #ifdef __osf__ #pragma pointer_size (restore) #endif c1 = GETENV("gtm_dist"); string_len = STRLEN(c1); if ((string_len + SIZEOF(MUMPS_EXE_STR)) < SIZEOF(tbuff)) { memcpy(tbuff, c1, string_len); c2 = &tbuff[string_len]; strcpy(c2, MUMPS_EXE_STR); } else rts_error(VARLSTCNT(5) ERR_LOGTOOLONG, 3, string_len, c1, SIZEOF(tbuff) - SIZEOF(MUMPS_EXE_STR)); # ifdef KEEP_zOS_EBCDIC_ /* use real strcpy to preserve env in native code set */ # pragma convlit(suspend) # endif strcpy(cbuff, MUMPS_DIRECT_STR); # ifdef KEEP_zOS_EBCDIC_ # pragma convlit(resume) # endif #ifdef KEEP_zOS_EBCDIC __getEstring1_a_copy(tbuff2, STR_AND_LEN(tbuff)); argv[0] = tbuff2; #else argv[0] = tbuff; #endif argv[1] = cbuff; argv[2] = (char *)0; EXECVE(tbuff, argv, env_ary); /* if we got here, error starting the Job */ rts_error(VARLSTCNT(7) ERR_JOBFAIL, 0, ERR_TEXT, 2, LEN_AND_LIT("Exec error in Job"), errno); REVERT; } else { /* Parent, wait for the Middle process */ /* Wait ends under the following conditions * The middle process successfully forks off the actual Job or exits with error before doing that. * Timeout for the Job command. * For the first case, return the wait status as got from wait() syscall. * For the second condition, set non_exit_return to TRUE and return the relevant errno. */ do { /* note : macro, WAITPID would not be suitable here because we want to catch our timeout * waitpid expects an integer wait_status even for _BSD cases, but WIF* macros expect * a union wait argument (on AIX) */ done_pid = waitpid(child_pid, &wait_status, 0); /* BYPASSOK */ } while(!ojtimeout && 0 > done_pid && EINTR == errno); if (done_pid == child_pid) return (wait_status); else if (0 > done_pid && EINTR == errno && TRUE == ojtimeout) { /* Kill the middle process with SIGTERM and check the exit status from * the handler to see if the Middle process had actually successfully forked the Job */ KILL_N_REAP(child_pid, SIGTERM, kill_ret); if (-1 == kill_ret && ESRCH == errno) /* if the middle process finished by now */ { WAITPID(child_pid, &wait_status, 0, done_pid); if (done_pid == child_pid) return (wait_status); } else if (-1 != kill_ret && done_pid == child_pid && 0 == wait_status) return 0; /* timer popped in the window of child fork and middle process exit */ *non_exit_return = TRUE; return TIMEOUT_ERROR; /* return special value so as to eliminate the window where the timer * might pop after this routine returns to the callee and before the callee * analyses the return status (ojtimeout may not be a reliable indicator) */ } else if (0 > done_pid) { *non_exit_return = TRUE; save_errno = errno; KILL_N_REAP(child_pid, SIGKILL, kill_ret); return (save_errno); } else if (0 == done_pid) /* this case should never arise */ { assert(FALSE); *non_exit_return = TRUE; KILL_N_REAP(child_pid, SIGKILL, kill_ret); return (EINVAL); /* return some error condition */ } } return (EINVAL); /* This should never get executed, added to make compiler happy */ }