initscripts/src/process.c
2011-04-26 12:26:24 +02:00

325 lines
7.5 KiB
C

/*
* Copyright (c) 1999-2003 Red Hat, Inc. All rights reserved.
*
* This software may be freely redistributed under the terms of the GNU
* public license.
*
* 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.
*
*/
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <popt.h>
#include <regex.h>
#include "initlog.h"
#include "process.h"
extern regex_t **regList;
int forkCommand(char **args, int *outfd, int *errfd, int *cmdfd, int quiet) {
/* Fork command 'cmd', returning pid, and optionally pointer
* to open file descriptor fd */
int fdout=-1, fderr=-1, fdcmd=-1, pid;
int outpipe[2], errpipe[2], fdpipe[2];
int ourpid;
if ( (pipe(outpipe)==-1) || (pipe(errpipe)==-1) || (pipe(fdpipe)==-1) ) {
perror("pipe");
return -1;
}
if (outfd) {
fdout = outpipe[1];
*outfd = outpipe[0];
} else {
if (!quiet)
fdout=dup(1);
}
if (errfd) {
fderr = errpipe[1];
*errfd = errpipe[0];
} else {
if (!quiet)
fderr=dup(2);
}
if (cmdfd) {
*cmdfd = fdpipe[0];
fdcmd = fdpipe[1];
} else {
fdcmd = open("/dev/null",O_WRONLY);
}
if (fdout==-1 || fderr==-1 || fdcmd==-1)
return -1;
ourpid = getpid();
if ((pid = fork())==-1) {
perror("fork");
return -1;
}
/* We exec the command normally as the child. However, if we're getting passed
* back arguments via an fd, we'll exec it as the parent. Therefore, if Bill
* fucks up and we segfault or something, we don't kill rc.sysinit. */
if ( (cmdfd&&!pid) || (pid &&!cmdfd)) {
/* parent */
close(fdout);
close(fderr);
close(fdcmd);
if (!pid)
return ourpid;
else
return pid;
} else {
/* kid */
int sc_open_max;
if (outfd) {
if ( (dup2(fdout,1)==-1) ) {
perror("dup2");
exit(-1);
}
} else if (quiet)
if ((dup2(open("/dev/null",O_WRONLY),1))==-1) {
perror("dup2");
exit(-1);
}
if (errfd) {
if ((dup2(fderr,2)==-1)) {
perror("dup2");
exit(-1);
}
} else if (quiet)
if ((dup2(open("/dev/null",O_WRONLY),2))==-1) {
perror("dup2");
exit(-1);
}
if ((dup2(fdcmd,CMD_FD)==-1)) {
perror("dup2");
exit(-1);
}
close(fdout);
close(fderr);
close(fdcmd);
if (outfd)
close(*outfd);
if (errfd)
close(*errfd);
if (cmdfd)
close(*cmdfd);
/* close up extra fds, and hope this doesn't break anything */
sc_open_max = sysconf(_SC_OPEN_MAX);
if(sc_open_max > 1) {
int fd;
for(fd = 3; fd < sc_open_max; fd++) {
if (!(cmdfd && fd == CMD_FD))
close(fd);
}
}
execvp(args[0],args);
perror("execvp");
exit(-1);
}
}
int monitor(char *cmdname, int pid, int numfds, int *fds, int reexec, int quiet, int debug) {
struct pollfd *pfds;
char *outbuf=NULL;
char *tmpstr=NULL;
int x,y,rc=-1;
int done=0;
int output=0;
char **cmdargs=NULL;
char **tmpargs=NULL;
int cmdargc;
char *procpath = NULL;
if (reexec) {
procpath=malloc(20*sizeof(char));
snprintf(procpath,20,"/proc/%d",pid);
}
pfds = malloc(numfds*sizeof(struct pollfd));
for (x=0;x<numfds;x++) {
pfds[x].fd = fds[x];
pfds[x].events = POLLIN | POLLPRI;
}
while (!done) {
usleep(500);
if (((x=poll(pfds,numfds,500))==-1)&&errno!=EINTR) {
perror("poll");
free(pfds);
if (procpath)
free(procpath);
return -1;
}
if (!reexec) {
if (waitpid(pid,&rc,WNOHANG))
done=1;
} else {
struct stat sbuf;
/* if /proc/pid ain't there and /proc is, it's dead... */
if (stat(procpath,&sbuf)&&!stat("/proc/cpuinfo",&sbuf))
done=1;
}
if (x<0)
continue;
y=0;
while (y<numfds) {
if ( x && ((pfds[y].revents & (POLLIN | POLLPRI)) )) {
int bytesread = 0;
do {
char *b, *buf=calloc(8193,sizeof(char));
b = buf;
bytesread = read(pfds[y].fd,buf,8192);
if (bytesread==-1) {
perror("read");
free(pfds);
if (procpath)
free(procpath);
free(buf);
return -1;
}
if (bytesread) {
if (!quiet && !reexec)
write(1,buf,bytesread);
if (quiet) {
outbuf=realloc(outbuf,(outbuf ? strlen(outbuf)+bytesread+1 : bytesread+1));
if (!output) outbuf[0]='\0';
strcat(outbuf,buf);
output = 1;
}
while ((tmpstr=getLine(&buf))) {
int ignore=0;
if (regList) {
int count=0;
while (regList[count]) {
if (!regexec(regList[count],tmpstr,0,NULL,0)) {
ignore=1;
break;
}
count++;
}
}
if (!ignore) {
if (!reexec) {
if (getenv("IN_INITLOG")) {
char *buffer=calloc(8192,sizeof(char));
DDEBUG("sending =%s= to initlog parent\n",tmpstr);
snprintf(buffer,8192,"-n %s -s \"%s\"\n",
cmdname,tmpstr);
/* don't blow up if parent isn't there */
signal(SIGPIPE,SIG_IGN);
write(CMD_FD,buffer,strlen(buffer));
signal(SIGPIPE,SIG_DFL);
free(buffer);
} else {
logString(cmdname,tmpstr);
}
} else {
int z;
cmdargs=NULL;
tmpargs=NULL;
cmdargc=0;
poptParseArgvString(tmpstr,&cmdargc,&tmpargs);
cmdargs=malloc( (cmdargc+2) * sizeof(char *) );
cmdargs[0]=strdup("initlog");
for (z=0;z<(cmdargc);z++) {
cmdargs[z+1]=tmpargs[z];
}
cmdargs[cmdargc+1]=NULL;
processArgs(cmdargc+1,cmdargs,1);
free(cmdargs[0]);
free(tmpargs);
free(cmdargs);
}
}
if (tmpstr) free(tmpstr);
}
}
free(b);
} while ( bytesread==8192 );
}
y++;
}
}
if ((!WIFEXITED(rc)) || (rc=WEXITSTATUS(rc))) {
/* If there was an error and we're quiet, be loud */
if (quiet && output) {
write(1,outbuf,strlen(outbuf));
}
free(pfds);
if (procpath)
free(procpath);
if(outbuf)
free(outbuf);
return (rc);
}
free(pfds);
if (procpath)
free(procpath);
if(outbuf)
free(outbuf);
return 0;
}
int runCommand(char *cmd, int reexec, int quiet, int debug) {
int fds[2];
int pid,x;
char **args, **tmpargs;
char *cmdname;
poptParseArgvString(cmd,&x,&tmpargs);
args = malloc((x+1)*sizeof(char *));
for ( pid = 0; pid < x ; pid++) {
args[pid] = strdup(tmpargs[pid]);
}
args[pid] = NULL;
if (strcmp(args[0],"sh") && strcmp(args[0],"/bin/sh"))
cmdname = basename(args[0]);
else
cmdname = basename(args[1]);
if ((cmdname[0] =='K' || cmdname[0] == 'S') &&
( cmdname[1] >= '0' && cmdname[1] <= '9' ) &&
( cmdname[2] >= '0' && cmdname[2] <= '9' ) )
cmdname+=3;
if (!reexec) {
pid=forkCommand(args,&fds[0],&fds[1],NULL,quiet);
if (pid == -1)
return -1;
x=monitor(cmdname,pid,2,fds,reexec,quiet,debug);
} else {
setenv("IN_INITLOG","yes",1);
pid=forkCommand(args,NULL,NULL,&fds[0],quiet);
if (pid == -1)
return -1;
unsetenv("IN_INITLOG");
x=monitor(cmdname,pid,1,&fds[0],reexec,quiet,debug);
}
return x;
}