Examples
Catching SIGCHLD
#include <sys/types.h> /* include this before any other sys headers */ #include <sys/wait.h> /* header for waitpid() and various macros */ #include <signal.h> /* header for signal functions */ #include <stdio.h> /* header for fprintf() */ #include <unistd.h> /* header for fork() */ void sig_chld(int); /* prototype for our SIGCHLD handler */ int main() { struct sigaction act; pid_t pid; /* Assign sig_chld as our SIGCHLD handler */ act.sa_handler = sig_chld; /* We don't want to block any other signals in this example */ sigemptyset(&act.sa_mask); /* * We're only interested in children that have terminated, not ones * which have been stopped (eg user pressing control-Z at terminal) */ act.sa_flags = SA_NOCLDSTOP; /* * Make these values effective. If we were writing a real * application, we would probably save the old value instead of * passing NULL. */ if (sigaction(SIGCHLD, &act, NULL) < 0) { fprintf(stderr, "sigaction failed\n"); return 1; } /* Fork */ switch (pid = fork()) { case -1: fprintf(stderr, "fork failed\n"); return 1; case 0: /* child -- finish straight away */ _exit(7); /* exit status = 7 */ default: /* parent */ sleep(10); /* give child time to finish */ } return 0; } /* * The signal handler function -- only gets called when a SIGCHLD * is received, ie when a child terminates */ void sig_chld(int signo) { int status, child_val; /* Wait for any child without blocking */ if (waitpid(-1, &status, WNOHANG) < 0) { /* * calling standard I/O functions like fprintf() in a * signal handler is not recommended, but probably OK * in toy programs like this one. */ fprintf(stderr, "waitpid failed\n"); return; } /* * We now have the info in 'status' and can manipulate it using * the macros in wait.h. */ if (WIFEXITED(status)) /* did child exit normally? */ { child_val = WEXITSTATUS(status); /* get child's exit status */ printf("child's exited normally with status %d\n", child_val); } }
Reading the process table -- SUNOS 4 version
#define _KMEMUSER #include <sys/proc.h> #include <kvm.h> #include <fcntl.h> char regexpstr[256]; #define INIT register char *sp=regexpstr; #define GETC() (*sp++) #define PEEKC() (*sp) #define UNGETC(c) (--sp) #define RETURN(pointer) return(pointer); #define ERROR(val) #include <regexp.h> pid_t getpidbyname(char *name,pid_t skipit) { kvm_t *kd; char **arg; int error; char *p_name=NULL; char expbuf[256]; char **freeme; int curpid; struct user * cur_user; struct user myuser; struct proc * cur_proc; if((kd=kvm_open(NULL,NULL,NULL,O_RDONLY,NULL))==NULL){ return(-1); } sprintf(regexpstr,"^.*/%s$",name); compile(NULL,expbuf,expbuf+256,'\0'); while(cur_proc=kvm_nextproc(kd)){ curpid = cur_proc->p_pid; if((cur_user=kvm_getu(kd,cur_proc))!=NULL){ error=kvm_getcmd(kd,cur_proc,cur_user,&arg,NULL); if(error==-1){ if(cur_user->u_comm[0]!='\0'){ p_name=cur_user->u_comm; } } else{ p_name=arg[0]; } } if(p_name){ if(!strcmp(p_name,name)){ if(error!=-1){ free(arg); } if(skipit!=-1 && ourretval==skipit){ ourretval=-1; } else{ close(fd); break; } break; } else{ if(step(p_name,expbuf)){ if(error!=-1){ free(arg); } break; } } } if(error!=-1){ free(arg); } p_name=NULL; } kvm_close(kd); if(p_name!=NULL){ return(curpid); } return (-1); }
Reading the process table -- SYSV version
pid_t getpidbyname(char *name,pid_t skipit) { DIR *dp; struct dirent *dirp; prpsinfo_t retval; int fd; pid_t ourretval=-1; if((dp=opendir("/proc"))==NULL){ return -1; } chdir("/proc"); while((dirp=readdir(dp))!=NULL){ if(dirp->d_name[0]!='.'){ if((fd=open(dirp->d_name,O_RDONLY))!=-1){ if(ioctl(fd,PIOCPSINFO,&retval)!=-1){ if(!strcmp(retval.pr_fname,name)){ ourretval=(pid_t)atoi(dirp->d_name); if(skipit!=-1 && ourretval==skipit){ ourretval=-1; } else{ close(fd); break; } } } close(fd); } } } closedir(dp); return ourretval; }
Reading the process table -- AIX 4.2 version
#include <stdio.h> #include <procinfo.h> int getprocs(struct procsinfo *, int, struct fdsinfo *, int, pid_t *, int); pid_t getpidbyname(char *name, pid_t *nextPid) { struct procsinfo pi; pid_t retval = (pid_t) -1; pid_t pid; pid = *nextPid; while(1) { if(getprocs(&pi, sizeof pi, 0, 0, &pid, 1) != 1) break; if(!strcmp(name, pi.pi_comm)) { retval = pi.pi_pid; *nextPid = pid; break; } } return retval; } int main(int argc, char *argv[]) { int curArg; pid_t pid; pid_t nextPid; if(argc == 1) { printf("syntax: %s <program> [program ...]\n",argv[0]); exit(1); } for(curArg = 1; curArg < argc; curArg++) { printf("Process IDs for %s\n", argv[curArg]); for(nextPid = 0, pid = 0; pid != -1; ) if((pid = getpidbyname(argv[curArg], &nextPid)) != -1) printf("\t%d\n", pid); } }
Reading the process table using popen and ps
#include <stdio.h> /* FILE, sprintf, fgets, puts */ #include <stdlib.h> /* atoi, exit, EXIT_SUCCESS */ #include <string.h> /* strtok, strcmp */ #include <sys/types.h> /* pid_t */ #include <sys/wait.h> /* WIFEXITED, WEXITSTATUS */ char *procname(pid_t pid) { static char line[133], command[80], *linep, *token, *cmd; FILE *fp; int status; if (0 == pid) return (char *)0; sprintf(command, "ps -p %d 2>/dev/null", pid); fp = popen(command, "r"); if ((FILE *)0 == fp) return (char *)0; /* read the header line */ if ((char *)0 == fgets(line, sizeof line, fp)) { pclose(fp); return (char *)0; } /* figure out where the command name is from the column headings. * (BSD-ish machines put the COMMAND in the 5th column, while SysV * seems to put CMD or COMMAND in the 4th column.) */ for (linep = line; ; linep = (char *)0) { if ((char *)0 == (token = strtok(linep, " \t\n"))) { pclose(fp); return (char *)0; } if (0 == strcmp("COMMAND", token) || 0 == strcmp("CMD", token)) { /* we found the COMMAND column */ cmd = token; break; } } /* read the ps(1) output line */ if ((char *)0 == fgets(line, sizeof line, fp)) { pclose(fp); return (char *)0; } /* grab the "word" underneath the command heading... */ if ((char *)0 == (token = strtok(cmd, " \t\n"))) { pclose(fp); return (char *)0; } status = pclose(fp); if (!WIFEXITED(status) || 0 != WEXITSTATUS(status)) return (char *)0; return token; } int main(int argc, char *argv[]) { puts(procname(atoi(argv[1]))); exit(EXIT_SUCCESS); }
Daemon utility functions
#include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> #include <errno.h> /* closeall() -- close all FDs >= a specified value */ void closeall(int fd) { int fdlimit = sysconf(_SC_OPEN_MAX); while (fd < fdlimit) close(fd++); } /* daemon() - detach process from user and disappear into the background * returns -1 on failure, but you can't do much except exit in that case * since we may already have forked. This is based on the BSD version, * so the caller is responsible for things like the umask, etc. */ /* believed to work on all Posix systems */ int daemon(int nochdir, int noclose) { switch (fork()) { case 0: break; case -1: return -1; default: _exit(0); /* exit the original process */ } if (setsid() < 0) /* shoudn't fail */ return -1; /* dyke out this switch if you want to acquire a control tty in */ /* the future -- not normally advisable for daemons */ switch (fork()) { case 0: break; case -1: return -1; default: _exit(0); } if (!nochdir) chdir("/"); if (!noclose) { closeall(0); open("/dev/null",O_RDWR); dup(0); dup(0); } return 0; } /* fork2() -- like fork, but the new process is immediately orphaned * (won't leave a zombie when it exits) * Returns 1 to the parent, not any meaningful pid. * The parent cannot wait() for the new process (it's unrelated). */ /* This version assumes that you *haven't* caught or ignored SIGCHLD. */ /* If you have, then you should just be using fork() instead anyway. */ int fork2() { pid_t pid; int rc; int status; if (!(pid = fork())) { switch (fork()) { case 0: return 0; case -1: _exit(errno); /* assumes all errnos are <256 */ default: _exit(0); } } if (pid < 0 || waitpid(pid,&status,0) < 0) return -1; if (WIFEXITED(status)) if (WEXITSTATUS(status) == 0) return 1; else errno = WEXITSTATUS(status); else errno = EINTR; /* well, sort of :-) */ return -1; }
An example of using the above functions:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <syslog.h> #include <errno.h> int daemon(int,int); int fork2(void); void closeall(int); #define TCP_PORT 8888 void errexit(const char *str) { syslog(LOG_INFO, "%s failed: %d (%m)", str, errno); exit(1); } void errreport(const char *str) { syslog(LOG_INFO, "%s failed: %d (%m)", str, errno); } /* the actual child process is here. */ void run_child(int sock) { FILE *in = fdopen(sock,"r"); FILE *out = fdopen(sock,"w"); int ch; setvbuf(in, NULL, _IOFBF, 1024); setvbuf(out, NULL, _IOLBF, 1024); while ((ch = fgetc(in)) != EOF) fputc(toupper(ch), out); fclose(out); } /* This is the daemon's main work -- listen for connections and spawn */ void process() { struct sockaddr_in addr; int addrlen = sizeof(addr); int sock = socket(AF_INET, SOCK_STREAM, 0); int flag = 1; int rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); if (rc < 0) errexit("setsockopt"); addr.sin_family = AF_INET; addr.sin_port = htons(TCP_PORT); addr.sin_addr.s_addr = INADDR_ANY; rc = bind(sock, (struct sockaddr *) &addr, addrlen); if (rc < 0) errexit("bind"); rc = listen(sock, 5); if (rc < 0) errexit("listen"); for (;;) { rc = accept(sock, (struct sockaddr *) &addr, &addrlen); if (rc >= 0) switch (fork2()) { case 0: close(sock); run_child(rc); _exit(0); case -1: errreport("fork2"); close(rc); break; default: close(rc); } } } int main() { if (daemon(0,0) < 0) { perror("daemon"); exit(2); } openlog("test", LOG_PID, LOG_DAEMON); process(); return 0; }
Modem handling example
/* issue some simple modem commands * requires the name of a serial device (preferably a dial-out device, * or a non-modem-control device) as its only parameter. * If you don't have functional dial-out devices, then move CLOCAL * to CFLAGS_TO_SET instead. */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/time.h> #include <sys/ioctl.h> /* maybe; system-dependent */ #include <termios.h> #include <errno.h> #include <string.h> #include <ctype.h> #define CFLAGS_TO_SET (CREAD | HUPCL) #define CFLAGS_TO_CLEAR (CSTOPB | PARENB | CLOCAL) enum flowmode { NoFlow, HardFlow, SoftFlow }; /* system-dependent */ #define CFLAGS_HARDFLOW (CRTSCTS) #define EXAMPLE_BAUD B19200 #define EXAMPLE_FLOW HardFlow static void die(const char *msg) { fprintf(stderr, "%s\n", msg); exit(1); } static int close_and_complain(int fd, const char *msg, int err) { fprintf(stderr, "%s: %s\n", msg, strerror(err)); if (fd >= 0) close(fd); errno = err; return -1; } int open_port(const char *name, speed_t baud, enum flowmode flow) { int flags; struct termios attr; int fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY); if (fd < 0) return close_and_complain(-1, "open", errno); /* set vaguely sensibe settings */ if (tcgetattr(fd, &attr) < 0) return close_and_complain(fd, "tcgetattr", errno); /* no special input or output processing */ attr.c_iflag = (flow == SoftFlow) ? (IXON | IXOFF) : 0; attr.c_oflag = 0; /* set 8-bit character size and miscellanous control modes */ attr.c_cflag &= ~(CSIZE | CFLAGS_TO_CLEAR | CFLAGS_HARDFLOW); attr.c_cflag |= (CS8 | CFLAGS_TO_SET); if (flow == HardFlow) attr.c_cflag |= CFLAGS_HARDFLOW; /* local modes */ attr.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ISIG); /* special characters -- most disabled by prior settings anyway */ { int i; #ifdef _POSIX_VDISABLE attr.c_cc[0] = _POSIX_VDISABLE; #else attr.c_cc[0] = fpathconf(fd, _PC_VDISABLE); #endif for (i = 1; i < NCCS; i++) attr.c_cc[i] = attr.c_cc[0]; } attr.c_cc[VSTART] = 0x11; attr.c_cc[VSTOP] = 0x13; /* timing controls for read() */ attr.c_cc[VMIN] = 1; attr.c_cc[VTIME] = 0; /* baud rate */ cfsetispeed(&attr, baud); cfsetospeed(&attr, baud); /* write settings */ if (tcsetattr(fd, TCSANOW, &attr) < 0) return close_and_complain(fd, "tcsetattr", errno); /* turn off O_NONBLOCK if the device remembered it */ flags = fcntl(fd, F_GETFL, 0); if (flags < 0) return close_and_complain(fd, "fcntl(GETFL)", errno); if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) return close_and_complain(fd, "fcntl(SETFL)", errno); return fd; } /* some simple timing utilities */ /* add SECS and USECS to *TV */ static void timeradd(struct timeval *tv, long secs, long usecs) { tv->tv_sec += secs; if ((tv->tv_usec += usecs) >= 1000000) { tv->tv_sec += tv->tv_usec / 1000000; tv->tv_usec %= 1000000; } } /* Set *RES = *A - *B, returning the sign of the result */ static int timersub(struct timeval *res, const struct timeval *a, const struct timeval *b) { long sec = a->tv_sec - b->tv_sec; long usec = a->tv_usec - b->tv_usec; if (usec < 0) usec += 1000000, --sec; res->tv_sec = sec; res->tv_usec = usec; return (sec < 0) ? (-1) : ((sec == 0 && usec == 0) ? 0 : 1); } /* this doesn't try and cope with pathological strings (e.g. ababc) * timeout is in millisecs * A more usual approach to this is to use alarm() for the timeout. * This example avoids signal handling for simplicity and to illustrate * an alternative approach */ int expect(int fd, const char *str, int timeo) { int matchlen = 0; int len = strlen(str); struct timeval now,end,left; fd_set fds; char c; gettimeofday(&end, NULL); timeradd(&end, timeo/1000, timeo%1000); while (matchlen < len) { gettimeofday(&now, NULL); if (timersub(&left, &end, &now) <= 0) return -1; FD_ZERO(&fds); FD_SET(fd, &fds); if (select(fd+1, &fds, NULL, NULL, &left) <= 0) return -1; if (read(fd, &c, 1) != 1) return -1; if (isprint((unsigned char)c) || c == '\n' || c == '\r') putchar(c); else printf("\\x%02x", c); if (c == str[matchlen]) ++matchlen; else matchlen = 0; } return 0; } int main(int argc, char **argv) { int fd; unsigned char c; if (argc < 2) die("no port specified"); setvbuf(stdout, NULL, _IONBF, 0); fd = open_port(argv[1], EXAMPLE_BAUD, EXAMPLE_FLOW); if (fd < 0) die("cannot open port"); write(fd, "AT\r", 3); if (expect(fd, "OK", 5000) < 0) { write(fd, "AT\r", 3); if (expect(fd, "OK", 5000) < 0) { tcflush(fd, TCIOFLUSH); close(fd); die("no response to AT"); } } write(fd, "ATI4\r", 5); expect(fd, "OK", 10000); putchar('\n'); tcflush(fd, TCIOFLUSH); close(fd); return 0; }
Job Control example
/* functions to spawn foreground/background jobs */ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> #include <errno.h> /* Some of the functions below fail if the control tty can't be * located, or if the calling process isn't in the foreground. In the * first case, we are assuming that a foreground process will have the * ctty open on either stdin, stdout or stderr, and we return ENOTTY * if it isn't. In the second case, we return EPERM if a * non-foreground process attempts to put something into the * foreground (probably overly paranoid) except for the special case * of foreground_self(). */ /* assign the terminal (open on ctty) to a specific pgrp. This wrapper * around tcsetpgrp() is needed only because of extreme bogosity on the * part of POSIX; conforming systems deliver STGTTOU if tcsetpgrp is * called from a non-foreground process (which it almost invariably is). * A triumph of spurious consistency over common sense. */ int assign_terminal(int ctty, pid_t pgrp) { sigset_t sigs; sigset_t oldsigs; int rc; sigemptyset(&sigs); sigaddset(&sigs,SIGTTOU); sigprocmask(SIG_BLOCK, &sigs, &oldsigs); rc = tcsetpgrp(ctty, pgrp); sigprocmask(SIG_SETMASK, &oldsigs, NULL); return rc; } /* Like fork(), but does job control. FG is true if the newly-created * process is to be placed in the foreground. (This implicitly puts * the calling process in the background, so watch out for tty I/O * after doing this.) PGRP is -1 to create a new job, in which case * the returned pid is also the pgrp of the new job, or specifies an * existing job in the same session (normally used only for starting * second or subsequent process in a pipeline). */ pid_t spawn_job(int fg, pid_t pgrp) { int ctty = -1; pid_t pid; /* If spawning a *new* foreground job, require that at least one * of stdin, stdout or stderr refer to the control tty, and that * the current process is in the foreground. * Only check for controlling tty if starting a new foreground * process in an existing job. * A session without a control tty can only have background jobs */ if (fg) { pid_t curpgrp; if ((curpgrp = tcgetpgrp(ctty = 2)) < 0 && (curpgrp = tcgetpgrp(ctty = 0)) < 0 && (curpgrp = tcgetpgrp(ctty = 1)) < 0) return errno = ENOTTY, (pid_t)-1; if (pgrp < 0 && curpgrp != getpgrp()) return errno = EPERM, (pid_t)-1; } switch (pid = fork()) { case -1: /* fork failure */ return pid; case 0: /* child */ /* establish new process group, and put ourselves in * foreground if necessary * unclear what to do if setpgid fails ("can't happen") */ if (pgrp < 0) pgrp = getpid(); if (setpgid(0,pgrp) == 0 && fg) assign_terminal(ctty, pgrp); return 0; default: /* parent */ /* establish child process group here too. */ if (pgrp < 0) pgrp = pid; setpgid(pid, pgrp); return pid; } /*NOTREACHED*/ } /* Kill job PGRP with signal SIGNO */ int kill_job(pid_t pgrp, int signo) { return kill(-pgrp, signo); } /* Suspend job PGRP */ int suspend_job(pid_t pgrp) { return kill_job(pgrp, SIGSTOP); } /* Resume job PGRP in background */ int resume_job_bg(pid_t pgrp) { return kill_job(pgrp, SIGCONT); } /* resume job PGRP in foreground */ int resume_job_fg(pid_t pgrp) { pid_t curpgrp; int ctty; if ((curpgrp = tcgetpgrp(ctty = 2)) < 0 && (curpgrp = tcgetpgrp(ctty = 0)) < 0 && (curpgrp = tcgetpgrp(ctty = 1)) < 0) return errno = ENOTTY, (pid_t)-1; if (curpgrp != getpgrp()) return errno = EPERM, (pid_t)-1; if (assign_terminal(ctty, pgrp) < 0) return -1; return kill_job(pgrp, SIGCONT); } /* put ourselves in the foreground, e.g. after suspending a foreground * job */ int foreground_self() { pid_t curpgrp; int ctty; if ((curpgrp = tcgetpgrp(ctty = 2)) < 0 && (curpgrp = tcgetpgrp(ctty = 0)) < 0 && (curpgrp = tcgetpgrp(ctty = 1)) < 0) return errno = ENOTTY, (pid_t)-1; return assign_terminal(ctty, getpgrp()); } /* closeall() - close all FDs >= a specified value */ void closeall(int fd) { int fdlimit = sysconf(_SC_OPEN_MAX); while (fd < fdlimit) close(fd++); } /* like system(), but executes the specified command as a background * job, returning the pid of the shell process (which is also the pgrp * of the job, suitable for kill_job etc.) * If INFD, OUTFD or ERRFD are non-NULL, then a pipe will be opened and * a descriptor for the parent end of the relevent pipe stored there. * If any of these are NULL, they will be redirected to /dev/null in the * child. * Also closes all FDs > 2 in the child process (an oft-overlooked task) */ pid_t spawn_background_command(const char *cmd, int *infd, int *outfd, int *errfd) { int nullfd = -1; int pipefds[3][2]; int error = 0; if (!cmd) return errno = EINVAL, -1; pipefds[0][0] = pipefds[0][1] = -1; pipefds[1][0] = pipefds[1][1] = -1; pipefds[2][0] = pipefds[2][1] = -1; if (infd && pipe(pipefds[0]) < 0) error = errno; else if (outfd && pipe(pipefds[1]) < 0) error = errno; else if (errfd && pipe(pipefds[2]) < 0) error = errno; if (!error && !(infd && outfd && errfd)) { nullfd = open("/dev/null",O_RDWR); if (nullfd < 0) error = errno; } if (!error) { pid_t pid = spawn_job(0, -1); switch (pid) { case -1: /* fork failure */ error = errno; break; case 0: /* child proc */ dup2(infd ? pipefds[0][0] : nullfd, 0); dup2(outfd ? pipefds[1][1] : nullfd, 1); dup2(errfd ? pipefds[2][1] : nullfd, 2); closeall(3); execl("/bin/sh","sh","-c",cmd,(char*)NULL); _exit(127); default: /* parent proc */ close(nullfd); if (infd) close(pipefds[0][0]), *infd = pipefds[0][1]; if (outfd) close(pipefds[1][1]), *outfd = pipefds[1][0]; if (errfd) close(pipefds[2][1]), *errfd = pipefds[2][0]; return pid; } } /* only reached if error */ { int i,j; for (i = 0; i < 3; ++i) for (j = 0; j < 2; ++j) if (pipefds[i][j] >= 0) close(pipefds[i][j]); } if (nullfd >= 0) close(nullfd); return errno = error, (pid_t) -1; } /*--------------------------------------------------------------------*/ /* This bit is a somewhat trivial example of using the above. */ pid_t bgjob = -1; volatile int signo = 0; #ifndef WCOREDUMP /* If WCOREDUMP is missing, you might want to supply a correct * definition for your platform (this is usually (status & 0x80) but * not always) or punt (as in this example) by assuming no core dumps. */ # define WCOREDUMP(status) (0) #endif int check_children() { pid_t pid; int status; int count = 0; while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { if (pid == bgjob && !WIFSTOPPED(status)) bgjob = -1; ++count; if (WIFEXITED(status)) fprintf(stderr,"Process %ld exited with return code %d\n", (long)pid, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) fprintf(stderr,"Process %ld killed by signal %d%s\n", (long)pid, WTERMSIG(status), WCOREDUMP(status) ? " (core dumped)" : ""); else if (WIFSTOPPED(status)) fprintf(stderr,"Process %ld stopped by signal %d\n", (long)pid, WSTOPSIG(status)); else fprintf(stderr,"Unexpected status - pid=%ld, status=0x%x\n", (long)pid, status); } return count; } void sighandler(int sig) { if (sig != SIGCHLD) signo = sig; } int main() { struct sigaction act; int sigcount = 0; act.sa_handler = sighandler; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGINT,&act,NULL); sigaction(SIGQUIT,&act,NULL); sigaction(SIGTERM,&act,NULL); sigaction(SIGTSTP,&act,NULL); sigaction(SIGCHLD,&act,NULL); fprintf(stderr,"Starting background job 'sleep 60'\n"); bgjob = spawn_background_command("sleep 60", NULL, NULL, NULL); if (bgjob < 0) { perror("spawn_background_command"); exit(1); } fprintf(stderr,"Background job started with id %ld\n", (long)bgjob); while (bgjob >= 0) { if (signo) { fprintf(stderr,"Signal %d caught\n", signo); if (sigcount++) kill_job(bgjob, SIGKILL); else { kill_job(bgjob, SIGTERM); kill_job(bgjob, SIGCONT); } } if (!check_children()) pause(); } fprintf(stderr,"Done - exiting\n"); return 0; }