+#define _GNU_SOURCE
#include "secnet.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
+#include <string.h>
#include "process.h"
-/* Advice about children from Peter:
-Better way: before the fork, make a pipe. In the child close the
-+reading end. Make the writing end close-on-exec. If the dup2 or exec fails,
-+write the errno value. In the parent, close the writing end. Now you can read
-+from it. If you get an errno value from the pipe, the process failed and you
-+know why. If you get EOF, the exec succeeded.
-
-<Senji> So, close on exec only closes if exec isn't going to return then?
-<Diziet> qu: I wouldn't bother with all that with pipes. Remember that the
-+runtime system can still make exec fail when it's `too late'.
-<Senji> Diz - I would rather have a coherant error message than 'child failed'
-<Diziet> The child, if it fails to exec, should print a message to stderr
-+(giving errno and what it was trying to execute, most likely), and exit
-+nonzero.
-<Diziet> It should exit calling _exit.
-*/
-
/* Process handling - subprocesses, signals, etc. */
static bool_t signal_handling=False;
int sys_cmd(const char *path, const char *arg, ...)
{
va_list ap;
- int rv;
+ int rv, rc;
pid_t c;
- va_start(ap,arg);
c=fork();
if (c) {
/* Parent -> wait for child */
- waitpid(c,&rv,0);
+ do {
+ rc = waitpid(c,&rv,0);
+ } while(rc < 0 && errno == EINTR);
+ if (rc < 0)
+ fatal_perror("sys_cmd: waitpid for %s", path);
+ if (rc != c) /* OS has gone mad */
+ fatal("sys_cmd: waitpid for %s returned wrong process ID!",
+ path);
+ if (rv) {
+ /* If the command failed reporting its exit status */
+ if (WIFEXITED(rv))
+ Message(M_ERR, "sys_cmd(%s,%s,...) exited with status %d\n",
+ path, arg, WEXITSTATUS(rv));
+ else if(WIFSIGNALED(rv))
+ Message(M_ERR, "sys_cmd(%s,%s,...) exited with signal %d (%s)%s\n",
+ path, arg, WTERMSIG(rv), strsignal(WTERMSIG(rv)),
+ WCOREDUMP(rv) ? " - core dumped" : "");
+ else
+ Message(M_ERR, "sys_cmd(%s,%s,...) exited with wstat %#x\n",
+ path, arg, rv);
+ }
} else if (c==0) {
char *args[100];
int i;
if the execvp() fails this seems somewhat pointless, and
increases the chance of the child process failing before it
gets to exec(). */
- args[0]=(char *)arg;
+ va_start(ap,arg);
+ args[0]=(char *)arg; /* program name */
i=1;
while ((args[i++]=va_arg(ap,char *)));
execvp(path,args);
- exit(1);
+ fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno));
+ _exit(1);
} else {
/* Error */
- fatal_perror("sys_cmd(%s,%s,...)");
+ fatal_perror("sys_cmd(%s,%s,...)", path, arg);
}
- va_end(ap);
return rv;
}
return 0;
}
+/* Bodge to work around Ubuntu's strict header files */
+static void discard(int anything) {}
+
static afterpoll_fn signal_afterpoll;
static void signal_afterpoll(void *st, struct pollfd *fds, int nfds)
{
sigset_t todo,old;
if (nfds && (fds->revents & POLLIN)) {
- read(spr,buf,16); /* We don't actually care what we read; as
+ discard(read(spr,buf,16));
+ /* We don't actually care what we read; as
long as there was at least one byte
(which there was) we'll pick up the
signals in the pending set */
will be atomic, and it seems to be the lesser of the two
evils. */
saved_errno=errno;
- write(spw,&thing,1); /* We don't care if this fails (i.e. the pipe
+ discard(write(spw,&thing,1));
+ /* We don't care if this fails (i.e. the pipe
is full) because the service routine will
spot the pending signal anyway */
errno=saved_errno;
sigemptyset(®istered);
sigemptyset(&pending);
- if (pipe(p)!=0) {
- fatal_perror("start_signal_handling: pipe");
- }
+ pipe_cloexec(p);
spw=p[1];
spr=p[0];
if (fcntl(spw, F_SETFL, fcntl(spw, F_GETFL)|O_NONBLOCK)==-1) {