NetBSD/usr.sbin/sup/source/run.c
2003-10-16 06:22:09 +00:00

298 lines
7.0 KiB
C

/* $NetBSD: run.c,v 1.12 2003/10/16 06:26:06 itojun 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"
static int dorun(char *, char **, int);
static char **makearglist(va_list);
static char **
makearglist(va_list ap)
{
static size_t ns = 0;
static char **np = NULL;
char **nnp;
int i = 0;
do {
if (i >= ns) {
nnp = realloc(np, ns + 20);
if (nnp == NULL) {
free(np);
return NULL;
}
np = nnp;
ns += 20;
}
np[i] = va_arg(ap, char *);
}
while (np[i++] != NULL);
return np;
}
int
run(char *name, ...)
{
int val;
va_list ap;
char **argv;
va_start(ap, name);
if ((argv = makearglist(ap)) == NULL) {
va_end(ap);
return -1;
}
val = runv(name, argv);
va_end(ap);
return (val);
}
int
runv(char *name, char **argv)
{
return (dorun(name, argv, 0));
}
int
runp(char *name, ...)
{
int val;
va_list ap;
char **argv;
va_start(ap, name);
if ((argv = makearglist(ap)) == NULL) {
va_end(ap);
return -1;
}
val = runvp(name, argv);
va_end(ap);
return (val);
}
int
runvp(char *name, char **argv)
{
return (dorun(name, argv, 1));
}
static int
dorun(char *name, char **argv, int usepath)
{
int wpid;
int pid;
struct sigaction ignoresig, intsig, quitsig;
int status;
if ((pid = vfork()) == -1)
return (-1); /* no more processes, 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(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(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;
}
}