This is zendutil.c in view mode; [Download] [Up]
static char rcsid[] = "$Id: zendutil.c,v 1.12 1993/02/02 21:00:00 gerben Exp $"; /* Zend --- a mail transport program for large files and directories Copyright (C) 1992 Gerben C. Th. Wierda This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Gerben Wierda Goudriaanstraat 1 1222 SG Hilversum The Netherlands gerben@rna.indiv.nluug.nl */ /* * * zendutil.c -- various routines for supporting zend. * */ #include "zend.h" /* * read_config() -- open HOMEDIR/config and parse the file. */ #if _ANSI_DEFUN_ extern int read_config( void) #else extern int read_config() #endif { char buf[PATHBUFSIZ]; FILE *configStream; strcpy( buf, HOMEDIR); strcat( buf, "/config"); if ((configStream = fopen( buf, "r")) == NIL) { return -1; } else { while (parsestreamtoglob( global, configStream) != PARSEEOF) ; fclose( configStream); } if (get_global_string( global, SYSTEMNAMEPROMPT) == NIL) { return -2; } return 0; } /* * Find a Variable in the array sentinel-terminated array `globs', by name. * Return a pointer to that variable, and NULL if not found. */ static Variable * #if _ANSI_DEFUN_ get_var( Variable vars[], const char *prompt) #else get_var( vars, prompt) Variable vars[]; char *prompt; #endif { register Variable *varptr; for (varptr = vars; varptr->type != NONE; varptr++) { if (strcmp( varptr->prompt, prompt) == 0) { return varptr; } } return NULL; } #if _ANSI_DEFUN_ extern int set_global( Variable *globs, const char *prompt, ...) #else extern int set_global( globs, prompt, va_alist) Variable *globs; char *prompt; va_dcl #endif { register va_list argpt; register Variable *varptr = get_var( globs, prompt); if (varptr == NULL) { return -1; } va_begin( argpt, prompt); switch( varptr->type) { case STRING: if (varptr->value.string) { free(varptr->value.string); } if (varptr->value.string = va_arg( argpt, char *)) { varptr->value.string = strcpy( safecalloc( strlen( varptr->value.string) + 1, sizeof( char)), varptr->value.string); } break; case ULONG: varptr->value.ulong = va_arg( argpt, Ulong); break; case USHORT: varptr->value.ushort = (Ushort)va_arg( argpt, unsigned int); break; case BOOLEAN: varptr->value.bool = va_arg( argpt, Boolean); break; default: ; } va_end( argpt); return (int)(varptr - globs); } #if _ANSI_DEFUN_ extern Ulong get_global_ulong( Variable *globs, const char *prompt) #else extern Ulong get_global_ulong( globs, prompt) Variable *globs; char *prompt; #endif { register Variable *varptr = get_var(globs, prompt); if (varptr == NULL || varptr->type != ULONG) { zerr( "get_global_ulong with wrong prompt %s\n", prompt); abort(); } return varptr->value.ulong; } #if _ANSI_DEFUN_ extern Ushort get_global_ushort( Variable *globs, const char *prompt) #else extern Ushort get_global_ushort( globs, prompt) Variable *globs; char *prompt; #endif { register Variable *varptr = get_var(globs, prompt); if (varptr == NULL || varptr->type != USHORT) { zerr( "get_global_ushort with wrong prompt %s\n", prompt); abort(); } return varptr->value.ushort; } #if _ANSI_DEFUN_ extern StringPointer get_global_string( Variable *globs, const char *prompt) #else extern StringPointer get_global_string( globs, prompt) Variable *globs; char *prompt; #endif { register Variable *varptr = get_var(globs, prompt); if (varptr == NULL || varptr->type != STRING) { zerr( "get_global_string with wrong prompt %s\n", prompt); abort(); } return varptr->value.string; } #if _ANSI_DEFUN_ extern Boolean get_global_boolean( Variable *globs, const char *prompt) #else extern Boolean get_global_boolean( globs, prompt) Variable *globs; char *prompt; #endif { register Variable *varptr = get_var(globs, prompt); if (varptr == NULL || varptr->type != BOOLEAN) { zerr( "get_global_boolean with wrong prompt %s\n", prompt); abort(); } return varptr->value.bool; } /* * parsebuftoglob() -- parse the buffer for a prompt from the global set * and get the accopanying value. */ #if _ANSI_DEFUN_ extern int parsebuftoglob( Variable *globs, const char *buf) #else extern int parsebuftoglob( globs, buf) Variable *globs; char *buf; #endif { register Variable *varptr; for (varptr = globs; varptr->type != NONE; varptr++) { if (strncmp( varptr->prompt, buf, strlen( varptr->prompt)) == 0) { buf += (strlen( varptr->prompt)); switch (varptr->type) { Ulong tmpulong; case STRING: if (strlen( buf) == 0) { varptr->value.string = NIL; } else { varptr->value.string = (StringPointer)safecalloc( strlen(buf)+1, sizeof( char)); strcpy( varptr->value.string, buf); } break; case USHORT: sscanf( buf, "%lu", &tmpulong); varptr->value.ushort = (Ushort)tmpulong; break; case ULONG: sscanf( buf, "%lu", &tmpulong); varptr->value.ulong = tmpulong; break; case BOOLEAN: if (strncmp( buf, BOOLTRUESTRING, strlen( BOOLTRUESTRING)) == 0) { varptr->value.bool = TRUE; } else if (strncmp( buf, BOOLFALSESTRING, strlen( BOOLFALSESTRING)) == 0) { varptr->value.bool = FALSE; } else { zerr( "ERROR, illegal value for global boolean, set to %s\n", BOOLFALSESTRING); varptr->value.bool = FALSE; } break; default: /* Only for compiler warning. */ ; } return (int)(varptr - globs); } } return PARSEUNKNOWN; } /* * writeglobtobuf() -- Write the prompt and the value to buf. */ #if _ANSI_DEFUN_ extern int writeglobtobuf( Variable *globs, const char *prompt, char *buf) #else extern int writeglobtobuf( globs, prompt, buf) Variable *globs; char *prompt; char *buf; #endif { register Variable *varptr = get_var( globs, prompt); if (varptr == NULL) { return -1; } switch (varptr->type) { char *boolstr; case STRING: sprintf( buf, "%s%s", varptr->prompt, varptr->value.string == NIL ? "" : varptr->value.string); break; case USHORT: sprintf( buf, "%s%u", varptr->prompt, (unsigned)varptr->value.ushort); break; case ULONG: sprintf( buf, "%s%lu", varptr->prompt, varptr->value.ulong); break; case BOOLEAN: boolstr = ""; switch (varptr->value.bool) { case FALSE: boolstr = BOOLFALSESTRING; break; case TRUE: boolstr = BOOLTRUESTRING; break; } sprintf( buf, "%s%s", varptr->prompt, boolstr); break; default: /* Programmer error */ ; } return (int)(varptr - globs); } /* * parsestreamtoglob() -- read a line from the stream and run parsebuftoglob() * reads until some variable is found or EOF. */ #if _ANSI_DEFUN_ extern int parsestreamtoglob( Variable *globs, FILE *stream) #else extern int parsestreamtoglob( globs, stream) Variable *globs; FILE *stream; #endif { char buf[PATHBUFSIZ]; while (fgets( buf, PATHBUFSIZ, stream)) { if (strncmp( buf, ZENDDELIMITER, strlen( ZENDDELIMITER)) == 0) { return PARSEDELIMITER; } if (buf[strlen( buf)-1] == '\n') { buf[strlen( buf)-1] = NUL; } return parsebuftoglob( globs, buf); } return PARSEEOF; } /* * writeglobtostream() -- run writeglobtobuf() and write buf to stream */ #if _ANSI_DEFUN_ extern int writeglobtostream( Variable *globs, const char *prompt, FILE *stream) #else extern int writeglobtostream( globs, prompt, stream) Variable *globs; char *prompt; FILE *stream; #endif { char buf[PATHBUFSIZ]; int varno; if ((varno = writeglobtobuf( globs, prompt, buf)) != -1) { strcat( buf, "\n"); fputs( buf, stream); #if DEBUG if (dump && stderr != stream) { zerr( "%s", buf); } #endif } return varno; } extern Boolean #if _ANSI_DEFUN_ write_globs_to_jobfile( Variable *globs, Ulong jobId) #else write_globs_to_jobfile( globs, jobId) Variable *globs; Ulong jobId; #endif { char buf[PATHBUFSIZ]; FILE *tmpStream; int try; int oldumask; sprintf( buf, "%s/C.%lu", HOMEDIR, jobId); add_file_to_cleanup( buf, FALSE); if (one_at_a_time( jobId, RDWR_JOBFILE) != 0) { return TRUE; } oldumask = umask( ~(S_IRUSR|S_IWUSR)); tmpStream = fopen( buf, "w"); umask( oldumask); if (tmpStream == NIL) { zerr( "Unable to create job config %s\n", buf); end_one_at_a_time( jobId, RDWR_JOBFILE); return FALSE; } for (try=0; try<nrOfGlobalVariables; try++) { writeglobtostream( global, global[try].prompt, tmpStream); } if (fclose( tmpStream) != 0) { zerr( "Unable to save job config %s\n", buf); end_one_at_a_time( jobId, RDWR_JOBFILE); return FALSE; } end_one_at_a_time( jobId, RDWR_JOBFILE); return TRUE; } #if _ANSI_DEFUN_ extern int read_jobfile_to_globs( Variable *globs, Ulong jobId) #else extern int read_jobfile_to_globs( globs, jobId) Variable *globs; Ulong jobId; #endif { char buf[PATHBUFSIZ]; FILE *tmpStream; sprintf( buf, "%s/C.%lu", HOMEDIR, jobId); if (one_at_a_time( jobId, RDWR_JOBFILE) != 0) { return -1; } if ((tmpStream = fopen( buf, "r")) == NIL) { end_one_at_a_time( jobId, RDWR_JOBFILE); return -1; } while( parsestreamtoglob( globs, tmpStream) != PARSEEOF) ; fclose( tmpStream); end_one_at_a_time( jobId, RDWR_JOBFILE); return 0; } #if _ANSI_DEFUN_ extern int one_at_a_time( Ulong jobId, char type) #else extern int one_at_a_time( jobId, type) Ulong jobId; char type; #endif { char buf[PATHBUFSIZ]; char buf2[PATHBUFSIZ]; int fd; sprintf( buf, "%s/D.%lu/X.%05u%c", HOMEDIR, jobId, process_id, type); add_file_to_cleanup( buf, FALSE); if ((fd = creat( buf, 0)) == -1) { remove_file_to_cleanup( buf); zerr( "Failed lock (%lu,%c)\n", jobId, type); return -1; } close( fd); if (jobId == RESERVEDID) { sprintf( buf2, "%s/LCK.%c", HOMEDIR, type); } else { sprintf( buf2, "%s/D.%lu/LCK.%c", HOMEDIR, jobId, type); } while (link( buf, buf2) != 0) { sleep( 2); } add_file_to_cleanup( buf2, FALSE); #if DEBUG zerr( "Got lock (%lu,%c)\n", jobId, type); #endif unlink( buf); remove_file_to_cleanup( buf); return 0; } #if _ANSI_DEFUN_ extern int end_one_at_a_time( Ulong jobId, char type) #else extern int end_one_at_a_time( jobId, type) Ulong jobId; char type; #endif { char buf[PATHBUFSIZ]; if (jobId == RESERVEDID) { sprintf( buf, "%s/LCK.%c", HOMEDIR, type); } else { sprintf( buf, "%s/D.%lu/LCK.%c", HOMEDIR, jobId, type); } if (unlink( buf) != 0) { zerr( "Failed unlock (%lu,%c)\n", jobId, type); return -1; } remove_file_to_cleanup( buf); #if DEBUG zerr( "Got unlock (%lu,%c)\n", jobId, type); #endif return 0; } extern void #if _ANSI_DEFUN_ cleanup_stale_locks( void) #else cleanup_stale_locks() #endif { char buf[PATHBUFSIZ]; sprintf( buf, "cd %s && %s -f LCK.* */LCK.* */X.*", HOMEDIR, RMPROG); system_as_zend( buf); } /* * Create a directory for this transfer. The name of the directory defines * the jobID. If not found within RETRY_LIMIT tries, give up. */ extern Ulong #if _ANSI_DEFUN_ new_job( void) #else new_job() #endif { char buf[PATHBUFSIZ]; int try; Ulong jobId; #if DEBUG zerr( "Trying to find job slot within retry limit (%d)\n", RETRY_LIMIT); #endif srandom( (int)time( NIL)); for (try=0; try<RETRY_LIMIT; try++) { if ((jobId = (Ulong)random()) == RESERVEDID) { continue; } sprintf( buf, "%s/D.%lu", HOMEDIR, jobId); add_file_to_cleanup( buf, TRUE); if (mkdir( buf, S_IRWXU) == 0) { #if DEBUG zerr( "Found job slot %lu\n", jobId); #endif return jobId; } remove_file_to_cleanup( buf); } zerr( "No job slot found within retry limit (%d)!\n", RETRY_LIMIT); return RESERVEDID; } /* * Kill a job. (but only if it exists.) */ extern void #if _ANSI_DEFUN_ kill_job( Ulong jobId) #else kill_job( jobId) Ulong jobId; #endif { char buf[PATHBUFSIZ]; struct stat statBuf; sprintf( buf, "%s/D.%lu", HOMEDIR, jobId); #if 0 if (access( buf, F_OK) == 0) #endif if (stat( buf, &statBuf) == 0) { sprintf( buf, "cd %s && %s -rf C.%lu D.%lu F.%lu", HOMEDIR, RMPROG, jobId, jobId, jobId); system_as_zend( buf); } } /* * See if `str' is listed as an element in a comma-separated `list'. */ extern Boolean #if _ANSI_DEFUN_ str_listed( const char *str, const char *list) #else str_listed(str, list) char *str, *list; #endif { const char *lp; if (lp = list) { int len = strlen(str); for (; lp = strchr( lp, str[0]); lp++) { if ((lp == list || lp[-1] == ',') && (strncmp( lp, str, len) == 0) && (lp[len] == '\0' || lp[len] == ',')) { return TRUE; } } } return FALSE; } static void #if _ANSI_DEFUN_ vzlog( const char *format, va_list argpt) #else vzlog( format, argpt) char *format; va_list argpt; #endif { char logfilename[PATHBUFSIZ]; FILE *logStream; struct tm *t; struct stat statBuf; sprintf( logfilename, "%s/LOGFILE", HOMEDIR); /* * Log only if logfile already exists. */ #if 0 if (access( logfilename, F_OK) != 0) #endif if (stat( logfilename, &statBuf) != 0) { return; } /* {{one_at_a_time()?}} */ if ((logStream = fopen( logfilename, "a")) == NULL) { return; } t = localtime( ×tamp); fprintf( logStream, "%04d/%02d/%02d-%02d:%02d (%lu) %s %s %s %s %s/%s: ", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, (get_global_string( global, ROLEPROMPT) ? get_global_ulong( global, (strcmp( get_global_string( global, ROLEPROMPT), ROLE_SENDER) == 0) ? SENDINGJOBIDPROMPT : RECEIVINGJOBIDPROMPT) : RESERVEDID), get_global_string( global, ROLEPROMPT), get_global_string( global, SENDINGUSERPROMPT), get_global_string( remoteglobal, SYSTEMNAMEPROMPT), get_global_string( global, RECEIVINGUSERPROMPT), get_global_string( global, DIRPROMPT), get_global_string( global, FILEPROMPT)); vfprintf( logStream, format, argpt); fprintf( logStream, "\n"); fclose( logStream); } extern void #if _ANSI_DEFUN_ zlog( const char *format, ...) #else zlog( format, va_alist) char *format; va_dcl #endif { va_list argpt; va_begin( argpt, format); vzlog( format, argpt); va_end( argpt); } #if _ANSI_DEFUN_ extern void zerr( const char *s, ...) #else extern void zerr( s, va_alist) char *s; va_dcl #endif { va_list argpt; va_begin( argpt, s); if ( logErrors) { char formatbuf[PATHBUFSIZ]; int len = strlen(s); /* Kludge to remove trailing newline (if any) */ if (len > 0 && s[len-1] == '\n') { --len; } sprintf( formatbuf, "error (%.*s)", len, s); vzlog( formatbuf, argpt); } else { fprintf( stderr, "Zend: "); vfprintf( stderr, s, argpt); } va_end( argpt); } /* support functions for signal handler. */ struct filelist { struct filelist *next; Boolean isDirectory; char name[1]; /* Dynamically allocated. */ }; static struct filelist *filesToCleanup; extern void #if _ANSI_DEFUN_ add_file_to_cleanup( const char *name, Boolean isDirectory) #else add_file_to_cleanup( name, isDirectory) char *name; Boolean isDirectory; #endif { struct filelist *np; for (np = filesToCleanup; np; np = np->next) { if (strcmp( name, np->name) == 0) { /* this shouldn't happen. */ zerr( "BUG (add_file_to_cleanup) duplicate filename %s\n", name); return; } } np = (struct filelist *)safecalloc( 1, strlen( name) + sizeof( *np)); np->next = filesToCleanup; filesToCleanup = np; strcpy( np->name, name); np->isDirectory = isDirectory; } extern void #if _ANSI_DEFUN_ remove_file_to_cleanup( const char *name) #else remove_file_to_cleanup( name) char *name; #endif { struct filelist *np, **head = &filesToCleanup; for (head = &filesToCleanup; np = (*head); head = &np->next) { if (strcmp( name, np->name) == 0) { (*head) = np->next; free(np); return; } } /* This shouldn't happen. */ zerr( "BUG (remove_file_to_cleanup) missing filename %s\n", name); } extern void #if _ANSI_DEFUN_ file_cleanup( void) #else file_cleanup() #endif { struct filelist *np; while (np = filesToCleanup) { if (np->isDirectory) { rmdir( np->name); } else { unlink( np->name); } filesToCleanup = np->next; free( np); } } /* Like system(), but user and group IDs of child process can be given. Also this doesn't ignore signals, so it can be interrupted. */ extern int #if _ANSI_DEFUN_ system_as( const char *command, uid_t uid, gid_t gid) #else system_as( command, uid, gid) const char *command; uid_t uid; gid_t gid; #endif { pid_t child_pid; WAIT_T status; if ((child_pid = fork()) == 0) { /* child. */ setuid( uid); setgid( gid); execl( "/bin/sh", "sh(zend)", "-c", command, NULL); exit( 127); } if (child_pid < 0) { #if DEBUG zerr( "Unable to fork()\n"); #endif return -1; } /* parent */ if (waitpid( child_pid, &status, 0) < 0) { #if DEBUG zerr( "system_as: waitpid( %d) failed. (%s)\n", child_pid, strerror( errno)); #endif return -1; } return *(int *)&status; /* {cast since WAIT_T may be `union wait'} */ } /* Like popen(), but user and group IDs of child process can be given. Also this doesn't ignore signals, so it can be interrupted. {NOTE: Also note that popen_as() should be paired with pclose_as(); the aliases for popen/pclose in zend.h help to enforce this.} */ static struct pinfo_listitem { FILE *stream; pid_t child_pid; struct pinfo_listitem *next; } popen_info_listhead; extern FILE * #if _ANSI_DEFUN_ popen_as( const char *command, const char *mode, uid_t uid, gid_t gid) #else popen_as( command, mode, uid, gid) const char *command; const char *mode; uid_t uid; gid_t gid; #endif { int pipe_fd[2]; int writing; struct pinfo_listitem *pinfo = &popen_info_listhead; assert(mode && (*mode == 'r' || *mode == 'w')); writing = (*mode == 'w'); while (pinfo->stream != NULL && pinfo->next != NULL) { #if DEBUG zerr( "popen_as: nested call.\n"); #endif pinfo = pinfo->next; } if (pinfo->stream != NULL) { pinfo->next = (struct pinfo_listitem *) calloc( 1, sizeof( struct pinfo_listitem)); if (pinfo->next == NULL) { #if DEBUG zerr( "popen_as: pipe() failed. (Out of memory)\n"); #endif return NULL; } pinfo = pinfo->next; } if (pipe( pipe_fd) != 0) { #if DEBUG zerr( "popen_as: pipe() failed. (%s)\n", strerror( errno)); #endif return NULL; } if ((pinfo->stream = fdopen( pipe_fd[writing], mode)) == NULL) { #if DEBUG zerr( "popen_as: fdopen() failed. (%s)\n", strerror( errno)); #endif close(pipe_fd[0]); close(pipe_fd[1]); return NULL; } if ((pinfo->child_pid = fork()) == 0) { /* child. */ setuid( uid); setgid( gid); /* Connect pipe. (in child, read and write reverse roles) */ writing = !writing; if (dup2( pipe_fd[writing], writing) != writing) { exit( 127); } close( pipe_fd[0]); close( pipe_fd[1]); execl( "/bin/sh", "sh(zend)", "-c", command, NULL); exit( 127); } /* parent */ close( pipe_fd[!writing]); if (pinfo->child_pid < 0) { #if DEBUG zerr( "popen_as: fork() failed. (%s)\n", strerror( errno)); #endif fclose(pinfo->stream); pinfo->stream = NULL; } return pinfo->stream; } extern int #if _ANSI_DEFUN_ pclose_as( FILE *stream) #else pclose_as( stream) FILE *stream; #endif { WAIT_T status; struct pinfo_listitem *pinfo = &popen_info_listhead; while (stream != pinfo->stream && pinfo->next != NULL) { pinfo = pinfo->next; } if (stream != pinfo->stream) { errno = EINVAL; return -1; } /* Close the pipe BEFORE wait()ing for the child to finish. (so the child, if still running, sees an end-of-file, or gets a broken pipe) */ fclose( stream); pinfo->stream = NULL; if (waitpid( pinfo->child_pid, &status, 0) < 0) { #if DEBUG zerr( "pclose_as: waitpid( %d) failed. (%s)\n", pinfo->child_pid, strerror( errno)); #endif return -1; /* {{really?}} */ } return *(int *)&status; /* {cast since WAIT_T may be `union wait'} */ } /* * Return basename of `filename'. */ extern char * #if _ANSI_DEFUN_ basename( const char *filename) #else basename( filename) char *filename; #endif { char *sep; return (sep = strrchr(filename, '/')) ? (sep + 1) : (char *) filename; } /* * Quote `str' for use in a shell command; return result in a dynamically * allocated string. */ extern char * #if _ANSI_DEFUN_ quote_for_sh( const char *str) #else quote_for_sh( str) char *str; #endif { const char *s; char *buf, *d; int nquotes = 0; /* count number of quote (') characters in string. */ for (s = str; *s != '\0'; ) { if (*s++ == '\'') { nquotes++; } } /* allocate space for result. */ d = buf = safecalloc( 1 + (size_t)(s - str) + nquotes * 3 + 1 + 1, sizeof( char)); *d++ = '\''; for (s = str; (*d++ = *s) != '\0'; ) { if (*s++ == '\'') { *d++ = '\\', *d++ = '\'', *d++ = '\''; } } d[-1] = '\''; d[0] = '\0'; return buf; } extern void_* #if _ANSI_DEFUN_ safecalloc( size_t n, size_t size) #else safecalloc( n, size) size_t n; size_t size; #endif { void_*p; if ((p = calloc( n, size)) == NULL) { zerr("Out of memory!"); abort(); } return p; } #if !HAVE_STRERROR # ifdef strerror # if _ANSI_DEFUN_ extern char *strerror( int errnum) # else extern char *strerror( errnum) int errnum; # endif { # ifndef sys_nerr extern int sys_nerr; # endif # ifndef sys_errlist extern char *sys_errlist[]; # endif if ((unsigned) errnum < sys_nerr) { return sys_errlist[errnum]; } else { static char buf[20]; sprintf( buf, "Error %d", errnum); return( buf); } } # endif /* strerror */ #endif /* HAVE_STRERROR */ #if !__POSIX__ /* Emulation of Posix.1 waitpid(). Semantics: pid > 0 - wait for single child process with process id `pid'; pid == 0 - wait for any child process in the caller's process group; pid == -1 - wait for any child process; pid < -1 - wait for any child process in process group `-pid'. Currently process group waits (i.e. pid == 0 or pid < -1) are not supported. Also WNOHANG and WUNTRACED options are supported only on systems that have at least the wait3() call. */ #if !HAVE_WAIT4 static pid_t wait_lookup __(( pid_t _(pid), WAIT_T *_(stat_loc) )); static int wait_store __(( pid_t _(pid), const WAIT_T *_(stat_loc) )); #endif extern pid_t #if _ANSI_DEFUN_ waitpid( pid_t pid, WAIT_T *stat_loc, int options) #else waitpid( pid, stat_loc, options) pid_t pid; WAIT_T *stat_loc; int options; #endif { #if HAVE_WAIT4 if (pid <= 0) { if (pid != -1) { errno = EINVAL; return -1; } pid = 0; /* wait4() expects pid=0 for indiscriminate wait. */ } return wait4( pid, stat_loc, options, NULL); #else /* !HAVE_WAIT4 */ pid_t child_pid; if (pid <= 0 && pid != -1) { errno = EINVAL; return -1; } if ((child_pid = wait_lookup( pid, stat_loc)) < 0) { # if !HAVE_WAIT3 if (options & WNOHANG) { errno = EINVAL; return -1; } while ((child_pid = wait( stat_loc)) > 0) # else /* HAVE_WAIT3 */ while ((child_pid = wait3( stat_loc, options, NULL)) > 0) # endif /* HAVE_WAIT3 */ { if (pid == -1 || pid == child_pid) { break; } if (wait_store( pid, stat_loc) != 0) { return -1; } } } return child_pid; #endif /* !HAVE_WAIT4 */ } #if !HAVE_WAIT4 /* Store status of previously-encountered waited-for children in an array sorted on process id (so we can do a binary search.) */ static struct wait_list { struct wait_info { pid_t wait_pid; WAIT_T wait_status; } *wait_base; int wait_size; int wait_used; } waitlist; static int #if _ANSI_DEFUN_ wait_search( pid_t pid) #else wait_search( pid) pid_t pid; #endif { /* Binary search on `pid'. */ int top = 0, end = waitlist.wait_used; while (top < end) { int mid = (top + end) >> 1; struct wait_info *wp = &waitlist.wait_base[mid]; if (pid < wp->wait_pid) { end = mid; } else if (pid > wp->wait_pid) { top = mid + 1; } else /* (pid == wp->wait_pid) -- match! */ { return mid; } } /* No match: return (-(the location where `pid' should be inserted) - 1) */ return (-top - 1); } static pid_t #if _ANSI_DEFUN_ wait_lookup( pid_t pid, WAIT_T *stat_loc) #else wait_lookup( pid, stat_loc) pid_t pid; WAIT_T *stat_loc; #endif { struct wait_info *wp = waitlist.wait_base; int where; if ((where = (pid == -1) ? waitlist.wait_used - 1 : wait_search( pid)) < 0) { return -1; } pid = wp[where].wait_pid; (*stat_loc) = wp[where].wait_status; /* Remove entry from array. */ while (++where < waitlist.wait_used) { wp[where - 1] = wp[where]; } --waitlist.wait_used; return pid; } static pid_t #if _ANSI_DEFUN_ wait_store( pid_t pid, const WAIT_T *stat_loc) #else wait_store( pid, stat_loc) pid_t pid; WAIT_T *stat_loc; #endif { struct wait_info *wp = waitlist.wait_base; int i, where; if ((where = wait_search( pid)) < 0) /* add a new entry. */ { where = -where - 1; /* Expand the array if necessary. */ if (waitlist.wait_used == waitlist.wait_size) { if (!(wp = malloc( (waitlist.wait_size + 16) * sizeof( *wp)))) { return -1; } /* Deliberately no realloc() since that would invalidate the entries we already have when it fails... */ if (waitlist.wait_base != NULL) { i = waitlist.wait_size; /* PRE: > 0 */ do { --i; wp[i] = waitlist.wait_base[i]; } while (i); free( waitlist.wait_base); } waitlist.wait_base = wp; waitlist.wait_size += 16; } /* Shift tail upward to make room for new entry. */ for (i = waitlist.wait_used; i > where; --i) { wp[i] = wp[i - 1]; } ++waitlist.wait_used; } wp[where].wait_pid = pid; wp[where].wait_status = (*stat_loc); return 0; } #endif /* !HAVE_WAIT4 */ #endif /* __POSIX__ */ /*====================================================================== * $Log: zendutil.c,v $ * Revision 1.12 1993/02/02 21:00:00 gerben * *** empty log message *** * * Revision 1.12 1993/02/02 15:41:09 tom * allow multiple active popen()ed streams since this was needed for * check_timeout(); in conneection with this, add emulation of waitpid() for * non-Posix systems. * * Revision 1.11 1993/01/28 18:54:26 tom * add popen_as(), pclose_as() for secure piping; allow setuid non-root * installation; add a real safecalloc(); fix typo in non-ANSI C code. * * Revision 1.10 1993/01/10 20:20:25 tom * add basename(), quote_for_shell(); make vzlog() more robust. * * Revision 1.9 1992/12/22 15:11:58 tom * revision of HAVE_STRERROR. * * Revision 1.8 1992/12/18 23:11:13 tom * fix some non-ANSI C discrepancies. * * Revision 1.7 1992/12/10 05:27:34 tom * add support for file-cleanup on signal; stale-lock cleanup; plug security * holes; add support for safe program execution. * *======================================================================*/
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.