shj@login.dkuug.dk (Stig Jacobsen) (03/01/91)
When my application desires to spawn off a background process, I use something roughly like this: int spawn(char *path, ...) { if (fork() == 0) execlp(path, NULL); } This is fine if the exec..() call goes well. However, if the exec() call fails, the error is not reported back to the parent. I get a SIGCLD of course, but what I'd really like is that my spawn() function just returns an error, if the exec() call fails. So far the best solution that I've come up with is, int spawn(char *path, ...) { if (access(path, R_OK|X_OK) != 0) return -1; if (fork() == 0) execlp(path, NULL); } But this is not an elegant solution, nor is it entirely safe: If someone unlinks 'path' between the access() and the exec() call (not _that_ unlikely if the system is heavily loaded), I won't detect the error anyways! So - what does everybody else do? Am I overseeing something totally obvious? -- Stig Jacobsen shj@login.dkuug.dk / sysadm
rosenqui@crc.skl.dnd.ca (Eric Rosenquist) (03/02/91)
In article <shj.667793966@dkuugin> shj@login.dkuug.dk (Stig Jacobsen) writes: >When my application desires to spawn off a background process, >I use something roughly like this: > >int spawn(char *path, ...) >{ > > if (fork() == 0) > execlp(path, NULL); > >} > >This is fine if the exec..() call goes well. However, if the exec() >call fails, the error is not reported back to the parent. I get >a SIGCLD of course, but what I'd really like is that my spawn() >function just returns an error, if the exec() call fails. ... > ... It's too late once the fork() has completed. What you need to do is have the child exit in a particular way that your SIGCLD handler looks for if the exec() fails. In your code snippet the child keeps executing! if (fork() == 0) { execlp(path, NULL); /* if you get here, the exec() failed */ exit(SOME_STATUS_CODE); /* or */ abort() or kill(...) etc. } Eric @ SKL ------------- Eric.Rosenquist@crc.skl.dnd.ca Software Kinetics Limited 65 Iber Road, Stittsville, Ontario Canada - K2S 1E7 Phone (613) 831-0888
mike@bria (03/02/91)
In an article, login.dkuug.dk!shj (Stig Jacobsen) writes: |When my application desires to spawn off a background process, |I use something roughly like this: [ example deleted ] |This is fine if the exec..() call goes well. However, if the exec() |call fails, the error is not reported back to the parent. I get |a SIGCLD of course, but what I'd really like is that my spawn() |function just returns an error, if the exec() call fails. So far |the best solution that I've come up with is, | |int spawn(char *path, ...) |{ | | if (access(path, R_OK|X_OK) != 0) | return -1; | if (fork() == 0) | execlp(path, NULL); | |} | |But this is not an elegant solution, nor is it entirely safe: |If someone unlinks 'path' between the access() and the exec() |call (not _that_ unlikely if the system is heavily loaded), |I won't detect the error anyways! | |So - what does everybody else do? Am I overseeing something |totally obvious? Some time ago, I wrote a spawn function (but then I got religion, and only use fork/exec explicitly :-) The only caveat is if the spawned program returns an exit status of 255, it will look like the spawn failed (this was never a big issue for me, so I left it). Here goes: #include <stdio.h> main(argc,argv) int argc; char *argv[]; { printf("spawn returned %d\n",spawn(argv[1],&argv[1])); return(0); } spawn(path,argv) char *path, *argv[]; { int ret, xit; if ( (ret = fork()) > 0 ) { if ( wait(&xit) == -1 ) return(-1); } else if ( ret == 0 ) { if ( execvp(path,argv) == -1 ) exit(-1); } else return(-1); return(xit >> 8); } -- Michael Stefanik, MGI Inc., Los Angeles| Opinions stated are not even my own. Title of the week: Systems Engineer | UUCP: ...!uunet!bria!mike ------------------------------------------------------------------------------- Remember folks: If you can't flame MS-DOS, then what _can_ you flame?
denap@alta.sw.stratus.com (Tom DeNapoli) (03/04/91)
>>>>> On 1 Mar 91 20:59:44 GMT, rosenqui@crc.skl.dnd.ca (Eric Rosenquist) said:
[original post deleted]
rosenqui> It's too late once the fork() has completed. What you need to do is
rosenqui> have the child exit in a particular way that your SIGCLD handler looks
rosenqui> for if the exec() fails. In your code snippet the child keeps
rosenqui> executing!
rosenqui> if (fork() == 0) {
rosenqui> execlp(path, NULL);
rosenqui> /* if you get here, the exec() failed */
rosenqui> exit(SOME_STATUS_CODE);
rosenqui> /* or */
rosenqui> abort() or kill(...) etc.
rosenqui> }
along the same lines... prior to exiting the child could use a
write back to the parent through a pipe with more details of the
failure.
rosenqui> Eric @ SKL
rosenqui> -------------
rosenqui> Eric.Rosenquist@crc.skl.dnd.ca
rosenqui> Software Kinetics Limited
rosenqui> 65 Iber Road, Stittsville, Ontario
rosenqui> Canada - K2S 1E7 Phone (613) 831-0888
--
Tom DeNapoli | Stratus Computer, Inc.
denap@alta.sw.stratus.com | 55 Fairbanks Blvd M23EN3
uunet!lectroid!alta!denap | Marlboro, MA 01752
maart@nat.vu.nl (Maarten Litmaath) (03/05/91)
In article <1991Mar1.205944.13198@crc.skl.dnd.ca>, rosenqui@crc.skl.dnd.ca (Eric Rosenquist) writes: >In article <shj.667793966@dkuugin> shj@login.dkuug.dk (Stig Jacobsen) writes: >>When my application desires to spawn off a background process, >>I use something roughly like this: >> >>int spawn(char *path, ...) >>{ >> >> if (fork() == 0) >> execlp(path, NULL); >> >>} >> >>This is fine if the exec..() call goes well. However, if the exec() >>call fails, the error is not reported back to the parent. I get >>a SIGCLD of course, but what I'd really like is that my spawn() >>function just returns an error, if the exec() call fails. ... >> ... > >It's too late once the fork() has completed. [...] Wrong. The following example shows how it can be done. (I used execvp() instead of execlp().) ------------------------------------------------------------ /* * Compile with `-DNO_FCNTL' if your UNIX variant doesn't have fcntl(2). * Compile with `-DNO_STRERROR' if it doesn't have strerror(3). */ #include <stdio.h> #ifdef NO_FCNTL #include <sys/ioctl.h> #else #include <fcntl.h> #endif /* NO_FCNTL */ main(argc, argv) int argc; char **argv; { int err, spawn(); char *strerror(); if (argc == 1) { fprintf(stderr, "Usage: %s command args\n", argv[0]); exit(1); } err = spawn(&argv[1]); switch (err) { case -1: perror("pipe"); break; case -2: perror("fork"); break; default: printf("The execvp() in the child %s.\n", err == 0 ? "succeeded" : "failed"); if (err != 0) printf("The reason was: %s.\n", strerror(err)); } } int spawn(argv) char **argv; { extern int errno; int pp[2], n, err; if (pipe(pp) < 0) return -1; switch (fork()) { case -1: return -2; case 0: close(pp[0]); /* set close-on-exec flag */ #ifdef NO_FCNTL ioctl(pp[1], FIOCLEX, (int *) 0); #else fcntl(pp[1], F_SETFD, 1); #endif /* NO_FCNTL */ execvp(*argv, argv); /* send a message indicating the failure */ write(pp[1], (char *) &errno, sizeof errno); _exit(1); } close(pp[1]); n = read(pp[0], (char *) &err, sizeof err); close(pp[0]); return n == 0 ? 0 : err; } #ifdef NO_STRERROR char *strerror(n) int n; { extern int errno, sys_nerr; extern char *sys_errlist[]; static char buf[32]; if ((unsigned) n < sys_nerr) return sys_errlist[n]; sprintf(buf, "Unknown error %d", n); return buf; } #endif /* NO_STRERROR */
shj@login.dkuug.dk (Stig Jacobsen) (03/06/91)
maart@nat.vu.nl (Maarten Litmaath) writes: > /* set close-on-exec flag */ >#ifdef NO_FCNTL > ioctl(pp[1], FIOCLEX, (int *) 0); >#else > fcntl(pp[1], F_SETFD, 1); >#endif /* NO_FCNTL */ ... > n = read(pp[0], (char *) &err, sizeof err); > close(pp[0]); > return n == 0 ? 0 : err; Yeah! It was something like this that I wanted, where I get no ugly messages splattering over the parents screen and where I get an error return from the spawn call and not via some signal. A big thanks to you and to everybody else who answered here and in mail. -- Stig Jacobsen shj@login.dkuug.dk / sysadm
maart@nat.vu.nl (Maarten Litmaath) (03/08/91)
In article <shj.668195000@dkuugin>, shj@login.dkuug.dk (Stig Jacobsen) writes: >[...] >Yeah! It was something like this that I wanted, where I get no >ugly messages splattering over the parents screen and where I get >an error return from the spawn call and not via some signal. [...] One thing I forgot to mention: don't forget to wait() for the child, _even_ when the spawn() has failed! If your UNIX variant has wait4(2), waitfor(2) or waitpid(2), you can let spawn() itself take care of it in case the execvp() has failed. Otherwise things must be handled by a routine that takes care of all children, if any. The interface might be changed to: int spawn(argv, pidp) char **argv int *pidp; ..and spawn() would return the PID in `*pidp'. Do _not_ use the simple wait(2) system call in spawn(), as that may reap other children as well... The other forms of wait() weren't invented for nothing.
libes@cme.nist.gov (Don Libes) (03/08/91)
In article <1991Mar7.222033.4711@nat.vu.nl> maart@nat.vu.nl (Maarten Litmaath) writes: > shj@login.dkuug.dk (Stig Jacobsen) writes: >>Yeah! It was something like this that I wanted, where I get no >>ugly messages splattering over the parents screen and where I get >>an error return from the spawn call and not via some signal. [...] >The interface might be changed to: > int spawn(argv, pidp) > char **argv > int *pidp; >..and spawn() would return the PID in `*pidp'. If you have 'expect' (as of 1/91), you can use the library that comes with it from C or C++. The following functions exist: int exp_spawnl(file, arg0 [, arg1, ..., argn] (char *)0); char *file; char *arg0, *arg1, ... *argn; int exp_spawnv(file,argv); char *file, *argv[ ]; extern int exp_pid; Both of these fork a new process so that stdin, stdout, and stderr can be written and read by the current process. A file descriptor is returned which corresponds to the process's stdin, stdout, and stderr. the new process. exp_pid is set to the pid of the new process. An additional interface exists, styled after popen: FILE * exp_popen(command); char *command; In all of these, exec errors are returned by reading output of the new process (as the original poster requested). The library has a number of other related I/O functions to control interactive processes from C. See the man page for more info. Don Libes libes@cme.nist.gov ...!uunet!cme-durer!libes
shj@login.dkuug.dk (Stig Jacobsen) (03/08/91)
maart@nat.vu.nl (Maarten Litmaath) writes: >One thing I forgot to mention: don't forget to wait() for the child, >_even_ when the spawn() has failed! Since I didn't want to wait() for anything, I just performed another fork() in the spawn() function and then let the first child exit() imediately, so that the second child would get init (process 1) as parent. This way I avoid a zombie process. Rather wastefull, yes, but I couldn't find another way to disassociate my child process from the parent. >If your UNIX variant has wait4(2), waitfor(2) or waitpid(2), It does not; its a lowly System V r3.2. -- Stig Jacobsen shj@login.dkuug.dk / sysadm
maart@nat.vu.nl (Maarten Litmaath) (03/12/91)
In article <shj.668447405@dkuugin>, shj@login.dkuug.dk (Stig Jacobsen) writes: >[...] >Since I didn't want to wait() for anything, I just performed >another fork() in the spawn() function and then let the first >child exit() imediately, so that the second child would get init >(process 1) as parent. This way I avoid a zombie process. > >Rather wastefull, yes, but I couldn't find another way to >disassociate my child process from the parent. This is quite a normal (portable!) way to dispose of child processes. Right, I've composed a general spawn() example, included below. Note that the calling syntax and semantics have changed. Enjoy! --------------------cut here-------------------- /* * spawn.c * * Usage: * * char *command, *argv[]; * int status, *pidp, *errp, (*preamble)(); * * status = spawn(command, argv, pidp, errp, preamble); * * Spawn `command' with arguments `argv' (argv[0] is the name); * the `argv' array ends with a NULL pointer. * * If `preamble' is not a NULL pointer, the addressed function will be * called in the child (before the execvp(), of course) with arguments * `command' and `argv'. This function may set some signals to be ignored, * reset the process group, redirect input, etc. Only if it returns 0 the * call to execvp() will be made; else spawn() will return -3 and if `errp' * is not a NULL pointer the return value of `preamble' will be copied into * `*errp'. * * The process ID of the child is copied into `*pidp', unless `pidp' is a * NULL pointer. * * The return value of spawn() itself is 0 on success, -1 if it could not * create the pipe that it needs, -2 if it could not fork(), -3 if `preamble' * was nonnull and did not return 0, and -4 if the execvp() failed. * * In the last case the errno value indicating why the execvp() failed * is copied into `*errp', unless `errp' is a NULL pointer. * * Compile with `-DNO_FCNTL' if your UNIX variant doesn't have fcntl(2). * Compile with `-DNO_STRERROR' if it doesn't have strerror(3). * * Author: Maarten Litmaath @ Dept. of Physics, Vrije University Amsterdam * Email: maart@nat.vu.nl * Date: 11 Mar 91 */ #include <stdio.h> #ifdef NO_FCNTL #include <sys/ioctl.h> #else #include <fcntl.h> #endif /* NO_FCNTL */ main(argc, argv) int argc; char **argv; { int status, spawn(), pid, error, preamble(); char *strerror(); if (argc == 1) { fprintf(stderr, "Usage: %s command args\n", argv[0]); exit(1); } /* start background job */ status = spawn(argv[1], &argv[1], &pid, &error, preamble); switch (status) { case 0: printf("The execvp() in the child succeeded.\n"); printf("The child has process ID %d.\n", pid); break; case -1: perror("pipe"); break; case -2: perror("fork"); break; case -3: printf("The preamble function returned %d.\n", error); break; case -4: printf("The execvp() in the child failed.\n"); printf("The reason was: %s.\n", strerror(error)); break; default: printf("Values of %d will give rise to dom!\n", status); /* pseudo V6 */ break; } return status; } int spawn(cmd, argv, pidp, errp, preamble) char *cmd, **argv; int *pidp, *errp, (*preamble)(); { extern int errno; int pp[2], n, pid; struct error { int status, errno; } err; if (pipe(pp) < 0) return -1; switch (pid = fork()) { case -1: close(pp[0]); close(pp[1]); return -2; case 0: close(pp[0]); /* set close-on-exec flag */ #ifdef NO_FCNTL ioctl(pp[1], FIOCLEX, (int *) 0); #else fcntl(pp[1], F_SETFD, 1); #endif /* NO_FCNTL */ if (preamble && (err.errno = (*preamble)(cmd, argv)) != 0) err.status = -3; else { execvp(cmd, argv); err.errno = errno; err.status = -4; } /* send a message indicating the failure */ write(pp[1], (char *) &err, sizeof err); _exit(1); } if (pidp) *pidp = pid; close(pp[1]); n = read(pp[0], (char *) &err, sizeof err); close(pp[0]); if (n != 0 && errp) *errp = err.errno; return n == 0 ? 0 : err.status; } #include <signal.h> static int Signals[] = { SIGHUP, /* ignore SIGHUP (on logout or hangup) */ SIGINT, /* ignore interrupts */ SIGQUIT, /* ignore quits */ #ifdef SIGTSTP SIGTSTP, /* ignore keyboard stop signals */ #endif /* SIGTSTP */ 0 }; /* ARGSUSED */ int preamble(cmd, argv) char *cmd, **argv; { int *sigp; for (sigp = Signals; *sigp; sigp++) signal(*sigp, SIG_IGN); return 0; } #ifdef NO_STRERROR char *strerror(n) int n; { extern int errno, sys_nerr; extern char *sys_errlist[]; static char buf[32]; if ((unsigned) n < sys_nerr) return sys_errlist[n]; sprintf(buf, "Unknown error %d", n); return buf; } #endif /* NO_STRERROR */