NetBSD/usr.sbin/sup/source/run.c

330 lines
7.4 KiB
C

/* $NetBSD: run.c,v 1.9 2001/09/24 13:22:38 wiz Exp $ */
/*
* Copyright (c) 1991 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the rights
* to redistribute these changes.
*/
/* run, runv, runp, runvp -- execute process and wait for it to exit
*
* Usage:
* i = run (file, arg1, arg2, ..., argn, 0);
* i = runv (file, arglist);
* i = runp (file, arg1, arg2, ..., argn, 0);
* i = runvp (file, arglist);
* i = runio (argv, in, out, err);
*
* Run, runv, runp and runvp have argument lists exactly like the
* corresponding routines, execl, execv, execlp, execvp. The run
* routines perform a fork, then:
* IN THE NEW PROCESS, an execl[p] or execv[p] is performed with the
* specified arguments. The process returns with a -1 code if the
* exec was not successful.
* IN THE PARENT PROCESS, the signals SIGQUIT and SIGINT are disabled,
* the process waits until the newly forked process exits, the
* signals are restored to their original status, and the return
* status of the process is analyzed.
* All run routines return: -1 if the exec failed or if the child was
* terminated abnormally; otherwise, the exit code of the child is
* returned.
*
**********************************************************************
* HISTORY
* Revision 1.1 89/10/14 19:53:39 rvb
* Initial revision
*
* Revision 1.2 89/08/03 14:36:46 mja
* Update run() and runp() to use <varargs.h>.
* [89/04/19 mja]
*
* 23-Sep-86 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Merged old runv and runvp modules.
*
* 22-Nov-85 Glenn Marcy (gm0w) at Carnegie-Mellon University
* Added check and kill if child process was stopped.
*
* 30-Apr-85 Steven Shafer (sas) at Carnegie-Mellon University
* Adapted for 4.2 BSD UNIX: Conforms to new signals and wait.
*
* 15-July-82 Mike Accetta (mja) and Neal Friedman (naf)
* at Carnegie-Mellon University
* Added a return(-1) if vfork fails. This should only happen
* if there are no more processes available.
*
* 28-Jan-80 Steven Shafer (sas) at Carnegie-Mellon University
* Added setuid and setgid for system programs' use.
*
* 21-Jan-80 Steven Shafer (sas) at Carnegie-Mellon University
* Changed fork to vfork.
*
* 20-Nov-79 Steven Shafer (sas) at Carnegie-Mellon University
* Created for VAX. The proper way to fork-and-execute a system
* program is now by "runvp" or "runp", with the program name
* (rather than an absolute pathname) as the first argument;
* that way, the "PATH" variable in the environment does the right
* thing. Too bad execvp and execlp (hence runvp and runp) don't
* accept a pathlist as an explicit argument.
*
**********************************************************************
*/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "supcdefs.h"
#include "supextern.h"
#ifndef __STDC__
#ifndef const
#define const
#endif
#endif
static int dorun __P((char *, char **, int));
static char **makearglist __P((va_list));
static char **
makearglist(ap)
va_list ap;
{
static size_t ns = 0;
static char **np = NULL;
int i = 0;
do {
if (i >= ns) {
ns += 20;
if ((np = realloc(np, ns)) == NULL)
return NULL;
}
np[i] = va_arg(ap, char *);
}
while (np[i++] != NULL);
return np;
}
int
#ifdef __STDC__
run(char *name, ...)
#else
run(va_alist)
va_dcl
#endif
{
int val;
va_list ap;
char **argv;
#ifdef __STDC__
va_start(ap, name);
#else
char *name;
va_start(ap);
name = va_arg(ap, char *);
#endif
if ((argv = makearglist(ap)) == NULL) {
va_end(ap);
return -1;
}
val = runv (name, argv);
va_end(ap);
return(val);
}
int runv (name,argv)
char *name,**argv;
{
return (dorun (name, argv, 0));
}
int
#ifdef __STDC__
runp(char *name, ...)
#else
runp (va_alist)
va_dcl
#endif
{
int val;
va_list ap;
char **argv;
#ifdef __STDC__
va_start(ap, name);
#else
char *name;
va_start(ap);
name = va_arg(ap, char *);
#endif
if ((argv = makearglist(ap)) == NULL)
return -1;
val = runvp (name, argv);
va_end(ap);
return (val);
}
int runvp (name,argv)
char *name,**argv;
{
return (dorun (name, argv, 1));
}
static
int dorun (name,argv,usepath)
char *name,**argv;
int usepath;
{
int wpid;
register int pid;
struct sigaction ignoresig,intsig,quitsig;
int status;
if ((pid = vfork()) == -1)
return(-1); /* no more process's, so exit with error */
if (pid == 0) { /* child process */
setgid (getgid());
setuid (getuid());
if (usepath)
execvp(name,argv);
else
execv(name,argv);
fprintf (stderr,"run: can't exec %s (%s)\n",name,
strerror(errno));
_exit (0377);
}
ignoresig.sa_handler = SIG_IGN; /* ignore INT and QUIT signals */
sigemptyset(&ignoresig.sa_mask);
ignoresig.sa_flags = 0;
sigaction (SIGINT,&ignoresig,&intsig);
sigaction (SIGQUIT,&ignoresig,&quitsig);
do {
wpid = wait3 (&status, WUNTRACED, 0);
if (WIFSTOPPED (status)) {
kill (0,SIGTSTP);
wpid = 0;
}
} while (wpid != pid && wpid != -1);
sigaction (SIGINT,&intsig,0); /* restore signals */
sigaction (SIGQUIT,&quitsig,0);
if (WIFSIGNALED (status) || WEXITSTATUS(status) == 0377)
return (-1);
return (WEXITSTATUS(status));
}
/*
* Like system(3), but with an argument list and explicit redirections
* that does not use the shell
*/
int
runio(argv, infile, outfile, errfile)
char *const argv[];
const char *infile;
const char *outfile;
const char *errfile;
{
int fd;
pid_t pid;
int status;
switch ((pid = fork())) {
case -1:
return -1;
case 0:
if (infile) {
(void) close(0);
if ((fd = open(infile, O_RDONLY)) == -1)
exit(1);
if (fd != 0)
(void) dup2(fd, 0);
}
if (outfile) {
(void) close(1);
if ((fd = open(outfile, O_RDWR|O_CREAT|O_TRUNC,
0666)) == -1)
exit(1);
if (fd != 1)
(void) dup2(fd, 1);
}
if (errfile) {
(void) close(2);
if ((fd = open(errfile, O_RDWR|O_CREAT|O_TRUNC,
0666)) == -1)
exit(1);
if (fd != 2)
(void) dup2(fd, 2);
}
execvp(argv[0], argv);
exit(1);
/*NOTREACHED*/
return 0;
default:
if (waitpid(pid, &status, 0) == -1)
return -1;
return status;
}
}
/*
* Like runio, but works with filedescriptors instead of filenames
*/
int
runiofd(argv, infile, outfile, errfile)
char *const argv[];
const int infile;
const int outfile;
const int errfile;
{
pid_t pid;
int status;
switch ((pid = fork())) {
case -1:
return -1;
case 0:
if (infile != 0) dup2(infile, 0);
if (outfile != 1) dup2(outfile,1);
if (errfile != 2) dup2(errfile,2);
execvp(argv[0], argv);
exit(1);
/*NOTREACHED*/
return 0;
default:
if (waitpid(pid, &status, 0) == -1)
return -1;
return status;
}
}