#include "cvs.h"
#ifndef HAVE_UNISTD_H
extern int execvp (char *file, char **argv);
#endif
static char **run_argv;
static int run_argc;
static size_t run_arg_allocated;
void
run_arg_free_p (int argc, char **argv)
{
int i;
for (i = 0; i < argc; i++)
free (argv[i]);
}
void
run_setup (const char *prog)
{
char *run_prog;
char *buf, *d, *s;
size_t length;
size_t doff;
char inquotes;
int dolastarg;
run_arg_free_p (run_argc, run_argv);
run_argc = 0;
run_prog = xstrdup (prog);
s = run_prog;
d = buf = NULL;
length = 0;
dolastarg = 1;
inquotes = '\0';
doff = d - buf;
expand_string(&buf, &length, doff + 1);
d = buf + doff;
while ((*d = *s++) != '\0')
{
switch (*d)
{
case '\\':
if (*s) *d = *s++;
d++;
break;
case '"':
case '\'':
if (inquotes == *d) inquotes = '\0';
else inquotes = *d;
break;
case ' ':
case '\t':
if (inquotes) d++;
else
{
*d = '\0';
run_add_arg (buf);
d = buf;
while (isspace(*s)) s++;
if (!*s) dolastarg = 0;
}
break;
default:
d++;
break;
}
doff = d - buf;
expand_string(&buf, &length, doff + 1);
d = buf + doff;
}
if (dolastarg) run_add_arg (buf);
if (buf) free (buf);
free (run_prog);
}
void
run_add_arg_p (int *iargc, size_t *iarg_allocated, char ***iargv,
const char *s)
{
if (*iargc >= *iarg_allocated)
{
*iarg_allocated += 50;
*iargv = xnrealloc (*iargv, *iarg_allocated, sizeof (char **));
}
if (s)
(*iargv)[(*iargc)++] = xstrdup (s);
else
(*iargv)[*iargc] = NULL;
}
void
run_add_arg (const char *s)
{
run_add_arg_p (&run_argc, &run_arg_allocated, &run_argv, s);
}
int
run_exec (const char *stin, const char *stout, const char *sterr, int flags)
{
int shin, shout, sherr;
int mode_out, mode_err;
int status;
int rc = -1;
int rerrno = 0;
int pid, w;
#ifdef POSIX_SIGNALS
sigset_t sigset_mask, sigset_omask;
struct sigaction act, iact, qact;
#else
#ifdef BSD_SIGNALS
int mask;
struct sigvec vec, ivec, qvec;
#else
RETSIGTYPE (*istat) (), (*qstat) ();
#endif
#endif
if (trace)
{
cvs_outerr (
#ifdef SERVER_SUPPORT
server_active ? "S" :
#endif
" ", 1);
cvs_outerr (" -> system (", 0);
run_print (stderr);
cvs_outerr (")\n", 0);
}
if (noexec && (flags & RUN_REALLY) == 0)
return 0;
run_add_arg (NULL);
shin = 0;
shout = 1;
sherr = 2;
mode_out = mode_err = O_WRONLY | O_CREAT;
mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC);
mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC);
if (stin && (shin = open (stin, O_RDONLY)) == -1)
{
rerrno = errno;
error (0, errno, "cannot open %s for reading (prog %s)",
stin, run_argv[0]);
goto out0;
}
if (stout && (shout = open (stout, mode_out, 0666)) == -1)
{
rerrno = errno;
error (0, errno, "cannot open %s for writing (prog %s)",
stout, run_argv[0]);
goto out1;
}
if (sterr && (flags & RUN_COMBINED) == 0)
{
if ((sherr = open (sterr, mode_err, 0666)) == -1)
{
rerrno = errno;
error (0, errno, "cannot open %s for writing (prog %s)",
sterr, run_argv[0]);
goto out2;
}
}
cvs_flushout();
cvs_flusherr();
#ifdef HAVE_VFORK
pid = vfork ();
#else
pid = fork ();
#endif
if (pid == 0)
{
if (shin != 0)
{
(void) dup2 (shin, 0);
(void) close (shin);
}
if (shout != 1)
{
(void) dup2 (shout, 1);
(void) close (shout);
}
if (flags & RUN_COMBINED)
(void) dup2 (1, 2);
else if (sherr != 2)
{
(void) dup2 (sherr, 2);
(void) close (sherr);
}
#ifdef SETXID_SUPPORT
if (!strcmp (run_argv[0], Editor) && setegid (getgid ()))
{
error (0, errno, "cannot set egid to gid");
_exit (127);
}
#endif
(void) execvp (run_argv[0], run_argv);
error (0, errno, "cannot exec %s", run_argv[0]);
_exit (127);
}
else if (pid == -1)
{
rerrno = errno;
goto out;
}
#ifdef POSIX_SIGNALS
if (flags & RUN_SIGIGNORE)
{
act.sa_handler = SIG_IGN;
(void) sigemptyset (&act.sa_mask);
act.sa_flags = 0;
(void) sigaction (SIGINT, &act, &iact);
(void) sigaction (SIGQUIT, &act, &qact);
}
else
{
(void) sigemptyset (&sigset_mask);
(void) sigaddset (&sigset_mask, SIGINT);
(void) sigaddset (&sigset_mask, SIGQUIT);
(void) sigprocmask (SIG_SETMASK, &sigset_mask, &sigset_omask);
}
#else
#ifdef BSD_SIGNALS
if (flags & RUN_SIGIGNORE)
{
memset (&vec, 0, sizeof vec);
vec.sv_handler = SIG_IGN;
(void) sigvec (SIGINT, &vec, &ivec);
(void) sigvec (SIGQUIT, &vec, &qvec);
}
else
mask = sigblock (sigmask (SIGINT) | sigmask (SIGQUIT));
#else
istat = signal (SIGINT, SIG_IGN);
qstat = signal (SIGQUIT, SIG_IGN);
#endif
#endif
#ifdef POSIX_SIGNALS
while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
;
#else
while ((w = wait (&status)) != pid)
{
if (w == -1 && errno != EINTR)
break;
}
#endif
if (w == -1)
{
rc = -1;
rerrno = errno;
}
#ifndef VMS
else if (WIFEXITED (status))
rc = WEXITSTATUS (status);
else if (WIFSIGNALED (status))
{
if (WTERMSIG (status) == SIGPIPE)
error (1, 0, "broken pipe");
rc = 2;
}
else
rc = 1;
#else
rc = WEXITSTATUS (status);
#endif
#ifdef POSIX_SIGNALS
if (flags & RUN_SIGIGNORE)
{
(void) sigaction (SIGINT, &iact, NULL);
(void) sigaction (SIGQUIT, &qact, NULL);
}
else
(void) sigprocmask (SIG_SETMASK, &sigset_omask, NULL);
#else
#ifdef BSD_SIGNALS
if (flags & RUN_SIGIGNORE)
{
(void) sigvec (SIGINT, &ivec, NULL);
(void) sigvec (SIGQUIT, &qvec, NULL);
}
else
(void) sigsetmask (mask);
#else
(void) signal (SIGINT, istat);
(void) signal (SIGQUIT, qstat);
#endif
#endif
out:
if (sterr)
(void) close (sherr);
else
cvs_flusherr();
out2:
if (stout)
(void) close (shout);
else
cvs_flushout();
out1:
if (stin)
(void) close (shin);
out0:
if (rerrno)
errno = rerrno;
return rc;
}
void
run_print (FILE *fp)
{
int i;
void (*outfn) (const char *, size_t);
if (fp == stderr)
outfn = cvs_outerr;
else if (fp == stdout)
outfn = cvs_output;
else
{
error (1, 0, "internal error: bad argument to run_print");
outfn = NULL;
}
for (i = 0; i < run_argc; i++)
{
(*outfn) ("'", 1);
(*outfn) (run_argv[i], 0);
(*outfn) ("'", 1);
if (i != run_argc - 1)
(*outfn) (" ", 1);
}
}
FILE *
run_popen (const char *cmd, const char *mode)
{
TRACE (TRACE_FUNCTION, "run_popen (%s,%s)", cmd, mode);
if (noexec)
return NULL;
return popen (cmd, mode);
}
static void
work_around_openssh_glitch (void)
{
pid_t pid;
int stderr_pipe[2];
struct stat sb;
if (!(fstat (STDERR_FILENO, &sb) == 0
&& (S_ISFIFO (sb.st_mode) || S_ISSOCK (sb.st_mode)
|| S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode))))
return;
if (pipe (stderr_pipe) < 0)
error (1, errno, "cannot create pipe");
pid = fork ();
if (pid < 0)
error (1, errno, "cannot fork");
if (pid != 0)
{
char buf[1 << 13];
ssize_t inbytes;
pid_t w;
int status;
if (close (stderr_pipe[1]) < 0)
error (1, errno, "cannot close pipe");
while ((inbytes = read (stderr_pipe[0], buf, sizeof buf)) != 0)
{
size_t outbytes = 0;
if (inbytes < 0)
{
if (errno == EINTR)
continue;
error (1, errno, "reading from pipe");
}
do
{
ssize_t w = write (STDERR_FILENO,
buf + outbytes, inbytes - outbytes);
if (w < 0)
{
if (errno == EINTR)
w = 0;
if (w < 0)
_exit (1);
}
outbytes += w;
}
while (inbytes != outbytes);
}
while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
continue;
if (w < 0)
error (1, errno, "waiting for child");
if (!WIFEXITED (status))
{
if (WIFSIGNALED (status))
raise (WTERMSIG (status));
error (1, errno, "child did not exit cleanly");
}
_exit (WEXITSTATUS (status));
}
if (close (stderr_pipe[0]) < 0)
error (1, errno, "cannot close pipe");
if (stderr_pipe[1] != STDERR_FILENO)
{
if (dup2 (stderr_pipe[1], STDERR_FILENO) < 0)
error (1, errno, "cannot dup2 pipe");
if (close (stderr_pipe[1]) < 0)
error (1, errno, "cannot close pipe");
}
}
int
piped_child (char *const *command, int *tofdp, int *fromfdp, bool fix_stderr)
{
int pid;
int to_child_pipe[2];
int from_child_pipe[2];
if (pipe (to_child_pipe) < 0)
error (1, errno, "cannot create pipe");
if (pipe (from_child_pipe) < 0)
error (1, errno, "cannot create pipe");
#ifdef USE_SETMODE_BINARY
setmode (to_child_pipe[0], O_BINARY);
setmode (to_child_pipe[1], O_BINARY);
setmode (from_child_pipe[0], O_BINARY);
setmode (from_child_pipe[1], O_BINARY);
#endif
pid = fork ();
if (pid < 0)
error (1, errno, "cannot fork");
if (pid == 0)
{
if (dup2 (to_child_pipe[0], STDIN_FILENO) < 0)
error (1, errno, "cannot dup2 pipe");
if (close (to_child_pipe[1]) < 0)
error (1, errno, "cannot close pipe");
if (close (from_child_pipe[0]) < 0)
error (1, errno, "cannot close pipe");
if (dup2 (from_child_pipe[1], STDOUT_FILENO) < 0)
error (1, errno, "cannot dup2 pipe");
if (fix_stderr)
work_around_openssh_glitch ();
execvp ((char *)command[0], (char **)command);
error (1, errno, "cannot exec %s", command[0]);
}
if (close (to_child_pipe[0]) < 0)
error (1, errno, "cannot close pipe");
if (close (from_child_pipe[1]) < 0)
error (1, errno, "cannot close pipe");
*tofdp = to_child_pipe[1];
*fromfdp = from_child_pipe[0];
return pid;
}
int
run_piped (int *tofdp, int *fromfdp)
{
run_add_arg (NULL);
return piped_child (run_argv, tofdp, fromfdp, false);
}
void
close_on_exec (int fd)
{
#ifdef F_SETFD
if (fcntl (fd, F_SETFD, 1) == -1)
error (1, errno, "can't set close-on-exec flag on %d", fd);
#endif
}