xref: /dflybsd-src/contrib/cvs-1.12/src/run.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /* run.c --- routines for executing subprocesses.
286d7f5d3SJohn Marino 
386d7f5d3SJohn Marino    This file is part of GNU CVS.
486d7f5d3SJohn Marino 
586d7f5d3SJohn Marino    GNU CVS is free software; you can redistribute it and/or modify it
686d7f5d3SJohn Marino    under the terms of the GNU General Public License as published by the
786d7f5d3SJohn Marino    Free Software Foundation; either version 2, or (at your option) any
886d7f5d3SJohn Marino    later version.
986d7f5d3SJohn Marino 
1086d7f5d3SJohn Marino    This program is distributed in the hope that it will be useful,
1186d7f5d3SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
1286d7f5d3SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1386d7f5d3SJohn Marino    GNU General Public License for more details.  */
1486d7f5d3SJohn Marino 
1586d7f5d3SJohn Marino #include "cvs.h"
1686d7f5d3SJohn Marino 
1786d7f5d3SJohn Marino #ifndef HAVE_UNISTD_H
1886d7f5d3SJohn Marino extern int execvp (char *file, char **argv);
1986d7f5d3SJohn Marino #endif
2086d7f5d3SJohn Marino 
2186d7f5d3SJohn Marino 
2286d7f5d3SJohn Marino 
2386d7f5d3SJohn Marino /*
2486d7f5d3SJohn Marino  * To exec a program under CVS, first call run_setup() to setup initial
2586d7f5d3SJohn Marino  * arguments.  The argument to run_setup will be parsed into whitespace
2686d7f5d3SJohn Marino  * separated words and added to the global run_argv list.
2786d7f5d3SJohn Marino  *
2886d7f5d3SJohn Marino  * Then, optionally call run_add_arg() for each additional argument that you'd
2986d7f5d3SJohn Marino  * like to pass to the executed program.
3086d7f5d3SJohn Marino  *
3186d7f5d3SJohn Marino  * Finally, call run_exec() to execute the program with the specified arguments.
3286d7f5d3SJohn Marino  * The execvp() syscall will be used, so that the PATH is searched correctly.
3386d7f5d3SJohn Marino  * File redirections can be performed in the call to run_exec().
3486d7f5d3SJohn Marino  */
3586d7f5d3SJohn Marino static char **run_argv;
3686d7f5d3SJohn Marino static int run_argc;
3786d7f5d3SJohn Marino static size_t run_arg_allocated;
3886d7f5d3SJohn Marino 
3986d7f5d3SJohn Marino 
4086d7f5d3SJohn Marino 
4186d7f5d3SJohn Marino void
run_arg_free_p(int argc,char ** argv)4286d7f5d3SJohn Marino run_arg_free_p (int argc, char **argv)
4386d7f5d3SJohn Marino {
4486d7f5d3SJohn Marino     int i;
4586d7f5d3SJohn Marino     for (i = 0; i < argc; i++)
4686d7f5d3SJohn Marino 	free (argv[i]);
4786d7f5d3SJohn Marino }
4886d7f5d3SJohn Marino 
4986d7f5d3SJohn Marino 
5086d7f5d3SJohn Marino 
5186d7f5d3SJohn Marino /* VARARGS */
5286d7f5d3SJohn Marino void
run_setup(const char * prog)5386d7f5d3SJohn Marino run_setup (const char *prog)
5486d7f5d3SJohn Marino {
5586d7f5d3SJohn Marino     char *run_prog;
5686d7f5d3SJohn Marino     char *buf, *d, *s;
5786d7f5d3SJohn Marino     size_t length;
5886d7f5d3SJohn Marino     size_t doff;
5986d7f5d3SJohn Marino     char inquotes;
6086d7f5d3SJohn Marino     int dolastarg;
6186d7f5d3SJohn Marino 
6286d7f5d3SJohn Marino     /* clean out any malloc'ed values from run_argv */
6386d7f5d3SJohn Marino     run_arg_free_p (run_argc, run_argv);
6486d7f5d3SJohn Marino     run_argc = 0;
6586d7f5d3SJohn Marino 
6686d7f5d3SJohn Marino     run_prog = xstrdup (prog);
6786d7f5d3SJohn Marino 
6886d7f5d3SJohn Marino     s = run_prog;
6986d7f5d3SJohn Marino     d = buf = NULL;
7086d7f5d3SJohn Marino     length = 0;
7186d7f5d3SJohn Marino     dolastarg = 1;
7286d7f5d3SJohn Marino     inquotes = '\0';
7386d7f5d3SJohn Marino     doff = d - buf;
7486d7f5d3SJohn Marino     expand_string(&buf, &length, doff + 1);
7586d7f5d3SJohn Marino     d = buf + doff;
7686d7f5d3SJohn Marino     while ((*d = *s++) != '\0')
7786d7f5d3SJohn Marino     {
7886d7f5d3SJohn Marino 	switch (*d)
7986d7f5d3SJohn Marino 	{
8086d7f5d3SJohn Marino 	    case '\\':
8186d7f5d3SJohn Marino 		if (*s) *d = *s++;
8286d7f5d3SJohn Marino 		d++;
8386d7f5d3SJohn Marino 		break;
8486d7f5d3SJohn Marino 	    case '"':
8586d7f5d3SJohn Marino 	    case '\'':
8686d7f5d3SJohn Marino 		if (inquotes == *d) inquotes = '\0';
8786d7f5d3SJohn Marino 		else inquotes = *d;
8886d7f5d3SJohn Marino 		break;
8986d7f5d3SJohn Marino 	    case ' ':
9086d7f5d3SJohn Marino 	    case '\t':
9186d7f5d3SJohn Marino 		if (inquotes) d++;
9286d7f5d3SJohn Marino 		else
9386d7f5d3SJohn Marino 		{
9486d7f5d3SJohn Marino 		    *d = '\0';
9586d7f5d3SJohn Marino 		    run_add_arg (buf);
9686d7f5d3SJohn Marino 		    d = buf;
9786d7f5d3SJohn Marino 		    while (isspace(*s)) s++;
9886d7f5d3SJohn Marino 		    if (!*s) dolastarg = 0;
9986d7f5d3SJohn Marino 		}
10086d7f5d3SJohn Marino 		break;
10186d7f5d3SJohn Marino 	    default:
10286d7f5d3SJohn Marino 		d++;
10386d7f5d3SJohn Marino 		break;
10486d7f5d3SJohn Marino 	}
10586d7f5d3SJohn Marino 	doff = d - buf;
10686d7f5d3SJohn Marino 	expand_string(&buf, &length, doff + 1);
10786d7f5d3SJohn Marino 	d = buf + doff;
10886d7f5d3SJohn Marino     }
10986d7f5d3SJohn Marino     if (dolastarg) run_add_arg (buf);
11086d7f5d3SJohn Marino     /* put each word into run_argv, allocating it as we go */
11186d7f5d3SJohn Marino     if (buf) free (buf);
11286d7f5d3SJohn Marino     free (run_prog);
11386d7f5d3SJohn Marino }
11486d7f5d3SJohn Marino 
11586d7f5d3SJohn Marino 
11686d7f5d3SJohn Marino 
11786d7f5d3SJohn Marino void
run_add_arg_p(int * iargc,size_t * iarg_allocated,char *** iargv,const char * s)11886d7f5d3SJohn Marino run_add_arg_p (int *iargc, size_t *iarg_allocated, char ***iargv,
11986d7f5d3SJohn Marino 	       const char *s)
12086d7f5d3SJohn Marino {
12186d7f5d3SJohn Marino     /* allocate more argv entries if we've run out */
12286d7f5d3SJohn Marino     if (*iargc >= *iarg_allocated)
12386d7f5d3SJohn Marino     {
12486d7f5d3SJohn Marino 	*iarg_allocated += 50;
12586d7f5d3SJohn Marino 	*iargv = xnrealloc (*iargv, *iarg_allocated, sizeof (char **));
12686d7f5d3SJohn Marino     }
12786d7f5d3SJohn Marino 
12886d7f5d3SJohn Marino     if (s)
12986d7f5d3SJohn Marino 	(*iargv)[(*iargc)++] = xstrdup (s);
13086d7f5d3SJohn Marino     else
13186d7f5d3SJohn Marino 	(*iargv)[*iargc] = NULL;	/* not post-incremented on purpose! */
13286d7f5d3SJohn Marino }
13386d7f5d3SJohn Marino 
13486d7f5d3SJohn Marino 
13586d7f5d3SJohn Marino 
13686d7f5d3SJohn Marino void
run_add_arg(const char * s)13786d7f5d3SJohn Marino run_add_arg (const char *s)
13886d7f5d3SJohn Marino {
13986d7f5d3SJohn Marino     run_add_arg_p (&run_argc, &run_arg_allocated, &run_argv, s);
14086d7f5d3SJohn Marino }
14186d7f5d3SJohn Marino 
14286d7f5d3SJohn Marino 
14386d7f5d3SJohn Marino 
14486d7f5d3SJohn Marino int
run_exec(const char * stin,const char * stout,const char * sterr,int flags)14586d7f5d3SJohn Marino run_exec (const char *stin, const char *stout, const char *sterr, int flags)
14686d7f5d3SJohn Marino {
14786d7f5d3SJohn Marino     int shin, shout, sherr;
14886d7f5d3SJohn Marino     int mode_out, mode_err;
14986d7f5d3SJohn Marino     int status;
15086d7f5d3SJohn Marino     int rc = -1;
15186d7f5d3SJohn Marino     int rerrno = 0;
15286d7f5d3SJohn Marino     int pid, w;
15386d7f5d3SJohn Marino 
15486d7f5d3SJohn Marino #ifdef POSIX_SIGNALS
15586d7f5d3SJohn Marino     sigset_t sigset_mask, sigset_omask;
15686d7f5d3SJohn Marino     struct sigaction act, iact, qact;
15786d7f5d3SJohn Marino 
15886d7f5d3SJohn Marino #else
15986d7f5d3SJohn Marino #ifdef BSD_SIGNALS
16086d7f5d3SJohn Marino     int mask;
16186d7f5d3SJohn Marino     struct sigvec vec, ivec, qvec;
16286d7f5d3SJohn Marino 
16386d7f5d3SJohn Marino #else
16486d7f5d3SJohn Marino     RETSIGTYPE (*istat) (), (*qstat) ();
16586d7f5d3SJohn Marino #endif
16686d7f5d3SJohn Marino #endif
16786d7f5d3SJohn Marino 
16886d7f5d3SJohn Marino     if (trace)
16986d7f5d3SJohn Marino     {
17086d7f5d3SJohn Marino 	cvs_outerr (
17186d7f5d3SJohn Marino #ifdef SERVER_SUPPORT
17286d7f5d3SJohn Marino 		    server_active ? "S" :
17386d7f5d3SJohn Marino #endif
17486d7f5d3SJohn Marino 		    " ", 1);
17586d7f5d3SJohn Marino 	cvs_outerr (" -> system (", 0);
17686d7f5d3SJohn Marino 	run_print (stderr);
17786d7f5d3SJohn Marino 	cvs_outerr (")\n", 0);
17886d7f5d3SJohn Marino     }
17986d7f5d3SJohn Marino     if (noexec && (flags & RUN_REALLY) == 0)
18086d7f5d3SJohn Marino 	return 0;
18186d7f5d3SJohn Marino 
18286d7f5d3SJohn Marino     /* make sure that we are null terminated, since we didn't calloc */
18386d7f5d3SJohn Marino     run_add_arg (NULL);
18486d7f5d3SJohn Marino 
18586d7f5d3SJohn Marino     /* setup default file descriptor numbers */
18686d7f5d3SJohn Marino     shin = 0;
18786d7f5d3SJohn Marino     shout = 1;
18886d7f5d3SJohn Marino     sherr = 2;
18986d7f5d3SJohn Marino 
19086d7f5d3SJohn Marino     /* set the file modes for stdout and stderr */
19186d7f5d3SJohn Marino     mode_out = mode_err = O_WRONLY | O_CREAT;
19286d7f5d3SJohn Marino     mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC);
19386d7f5d3SJohn Marino     mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC);
19486d7f5d3SJohn Marino 
19586d7f5d3SJohn Marino     if (stin && (shin = open (stin, O_RDONLY)) == -1)
19686d7f5d3SJohn Marino     {
19786d7f5d3SJohn Marino 	rerrno = errno;
19886d7f5d3SJohn Marino 	error (0, errno, "cannot open %s for reading (prog %s)",
19986d7f5d3SJohn Marino 	       stin, run_argv[0]);
20086d7f5d3SJohn Marino 	goto out0;
20186d7f5d3SJohn Marino     }
20286d7f5d3SJohn Marino     if (stout && (shout = open (stout, mode_out, 0666)) == -1)
20386d7f5d3SJohn Marino     {
20486d7f5d3SJohn Marino 	rerrno = errno;
20586d7f5d3SJohn Marino 	error (0, errno, "cannot open %s for writing (prog %s)",
20686d7f5d3SJohn Marino 	       stout, run_argv[0]);
20786d7f5d3SJohn Marino 	goto out1;
20886d7f5d3SJohn Marino     }
20986d7f5d3SJohn Marino     if (sterr && (flags & RUN_COMBINED) == 0)
21086d7f5d3SJohn Marino     {
21186d7f5d3SJohn Marino 	if ((sherr = open (sterr, mode_err, 0666)) == -1)
21286d7f5d3SJohn Marino 	{
21386d7f5d3SJohn Marino 	    rerrno = errno;
21486d7f5d3SJohn Marino 	    error (0, errno, "cannot open %s for writing (prog %s)",
21586d7f5d3SJohn Marino 		   sterr, run_argv[0]);
21686d7f5d3SJohn Marino 	    goto out2;
21786d7f5d3SJohn Marino 	}
21886d7f5d3SJohn Marino     }
21986d7f5d3SJohn Marino 
22086d7f5d3SJohn Marino     /* Make sure we don't flush this twice, once in the subprocess.  */
22186d7f5d3SJohn Marino     cvs_flushout();
22286d7f5d3SJohn Marino     cvs_flusherr();
22386d7f5d3SJohn Marino 
22486d7f5d3SJohn Marino     /* The output files, if any, are now created.  Do the fork and dups.
22586d7f5d3SJohn Marino 
22686d7f5d3SJohn Marino        We use vfork not so much for a performance boost (the
22786d7f5d3SJohn Marino        performance boost, if any, is modest on most modern unices),
22886d7f5d3SJohn Marino        but for the sake of systems without a memory management unit,
22986d7f5d3SJohn Marino        which find it difficult or impossible to implement fork at all
23086d7f5d3SJohn Marino        (e.g. Amiga).  The other solution is spawn (see
23186d7f5d3SJohn Marino        windows-NT/run.c).  */
23286d7f5d3SJohn Marino 
23386d7f5d3SJohn Marino #ifdef HAVE_VFORK
23486d7f5d3SJohn Marino     pid = vfork ();
23586d7f5d3SJohn Marino #else
23686d7f5d3SJohn Marino     pid = fork ();
23786d7f5d3SJohn Marino #endif
23886d7f5d3SJohn Marino     if (pid == 0)
23986d7f5d3SJohn Marino     {
24086d7f5d3SJohn Marino 	if (shin != 0)
24186d7f5d3SJohn Marino 	{
24286d7f5d3SJohn Marino 	    (void) dup2 (shin, 0);
24386d7f5d3SJohn Marino 	    (void) close (shin);
24486d7f5d3SJohn Marino 	}
24586d7f5d3SJohn Marino 	if (shout != 1)
24686d7f5d3SJohn Marino 	{
24786d7f5d3SJohn Marino 	    (void) dup2 (shout, 1);
24886d7f5d3SJohn Marino 	    (void) close (shout);
24986d7f5d3SJohn Marino 	}
25086d7f5d3SJohn Marino 	if (flags & RUN_COMBINED)
25186d7f5d3SJohn Marino 	    (void) dup2 (1, 2);
25286d7f5d3SJohn Marino 	else if (sherr != 2)
25386d7f5d3SJohn Marino 	{
25486d7f5d3SJohn Marino 	    (void) dup2 (sherr, 2);
25586d7f5d3SJohn Marino 	    (void) close (sherr);
25686d7f5d3SJohn Marino 	}
25786d7f5d3SJohn Marino 
25886d7f5d3SJohn Marino #ifdef SETXID_SUPPORT
25986d7f5d3SJohn Marino 	/*
26086d7f5d3SJohn Marino 	** This prevents a user from creating a privileged shell
26186d7f5d3SJohn Marino 	** from the text editor when the SETXID_SUPPORT option is selected.
26286d7f5d3SJohn Marino 	*/
26386d7f5d3SJohn Marino 	if (!strcmp (run_argv[0], Editor) && setegid (getgid ()))
26486d7f5d3SJohn Marino 	{
26586d7f5d3SJohn Marino 	    error (0, errno, "cannot set egid to gid");
26686d7f5d3SJohn Marino 	    _exit (127);
26786d7f5d3SJohn Marino 	}
26886d7f5d3SJohn Marino #endif
26986d7f5d3SJohn Marino 
27086d7f5d3SJohn Marino 	/* dup'ing is done.  try to run it now */
27186d7f5d3SJohn Marino 	(void) execvp (run_argv[0], run_argv);
27286d7f5d3SJohn Marino 	error (0, errno, "cannot exec %s", run_argv[0]);
27386d7f5d3SJohn Marino 	_exit (127);
27486d7f5d3SJohn Marino     }
27586d7f5d3SJohn Marino     else if (pid == -1)
27686d7f5d3SJohn Marino     {
27786d7f5d3SJohn Marino 	rerrno = errno;
27886d7f5d3SJohn Marino 	goto out;
27986d7f5d3SJohn Marino     }
28086d7f5d3SJohn Marino 
28186d7f5d3SJohn Marino     /* the parent.  Ignore some signals for now */
28286d7f5d3SJohn Marino #ifdef POSIX_SIGNALS
28386d7f5d3SJohn Marino     if (flags & RUN_SIGIGNORE)
28486d7f5d3SJohn Marino     {
28586d7f5d3SJohn Marino 	act.sa_handler = SIG_IGN;
28686d7f5d3SJohn Marino 	(void) sigemptyset (&act.sa_mask);
28786d7f5d3SJohn Marino 	act.sa_flags = 0;
28886d7f5d3SJohn Marino 	(void) sigaction (SIGINT, &act, &iact);
28986d7f5d3SJohn Marino 	(void) sigaction (SIGQUIT, &act, &qact);
29086d7f5d3SJohn Marino     }
29186d7f5d3SJohn Marino     else
29286d7f5d3SJohn Marino     {
29386d7f5d3SJohn Marino 	(void) sigemptyset (&sigset_mask);
29486d7f5d3SJohn Marino 	(void) sigaddset (&sigset_mask, SIGINT);
29586d7f5d3SJohn Marino 	(void) sigaddset (&sigset_mask, SIGQUIT);
29686d7f5d3SJohn Marino 	(void) sigprocmask (SIG_SETMASK, &sigset_mask, &sigset_omask);
29786d7f5d3SJohn Marino     }
29886d7f5d3SJohn Marino #else
29986d7f5d3SJohn Marino #ifdef BSD_SIGNALS
30086d7f5d3SJohn Marino     if (flags & RUN_SIGIGNORE)
30186d7f5d3SJohn Marino     {
30286d7f5d3SJohn Marino 	memset (&vec, 0, sizeof vec);
30386d7f5d3SJohn Marino 	vec.sv_handler = SIG_IGN;
30486d7f5d3SJohn Marino 	(void) sigvec (SIGINT, &vec, &ivec);
30586d7f5d3SJohn Marino 	(void) sigvec (SIGQUIT, &vec, &qvec);
30686d7f5d3SJohn Marino     }
30786d7f5d3SJohn Marino     else
30886d7f5d3SJohn Marino 	mask = sigblock (sigmask (SIGINT) | sigmask (SIGQUIT));
30986d7f5d3SJohn Marino #else
31086d7f5d3SJohn Marino     istat = signal (SIGINT, SIG_IGN);
31186d7f5d3SJohn Marino     qstat = signal (SIGQUIT, SIG_IGN);
31286d7f5d3SJohn Marino #endif
31386d7f5d3SJohn Marino #endif
31486d7f5d3SJohn Marino 
31586d7f5d3SJohn Marino     /* wait for our process to die and munge return status */
31686d7f5d3SJohn Marino #ifdef POSIX_SIGNALS
31786d7f5d3SJohn Marino     while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
31886d7f5d3SJohn Marino 	;
31986d7f5d3SJohn Marino #else
32086d7f5d3SJohn Marino     while ((w = wait (&status)) != pid)
32186d7f5d3SJohn Marino     {
32286d7f5d3SJohn Marino 	if (w == -1 && errno != EINTR)
32386d7f5d3SJohn Marino 	    break;
32486d7f5d3SJohn Marino     }
32586d7f5d3SJohn Marino #endif
32686d7f5d3SJohn Marino 
32786d7f5d3SJohn Marino     if (w == -1)
32886d7f5d3SJohn Marino     {
32986d7f5d3SJohn Marino 	rc = -1;
33086d7f5d3SJohn Marino 	rerrno = errno;
33186d7f5d3SJohn Marino     }
33286d7f5d3SJohn Marino #ifndef VMS /* status is return status */
33386d7f5d3SJohn Marino     else if (WIFEXITED (status))
33486d7f5d3SJohn Marino 	rc = WEXITSTATUS (status);
33586d7f5d3SJohn Marino     else if (WIFSIGNALED (status))
33686d7f5d3SJohn Marino     {
33786d7f5d3SJohn Marino 	if (WTERMSIG (status) == SIGPIPE)
33886d7f5d3SJohn Marino 	    error (1, 0, "broken pipe");
33986d7f5d3SJohn Marino 	rc = 2;
34086d7f5d3SJohn Marino     }
34186d7f5d3SJohn Marino     else
34286d7f5d3SJohn Marino 	rc = 1;
34386d7f5d3SJohn Marino #else /* VMS */
34486d7f5d3SJohn Marino     rc = WEXITSTATUS (status);
34586d7f5d3SJohn Marino #endif /* VMS */
34686d7f5d3SJohn Marino 
34786d7f5d3SJohn Marino     /* restore the signals */
34886d7f5d3SJohn Marino #ifdef POSIX_SIGNALS
34986d7f5d3SJohn Marino     if (flags & RUN_SIGIGNORE)
35086d7f5d3SJohn Marino     {
35186d7f5d3SJohn Marino 	(void) sigaction (SIGINT, &iact, NULL);
35286d7f5d3SJohn Marino 	(void) sigaction (SIGQUIT, &qact, NULL);
35386d7f5d3SJohn Marino     }
35486d7f5d3SJohn Marino     else
35586d7f5d3SJohn Marino 	(void) sigprocmask (SIG_SETMASK, &sigset_omask, NULL);
35686d7f5d3SJohn Marino #else
35786d7f5d3SJohn Marino #ifdef BSD_SIGNALS
35886d7f5d3SJohn Marino     if (flags & RUN_SIGIGNORE)
35986d7f5d3SJohn Marino     {
36086d7f5d3SJohn Marino 	(void) sigvec (SIGINT, &ivec, NULL);
36186d7f5d3SJohn Marino 	(void) sigvec (SIGQUIT, &qvec, NULL);
36286d7f5d3SJohn Marino     }
36386d7f5d3SJohn Marino     else
36486d7f5d3SJohn Marino 	(void) sigsetmask (mask);
36586d7f5d3SJohn Marino #else
36686d7f5d3SJohn Marino     (void) signal (SIGINT, istat);
36786d7f5d3SJohn Marino     (void) signal (SIGQUIT, qstat);
36886d7f5d3SJohn Marino #endif
36986d7f5d3SJohn Marino #endif
37086d7f5d3SJohn Marino 
37186d7f5d3SJohn Marino     /* cleanup the open file descriptors */
37286d7f5d3SJohn Marino   out:
37386d7f5d3SJohn Marino     if (sterr)
37486d7f5d3SJohn Marino 	(void) close (sherr);
37586d7f5d3SJohn Marino     else
37686d7f5d3SJohn Marino 	/* ensure things are received by the parent in the correct order
37786d7f5d3SJohn Marino 	 * relative to the protocol pipe
37886d7f5d3SJohn Marino 	 */
37986d7f5d3SJohn Marino 	cvs_flusherr();
38086d7f5d3SJohn Marino   out2:
38186d7f5d3SJohn Marino     if (stout)
38286d7f5d3SJohn Marino 	(void) close (shout);
38386d7f5d3SJohn Marino     else
38486d7f5d3SJohn Marino 	/* ensure things are received by the parent in the correct order
38586d7f5d3SJohn Marino 	 * relative to the protocol pipe
38686d7f5d3SJohn Marino 	 */
38786d7f5d3SJohn Marino 	cvs_flushout();
38886d7f5d3SJohn Marino   out1:
38986d7f5d3SJohn Marino     if (stin)
39086d7f5d3SJohn Marino 	(void) close (shin);
39186d7f5d3SJohn Marino 
39286d7f5d3SJohn Marino   out0:
39386d7f5d3SJohn Marino     if (rerrno)
39486d7f5d3SJohn Marino 	errno = rerrno;
39586d7f5d3SJohn Marino     return rc;
39686d7f5d3SJohn Marino }
39786d7f5d3SJohn Marino 
39886d7f5d3SJohn Marino 
39986d7f5d3SJohn Marino 
40086d7f5d3SJohn Marino void
run_print(FILE * fp)40186d7f5d3SJohn Marino run_print (FILE *fp)
40286d7f5d3SJohn Marino {
40386d7f5d3SJohn Marino     int i;
40486d7f5d3SJohn Marino     void (*outfn) (const char *, size_t);
40586d7f5d3SJohn Marino 
40686d7f5d3SJohn Marino     if (fp == stderr)
40786d7f5d3SJohn Marino 	outfn = cvs_outerr;
40886d7f5d3SJohn Marino     else if (fp == stdout)
40986d7f5d3SJohn Marino 	outfn = cvs_output;
41086d7f5d3SJohn Marino     else
41186d7f5d3SJohn Marino     {
41286d7f5d3SJohn Marino 	error (1, 0, "internal error: bad argument to run_print");
41386d7f5d3SJohn Marino 	/* Solely to placate gcc -Wall.
41486d7f5d3SJohn Marino 	   FIXME: it'd be better to use a function named `fatal' that
41586d7f5d3SJohn Marino 	   is known never to return.  Then kludges wouldn't be necessary.  */
41686d7f5d3SJohn Marino 	outfn = NULL;
41786d7f5d3SJohn Marino     }
41886d7f5d3SJohn Marino 
41986d7f5d3SJohn Marino     for (i = 0; i < run_argc; i++)
42086d7f5d3SJohn Marino     {
42186d7f5d3SJohn Marino 	(*outfn) ("'", 1);
42286d7f5d3SJohn Marino 	(*outfn) (run_argv[i], 0);
42386d7f5d3SJohn Marino 	(*outfn) ("'", 1);
42486d7f5d3SJohn Marino 	if (i != run_argc - 1)
42586d7f5d3SJohn Marino 	    (*outfn) (" ", 1);
42686d7f5d3SJohn Marino     }
42786d7f5d3SJohn Marino }
42886d7f5d3SJohn Marino 
42986d7f5d3SJohn Marino 
43086d7f5d3SJohn Marino 
43186d7f5d3SJohn Marino /* Return value is NULL for error, or if noexec was set.  If there was an
43286d7f5d3SJohn Marino    error, return NULL and I'm not sure whether errno was set (the Red Hat
43386d7f5d3SJohn Marino    Linux 4.1 popen manpage was kind of vague but discouraging; and the noexec
43486d7f5d3SJohn Marino    case complicates this even aside from popen behavior).  */
43586d7f5d3SJohn Marino FILE *
run_popen(const char * cmd,const char * mode)43686d7f5d3SJohn Marino run_popen (const char *cmd, const char *mode)
43786d7f5d3SJohn Marino {
43886d7f5d3SJohn Marino     TRACE (TRACE_FUNCTION, "run_popen (%s,%s)", cmd, mode);
43986d7f5d3SJohn Marino     if (noexec)
44086d7f5d3SJohn Marino 	return NULL;
44186d7f5d3SJohn Marino 
44286d7f5d3SJohn Marino     return popen (cmd, mode);
44386d7f5d3SJohn Marino }
44486d7f5d3SJohn Marino 
44586d7f5d3SJohn Marino 
44686d7f5d3SJohn Marino 
44786d7f5d3SJohn Marino /* Work around an OpenSSH problem: it can put its standard file
44886d7f5d3SJohn Marino    descriptors into nonblocking mode, which will mess us up if we
44986d7f5d3SJohn Marino    share file descriptions with it.  The simplest workaround is
45086d7f5d3SJohn Marino    to create an intervening process between OpenSSH and the
45186d7f5d3SJohn Marino    actual stderr.  */
45286d7f5d3SJohn Marino 
45386d7f5d3SJohn Marino static void
work_around_openssh_glitch(void)45486d7f5d3SJohn Marino work_around_openssh_glitch (void)
45586d7f5d3SJohn Marino {
45686d7f5d3SJohn Marino     pid_t pid;
45786d7f5d3SJohn Marino     int stderr_pipe[2];
45886d7f5d3SJohn Marino     struct stat sb;
45986d7f5d3SJohn Marino 
46086d7f5d3SJohn Marino     /* Do nothing unless stderr is a file that is affected by
46186d7f5d3SJohn Marino        nonblocking mode.  */
46286d7f5d3SJohn Marino     if (!(fstat (STDERR_FILENO, &sb) == 0
46386d7f5d3SJohn Marino           && (S_ISFIFO (sb.st_mode) || S_ISSOCK (sb.st_mode)
46486d7f5d3SJohn Marino               || S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode))))
46586d7f5d3SJohn Marino        return;
46686d7f5d3SJohn Marino 
46786d7f5d3SJohn Marino     if (pipe (stderr_pipe) < 0)
46886d7f5d3SJohn Marino        error (1, errno, "cannot create pipe");
46986d7f5d3SJohn Marino     pid = fork ();
47086d7f5d3SJohn Marino     if (pid < 0)
47186d7f5d3SJohn Marino        error (1, errno, "cannot fork");
47286d7f5d3SJohn Marino     if (pid != 0)
47386d7f5d3SJohn Marino     {
47486d7f5d3SJohn Marino        /* Still in child of original process.  Act like "cat -u".  */
47586d7f5d3SJohn Marino        char buf[1 << 13];
47686d7f5d3SJohn Marino        ssize_t inbytes;
47786d7f5d3SJohn Marino        pid_t w;
47886d7f5d3SJohn Marino        int status;
47986d7f5d3SJohn Marino 
48086d7f5d3SJohn Marino        if (close (stderr_pipe[1]) < 0)
48186d7f5d3SJohn Marino            error (1, errno, "cannot close pipe");
48286d7f5d3SJohn Marino 
48386d7f5d3SJohn Marino        while ((inbytes = read (stderr_pipe[0], buf, sizeof buf)) != 0)
48486d7f5d3SJohn Marino        {
48586d7f5d3SJohn Marino            size_t outbytes = 0;
48686d7f5d3SJohn Marino 
48786d7f5d3SJohn Marino            if (inbytes < 0)
48886d7f5d3SJohn Marino            {
48986d7f5d3SJohn Marino                if (errno == EINTR)
49086d7f5d3SJohn Marino                    continue;
49186d7f5d3SJohn Marino                error (1, errno, "reading from pipe");
49286d7f5d3SJohn Marino            }
49386d7f5d3SJohn Marino 
49486d7f5d3SJohn Marino            do
49586d7f5d3SJohn Marino            {
49686d7f5d3SJohn Marino                ssize_t w = write (STDERR_FILENO,
49786d7f5d3SJohn Marino                                   buf + outbytes, inbytes - outbytes);
49886d7f5d3SJohn Marino                if (w < 0)
49986d7f5d3SJohn Marino                {
50086d7f5d3SJohn Marino                    if (errno == EINTR)
50186d7f5d3SJohn Marino                      w = 0;
50286d7f5d3SJohn Marino                    if (w < 0)
50386d7f5d3SJohn Marino                      _exit (1);
50486d7f5d3SJohn Marino                }
50586d7f5d3SJohn Marino                outbytes += w;
50686d7f5d3SJohn Marino            }
50786d7f5d3SJohn Marino            while (inbytes != outbytes);
50886d7f5d3SJohn Marino        }
50986d7f5d3SJohn Marino 
51086d7f5d3SJohn Marino        /* Done processing output from grandchild.  Propagate
51186d7f5d3SJohn Marino           its exit status back to the parent.  */
51286d7f5d3SJohn Marino        while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
51386d7f5d3SJohn Marino            continue;
51486d7f5d3SJohn Marino        if (w < 0)
51586d7f5d3SJohn Marino            error (1, errno, "waiting for child");
51686d7f5d3SJohn Marino        if (!WIFEXITED (status))
51786d7f5d3SJohn Marino        {
51886d7f5d3SJohn Marino            if (WIFSIGNALED (status))
51986d7f5d3SJohn Marino                raise (WTERMSIG (status));
52086d7f5d3SJohn Marino            error (1, errno, "child did not exit cleanly");
52186d7f5d3SJohn Marino        }
52286d7f5d3SJohn Marino        _exit (WEXITSTATUS (status));
52386d7f5d3SJohn Marino     }
52486d7f5d3SJohn Marino 
52586d7f5d3SJohn Marino     /* Grandchild of original process.  */
52686d7f5d3SJohn Marino     if (close (stderr_pipe[0]) < 0)
52786d7f5d3SJohn Marino        error (1, errno, "cannot close pipe");
52886d7f5d3SJohn Marino 
52986d7f5d3SJohn Marino     if (stderr_pipe[1] != STDERR_FILENO)
53086d7f5d3SJohn Marino     {
53186d7f5d3SJohn Marino        if (dup2 (stderr_pipe[1], STDERR_FILENO) < 0)
53286d7f5d3SJohn Marino            error (1, errno, "cannot dup2 pipe");
53386d7f5d3SJohn Marino        if (close (stderr_pipe[1]) < 0)
53486d7f5d3SJohn Marino            error (1, errno, "cannot close pipe");
53586d7f5d3SJohn Marino     }
53686d7f5d3SJohn Marino }
53786d7f5d3SJohn Marino 
53886d7f5d3SJohn Marino 
53986d7f5d3SJohn Marino 
54086d7f5d3SJohn Marino int
piped_child(char * const * command,int * tofdp,int * fromfdp,bool fix_stderr)54186d7f5d3SJohn Marino piped_child (char *const *command, int *tofdp, int *fromfdp, bool fix_stderr)
54286d7f5d3SJohn Marino {
54386d7f5d3SJohn Marino     int pid;
54486d7f5d3SJohn Marino     int to_child_pipe[2];
54586d7f5d3SJohn Marino     int from_child_pipe[2];
54686d7f5d3SJohn Marino 
54786d7f5d3SJohn Marino     if (pipe (to_child_pipe) < 0)
54886d7f5d3SJohn Marino 	error (1, errno, "cannot create pipe");
54986d7f5d3SJohn Marino     if (pipe (from_child_pipe) < 0)
55086d7f5d3SJohn Marino 	error (1, errno, "cannot create pipe");
55186d7f5d3SJohn Marino 
55286d7f5d3SJohn Marino #ifdef USE_SETMODE_BINARY
55386d7f5d3SJohn Marino     setmode (to_child_pipe[0], O_BINARY);
55486d7f5d3SJohn Marino     setmode (to_child_pipe[1], O_BINARY);
55586d7f5d3SJohn Marino     setmode (from_child_pipe[0], O_BINARY);
55686d7f5d3SJohn Marino     setmode (from_child_pipe[1], O_BINARY);
55786d7f5d3SJohn Marino #endif
55886d7f5d3SJohn Marino 
55986d7f5d3SJohn Marino     pid = fork ();
56086d7f5d3SJohn Marino     if (pid < 0)
56186d7f5d3SJohn Marino 	error (1, errno, "cannot fork");
56286d7f5d3SJohn Marino     if (pid == 0)
56386d7f5d3SJohn Marino     {
56486d7f5d3SJohn Marino 	if (dup2 (to_child_pipe[0], STDIN_FILENO) < 0)
56586d7f5d3SJohn Marino 	    error (1, errno, "cannot dup2 pipe");
56686d7f5d3SJohn Marino 	if (close (to_child_pipe[1]) < 0)
56786d7f5d3SJohn Marino 	    error (1, errno, "cannot close pipe");
56886d7f5d3SJohn Marino 	if (close (from_child_pipe[0]) < 0)
56986d7f5d3SJohn Marino 	    error (1, errno, "cannot close pipe");
57086d7f5d3SJohn Marino 	if (dup2 (from_child_pipe[1], STDOUT_FILENO) < 0)
57186d7f5d3SJohn Marino 	    error (1, errno, "cannot dup2 pipe");
57286d7f5d3SJohn Marino 
57386d7f5d3SJohn Marino         if (fix_stderr)
57486d7f5d3SJohn Marino 	    work_around_openssh_glitch ();
57586d7f5d3SJohn Marino 
57686d7f5d3SJohn Marino 	/* Okay to cast out const below - execvp don't return nohow.  */
57786d7f5d3SJohn Marino 	execvp ((char *)command[0], (char **)command);
57886d7f5d3SJohn Marino 	error (1, errno, "cannot exec %s", command[0]);
57986d7f5d3SJohn Marino     }
58086d7f5d3SJohn Marino     if (close (to_child_pipe[0]) < 0)
58186d7f5d3SJohn Marino 	error (1, errno, "cannot close pipe");
58286d7f5d3SJohn Marino     if (close (from_child_pipe[1]) < 0)
58386d7f5d3SJohn Marino 	error (1, errno, "cannot close pipe");
58486d7f5d3SJohn Marino 
58586d7f5d3SJohn Marino     *tofdp = to_child_pipe[1];
58686d7f5d3SJohn Marino     *fromfdp = from_child_pipe[0];
58786d7f5d3SJohn Marino     return pid;
58886d7f5d3SJohn Marino }
58986d7f5d3SJohn Marino 
59086d7f5d3SJohn Marino 
59186d7f5d3SJohn Marino 
59286d7f5d3SJohn Marino int
run_piped(int * tofdp,int * fromfdp)59386d7f5d3SJohn Marino run_piped (int *tofdp, int *fromfdp)
59486d7f5d3SJohn Marino {
59586d7f5d3SJohn Marino     run_add_arg (NULL);
59686d7f5d3SJohn Marino     return piped_child (run_argv, tofdp, fromfdp, false);
59786d7f5d3SJohn Marino }
59886d7f5d3SJohn Marino 
59986d7f5d3SJohn Marino 
60086d7f5d3SJohn Marino 
60186d7f5d3SJohn Marino void
close_on_exec(int fd)60286d7f5d3SJohn Marino close_on_exec (int fd)
60386d7f5d3SJohn Marino {
60486d7f5d3SJohn Marino #ifdef F_SETFD
60586d7f5d3SJohn Marino     if (fcntl (fd, F_SETFD, 1) == -1)
60686d7f5d3SJohn Marino 	error (1, errno, "can't set close-on-exec flag on %d", fd);
60786d7f5d3SJohn Marino #endif
60886d7f5d3SJohn Marino }
609