[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Chicken-users] Interaction of subprocess and condition handling
From: |
Lassi Kortela |
Subject: |
Re: [Chicken-users] Interaction of subprocess and condition handling |
Date: |
Sat, 20 Jul 2019 01:13:53 +0300 |
User-agent: |
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:60.0) Gecko/20100101 Thunderbird/60.8.0 |
I think you'd basically have to replicate the following C traditional
programming technique in whatever mixture of Scheme and C is appropriate
for Chicken. So when you run "./spawn date" it succeeds and prints
output from the standard Unix "date" utility. But when you run "./spawn
nonexistent" it writes an error message to stderr (the child process
does the writing since it has access to the precise errno value after
execve() whereas the parent does not, so the child can show a more
precise error message). And the exit code from the parent is the nonzero
code 1 to indicate error.
Obviously in Chicken you would not exit the parent, but you'd exit the
child with code 126 as here. In Chicken you'd probably want some fancy
way to propagate the errno value from the child to the parent after a
failed execve(). It's unwise to do something like _exit(errno); One
solution would be to open a close-on-exec pipe from parent to child at a
known fd number and pass the errno value via the pipe. The parent would
probably have to poll() or select() on the pipe to check for error, but
I haven't thought this through.
Source code, also downloadable at <https://misc.lassi.io/2019/spawn.c>:
----------------------------------------------------------------------
// The child process uses the magic exit code 126 to indicate to the
// parent process that execve() failed. You could use another code,
// but IIRC this is the traditional choice.
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void warn(const char *msg) {
fprintf(stderr, "%s\n", msg);
}
static void warnsys(const char *msg) {
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
}
static void diesys(const char *msg) {
warnsys(msg);
exit(1);
}
static void die(const char *msg) {
warn(msg);
exit(1);
}
static void spawn(char **argv) {
pid_t child;
int status;
if ((child = fork()) == -1) {
diesys("cannot fork");
}
if (!child) {
// We are in the new child process.
execvp(argv[0], argv);
warnsys("cannot exec child process");
_exit(126);
}
// We are in the old parent process.
if (waitpid(child, &status, 0) == -1) {
diesys("cannot wait for child process");
}
if (!WIFEXITED(status)) {
die("child process did not exit normally");
}
switch (WEXITSTATUS(status)) {
case 0:
// Success, all good.
break;
case 126:
// Exec failed. Child already printed error message. Since
// this we are the parent, we stay silent -- there's no need
// to write a duplicate error message. But propagate the error
// to the process that started _us_ by exiting with a nonzero
// code anyway.
exit(1);
default:
// Some other error.
fprintf(stderr, "Child exited with code %d\n",
(int)WEXITSTATUS(status));
break;
}
}
int main(int argc, char **argv) {
if (argc < 2) {
die("usage: spawn command [args...]");
}
spawn(&argv[1]);
return 0;
}