xref: /netbsd-src/external/gpl2/xcvs/dist/src/run.c (revision 70aed070fa961c5c69216360d0b522e1b69deb3c)
1 /* run.c --- routines for executing subprocesses.
2 
3    This file is part of GNU CVS.
4 
5    GNU CVS is free software; you can redistribute it and/or modify it
6    under the terms of the GNU General Public License as published by the
7    Free Software Foundation; either version 2, or (at your option) any
8    later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.  */
14 #include <sys/cdefs.h>
15 __RCSID("$NetBSD: run.c,v 1.4 2018/08/21 15:37:33 christos Exp $");
16 
17 #include "cvs.h"
18 
19 #ifndef HAVE_UNISTD_H
20 extern int execvp (char *file, char **argv);
21 #endif
22 
23 
24 
25 /*
26  * To exec a program under CVS, first call run_setup() to setup initial
27  * arguments.  The argument to run_setup will be parsed into whitespace
28  * separated words and added to the global run_argv list.
29  *
30  * Then, optionally call run_add_arg() for each additional argument that you'd
31  * like to pass to the executed program.
32  *
33  * Finally, call run_exec() to execute the program with the specified arguments.
34  * The execvp() syscall will be used, so that the PATH is searched correctly.
35  * File redirections can be performed in the call to run_exec().
36  */
37 static char **run_argv;
38 static int run_argc;
39 static size_t run_arg_allocated;
40 
41 
42 
43 void
run_arg_free_p(int argc,char ** argv)44 run_arg_free_p (int argc, char **argv)
45 {
46     int i;
47     for (i = 0; i < argc; i++)
48 	free (argv[i]);
49 }
50 
51 
52 
53 /* VARARGS */
54 void
run_setup(const char * prog)55 run_setup (const char *prog)
56 {
57     char *run_prog;
58     char *buf, *d, *s;
59     size_t length;
60     size_t doff;
61     char inquotes;
62     int dolastarg;
63 
64     /* clean out any malloc'ed values from run_argv */
65     run_arg_free_p (run_argc, run_argv);
66     run_argc = 0;
67 
68     run_prog = xstrdup (prog);
69 
70     s = run_prog;
71     d = buf = NULL;
72     length = 0;
73     dolastarg = 1;
74     inquotes = '\0';
75     doff = d - buf;
76     expand_string(&buf, &length, doff + 1);
77     d = buf + doff;
78     while ((*d = *s++) != '\0')
79     {
80 	switch (*d)
81 	{
82 	    case '\\':
83 		if (*s) *d = *s++;
84 		d++;
85 		break;
86 	    case '"':
87 	    case '\'':
88 		if (inquotes == *d) inquotes = '\0';
89 		else inquotes = *d;
90 		break;
91 	    case ' ':
92 	    case '\t':
93 		if (inquotes) d++;
94 		else
95 		{
96 		    *d = '\0';
97 		    run_add_arg (buf);
98 		    d = buf;
99 		    while (isspace(*s)) s++;
100 		    if (!*s) dolastarg = 0;
101 		}
102 		break;
103 	    default:
104 		d++;
105 		break;
106 	}
107 	doff = d - buf;
108 	expand_string(&buf, &length, doff + 1);
109 	d = buf + doff;
110     }
111     if (dolastarg) run_add_arg (buf);
112     /* put each word into run_argv, allocating it as we go */
113     if (buf) free (buf);
114     free (run_prog);
115 }
116 
117 
118 
119 void
run_add_arg_p(int * iargc,size_t * iarg_allocated,char *** iargv,const char * s)120 run_add_arg_p (int *iargc, size_t *iarg_allocated, char ***iargv,
121 	       const char *s)
122 {
123     /* allocate more argv entries if we've run out */
124     if (*iargc >= *iarg_allocated)
125     {
126 	*iarg_allocated += 50;
127 	*iargv = xnrealloc (*iargv, *iarg_allocated, sizeof (char **));
128     }
129 
130     if (s)
131 	(*iargv)[(*iargc)++] = xstrdup (s);
132     else
133 	(*iargv)[*iargc] = NULL;	/* not post-incremented on purpose! */
134 }
135 
136 
137 
138 void
run_add_arg(const char * s)139 run_add_arg (const char *s)
140 {
141     run_add_arg_p (&run_argc, &run_arg_allocated, &run_argv, s);
142 }
143 
144 
145 
146 int
run_exec(const char * stin,const char * stout,const char * sterr,int flags)147 run_exec (const char *stin, const char *stout, const char *sterr, int flags)
148 {
149     int shin, shout, sherr;
150     int mode_out, mode_err;
151     int status;
152     int rc = -1;
153     int rerrno = 0;
154     int pid, w;
155 
156 #ifdef POSIX_SIGNALS
157     sigset_t sigset_mask, sigset_omask;
158     struct sigaction act, iact, qact;
159 
160 #else
161 #ifdef BSD_SIGNALS
162     int mask;
163     struct sigvec vec, ivec, qvec;
164 
165 #else
166     RETSIGTYPE (*istat) (), (*qstat) ();
167 #endif
168 #endif
169 
170     if (trace)
171     {
172 	cvs_outerr (
173 #ifdef SERVER_SUPPORT
174 		    server_active ? "S" :
175 #endif
176 		    " ", 1);
177 	cvs_outerr (" -> system (", 0);
178 	run_print (stderr);
179 	cvs_outerr (")\n", 0);
180     }
181     if (noexec && (flags & RUN_REALLY) == 0)
182 	return 0;
183 
184     /* make sure that we are null terminated, since we didn't calloc */
185     run_add_arg (NULL);
186 
187     /* setup default file descriptor numbers */
188     shin = 0;
189     shout = 1;
190     sherr = 2;
191 
192     /* set the file modes for stdout and stderr */
193     mode_out = mode_err = O_WRONLY | O_CREAT;
194     mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC);
195     mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC);
196 
197     if (stin && (shin = open (stin, O_RDONLY)) == -1)
198     {
199 	rerrno = errno;
200 	error (0, errno, "cannot open %s for reading (prog %s)",
201 	       stin, run_argv[0]);
202 	goto out0;
203     }
204     if (stout && (shout = open (stout, mode_out, 0666)) == -1)
205     {
206 	rerrno = errno;
207 	error (0, errno, "cannot open %s for writing (prog %s)",
208 	       stout, run_argv[0]);
209 	goto out1;
210     }
211     if (sterr && (flags & RUN_COMBINED) == 0)
212     {
213 	if ((sherr = open (sterr, mode_err, 0666)) == -1)
214 	{
215 	    rerrno = errno;
216 	    error (0, errno, "cannot open %s for writing (prog %s)",
217 		   sterr, run_argv[0]);
218 	    goto out2;
219 	}
220     }
221 
222     /* Make sure we don't flush this twice, once in the subprocess.  */
223     cvs_flushout();
224     cvs_flusherr();
225 
226     /* The output files, if any, are now created.  Do the fork and dups.
227 
228        We use vfork not so much for a performance boost (the
229        performance boost, if any, is modest on most modern unices),
230        but for the sake of systems without a memory management unit,
231        which find it difficult or impossible to implement fork at all
232        (e.g. Amiga).  The other solution is spawn (see
233        windows-NT/run.c).  */
234 
235 #ifdef HAVE_VFORK
236     pid = vfork ();
237 #else
238     pid = fork ();
239 #endif
240     if (pid == 0)
241     {
242 #ifdef SIGINFO
243        signal (SIGINFO, SIG_DFL);
244 #endif
245 #ifdef SETXID_SUPPORT
246 	if (flags & RUN_UNSETXID) {
247 	    (void) setgid (getgid ());
248 	    (void) setuid (getuid ());
249 	}
250 #endif
251 
252 	if (shin != 0)
253 	{
254 	    (void) dup2 (shin, 0);
255 	    (void) close (shin);
256 	}
257 	if (shout != 1)
258 	{
259 	    (void) dup2 (shout, 1);
260 	    (void) close (shout);
261 	}
262 	if (flags & RUN_COMBINED)
263 	    (void) dup2 (1, 2);
264 	else if (sherr != 2)
265 	{
266 	    (void) dup2 (sherr, 2);
267 	    (void) close (sherr);
268 	}
269 
270 #ifdef SETXID_SUPPORT
271 	/*
272 	** This prevents a user from creating a privileged shell
273 	** from the text editor when the SETXID_SUPPORT option is selected.
274 	*/
275 	if (!strcmp (run_argv[0], Editor) && setegid (getgid ()))
276 	{
277 	    error (0, errno, "cannot set egid to gid");
278 	    _exit (127);
279 	}
280 #endif
281 
282 	/* dup'ing is done.  try to run it now */
283 	(void) execvp (run_argv[0], run_argv);
284 	error (0, errno, "cannot exec %s", run_argv[0]);
285 	_exit (127);
286     }
287     else if (pid == -1)
288     {
289 	rerrno = errno;
290 	goto out;
291     }
292 
293     /* the parent.  Ignore some signals for now */
294 #ifdef POSIX_SIGNALS
295     if (flags & RUN_SIGIGNORE)
296     {
297 	act.sa_handler = SIG_IGN;
298 	(void) sigemptyset (&act.sa_mask);
299 	act.sa_flags = 0;
300 	(void) sigaction (SIGINT, &act, &iact);
301 	(void) sigaction (SIGQUIT, &act, &qact);
302     }
303     else
304     {
305 	(void) sigemptyset (&sigset_mask);
306 	(void) sigaddset (&sigset_mask, SIGINT);
307 	(void) sigaddset (&sigset_mask, SIGQUIT);
308 	(void) sigprocmask (SIG_SETMASK, &sigset_mask, &sigset_omask);
309     }
310 #else
311 #ifdef BSD_SIGNALS
312     if (flags & RUN_SIGIGNORE)
313     {
314 	memset (&vec, 0, sizeof vec);
315 	vec.sv_handler = SIG_IGN;
316 	(void) sigvec (SIGINT, &vec, &ivec);
317 	(void) sigvec (SIGQUIT, &vec, &qvec);
318     }
319     else
320 	mask = sigblock (sigmask (SIGINT) | sigmask (SIGQUIT));
321 #else
322     istat = signal (SIGINT, SIG_IGN);
323     qstat = signal (SIGQUIT, SIG_IGN);
324 #endif
325 #endif
326 
327     /* wait for our process to die and munge return status */
328 #ifdef POSIX_SIGNALS
329     while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
330 	;
331 #else
332     while ((w = wait (&status)) != pid)
333     {
334 	if (w == -1 && errno != EINTR)
335 	    break;
336     }
337 #endif
338 
339     if (w == -1)
340     {
341 	rc = -1;
342 	rerrno = errno;
343     }
344 #ifndef VMS /* status is return status */
345     else if (WIFEXITED (status))
346 	rc = WEXITSTATUS (status);
347     else if (WIFSIGNALED (status))
348     {
349 	if (WTERMSIG (status) == SIGPIPE)
350 	    error (1, 0, "broken pipe");
351 	rc = 2;
352     }
353     else
354 	rc = 1;
355 #else /* VMS */
356     rc = WEXITSTATUS (status);
357 #endif /* VMS */
358 
359     /* restore the signals */
360 #ifdef POSIX_SIGNALS
361     if (flags & RUN_SIGIGNORE)
362     {
363 	(void) sigaction (SIGINT, &iact, NULL);
364 	(void) sigaction (SIGQUIT, &qact, NULL);
365     }
366     else
367 	(void) sigprocmask (SIG_SETMASK, &sigset_omask, NULL);
368 #else
369 #ifdef BSD_SIGNALS
370     if (flags & RUN_SIGIGNORE)
371     {
372 	(void) sigvec (SIGINT, &ivec, NULL);
373 	(void) sigvec (SIGQUIT, &qvec, NULL);
374     }
375     else
376 	(void) sigsetmask (mask);
377 #else
378     (void) signal (SIGINT, istat);
379     (void) signal (SIGQUIT, qstat);
380 #endif
381 #endif
382 
383     /* cleanup the open file descriptors */
384   out:
385     if (sterr)
386 	(void) close (sherr);
387     else
388 	/* ensure things are received by the parent in the correct order
389 	 * relative to the protocol pipe
390 	 */
391 	cvs_flusherr();
392   out2:
393     if (stout)
394 	(void) close (shout);
395     else
396 	/* ensure things are received by the parent in the correct order
397 	 * relative to the protocol pipe
398 	 */
399 	cvs_flushout();
400   out1:
401     if (stin)
402 	(void) close (shin);
403 
404   out0:
405     if (rerrno)
406 	errno = rerrno;
407     return rc;
408 }
409 
410 
411 
412 void
run_print(FILE * fp)413 run_print (FILE *fp)
414 {
415     int i;
416     void (*outfn) (const char *, size_t);
417 
418     if (fp == stderr)
419 	outfn = cvs_outerr;
420     else if (fp == stdout)
421 	outfn = cvs_output;
422     else
423     {
424 	error (1, 0, "internal error: bad argument to run_print");
425 	/* Solely to placate gcc -Wall.
426 	   FIXME: it'd be better to use a function named `fatal' that
427 	   is known never to return.  Then kludges wouldn't be necessary.  */
428 	outfn = NULL;
429     }
430 
431     for (i = 0; i < run_argc; i++)
432     {
433 	(*outfn) ("'", 1);
434 	(*outfn) (run_argv[i], 0);
435 	(*outfn) ("'", 1);
436 	if (i != run_argc - 1)
437 	    (*outfn) (" ", 1);
438     }
439 }
440 
441 
442 
443 /* Return value is NULL for error, or if noexec was set.  If there was an
444    error, return NULL and I'm not sure whether errno was set (the Red Hat
445    Linux 4.1 popen manpage was kind of vague but discouraging; and the noexec
446    case complicates this even aside from popen behavior).  */
447 FILE *
run_popen(const char * cmd,const char * mode)448 run_popen (const char *cmd, const char *mode)
449 {
450     TRACE (TRACE_FUNCTION, "run_popen (%s,%s)", cmd, mode);
451     if (noexec)
452 	return NULL;
453 
454     return popen (cmd, mode);
455 }
456 
457 
458 
459 /* Work around an OpenSSH problem: it can put its standard file
460    descriptors into nonblocking mode, which will mess us up if we
461    share file descriptions with it.  The simplest workaround is
462    to create an intervening process between OpenSSH and the
463    actual stderr.  */
464 
465 static void
work_around_openssh_glitch(void)466 work_around_openssh_glitch (void)
467 {
468     pid_t pid;
469     int stderr_pipe[2];
470     struct stat sb;
471 
472     /* Do nothing unless stderr is a file that is affected by
473        nonblocking mode.  */
474     if (!(fstat (STDERR_FILENO, &sb) == 0
475           && (S_ISFIFO (sb.st_mode) || S_ISSOCK (sb.st_mode)
476               || S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode))))
477        return;
478 
479     if (pipe (stderr_pipe) < 0)
480        error (1, errno, "cannot create pipe");
481     pid = fork ();
482     if (pid < 0)
483        error (1, errno, "cannot fork");
484     if (pid != 0)
485     {
486        /* Still in child of original process.  Act like "cat -u".  */
487        char buf[1 << 13];
488        ssize_t inbytes;
489        pid_t w;
490        int status;
491 
492        if (close (stderr_pipe[1]) < 0)
493            error (1, errno, "cannot close pipe");
494 
495        while ((inbytes = read (stderr_pipe[0], buf, sizeof buf)) != 0)
496        {
497            size_t outbytes = 0;
498 
499            if (inbytes < 0)
500            {
501                if (errno == EINTR)
502                    continue;
503                error (1, errno, "reading from pipe");
504            }
505 
506            do
507            {
508                ssize_t w = write (STDERR_FILENO,
509                                   buf + outbytes, inbytes - outbytes);
510                if (w < 0)
511                {
512                    if (errno == EINTR)
513                      w = 0;
514                    if (w < 0)
515                      _exit (1);
516                }
517                outbytes += w;
518            }
519            while (inbytes != outbytes);
520        }
521 
522        /* Done processing output from grandchild.  Propagate
523           its exit status back to the parent.  */
524        while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
525            continue;
526        if (w < 0)
527            error (1, errno, "waiting for child");
528        if (!WIFEXITED (status))
529        {
530            if (WIFSIGNALED (status))
531                raise (WTERMSIG (status));
532            error (1, errno, "child did not exit cleanly");
533        }
534        _exit (WEXITSTATUS (status));
535     }
536 
537     /* Grandchild of original process.  */
538     if (close (stderr_pipe[0]) < 0)
539        error (1, errno, "cannot close pipe");
540 
541     if (stderr_pipe[1] != STDERR_FILENO)
542     {
543        if (dup2 (stderr_pipe[1], STDERR_FILENO) < 0)
544            error (1, errno, "cannot dup2 pipe");
545        if (close (stderr_pipe[1]) < 0)
546            error (1, errno, "cannot close pipe");
547     }
548 }
549 
550 
551 
552 int
piped_child(char * const * command,int * tofdp,int * fromfdp,bool fix_stderr)553 piped_child (char *const *command, int *tofdp, int *fromfdp, bool fix_stderr)
554 {
555     int pid;
556     int to_child_pipe[2];
557     int from_child_pipe[2];
558 
559     if (pipe (to_child_pipe) < 0)
560 	error (1, errno, "cannot create pipe");
561     if (pipe (from_child_pipe) < 0)
562 	error (1, errno, "cannot create pipe");
563 
564 #ifdef USE_SETMODE_BINARY
565     setmode (to_child_pipe[0], O_BINARY);
566     setmode (to_child_pipe[1], O_BINARY);
567     setmode (from_child_pipe[0], O_BINARY);
568     setmode (from_child_pipe[1], O_BINARY);
569 #endif
570 
571     pid = fork ();
572     if (pid < 0)
573 	error (1, errno, "cannot fork");
574     if (pid == 0)
575     {
576 #ifdef SIGINFO
577 	signal (SIGINFO, SIG_DFL);
578 #endif
579 	if (dup2 (to_child_pipe[0], STDIN_FILENO) < 0)
580 	    error (1, errno, "cannot dup2 pipe");
581 	if (close (to_child_pipe[1]) < 0)
582 	    error (1, errno, "cannot close pipe");
583 	if (close (from_child_pipe[0]) < 0)
584 	    error (1, errno, "cannot close pipe");
585 	if (dup2 (from_child_pipe[1], STDOUT_FILENO) < 0)
586 	    error (1, errno, "cannot dup2 pipe");
587 
588         if (fix_stderr)
589 	    work_around_openssh_glitch ();
590 
591 	/* Okay to cast out const below - execvp don't return nohow.  */
592 	execvp ((char *)command[0], (char **)command);
593 	error (1, errno, "cannot exec %s", command[0]);
594     }
595     if (close (to_child_pipe[0]) < 0)
596 	error (1, errno, "cannot close pipe");
597     if (close (from_child_pipe[1]) < 0)
598 	error (1, errno, "cannot close pipe");
599 
600     *tofdp = to_child_pipe[1];
601     *fromfdp = from_child_pipe[0];
602     return pid;
603 }
604 
605 
606 
607 int
run_piped(int * tofdp,int * fromfdp)608 run_piped (int *tofdp, int *fromfdp)
609 {
610     run_add_arg (NULL);
611     return piped_child (run_argv, tofdp, fromfdp, false);
612 }
613 
614 
615 
616 void
close_on_exec(int fd)617 close_on_exec (int fd)
618 {
619 #ifdef F_SETFD
620     if (fcntl (fd, F_SETFD, 1) == -1)
621 	error (1, errno, "can't set close-on-exec flag on %d", fd);
622 #endif
623 }
624