117721Speter/* run.c --- routines for executing subprocesses.
217721Speter
317721Speter   This file is part of GNU CVS.
417721Speter
517721Speter   GNU CVS is free software; you can redistribute it and/or modify it
617721Speter   under the terms of the GNU General Public License as published by the
717721Speter   Free Software Foundation; either version 2, or (at your option) any
817721Speter   later version.
917721Speter
1017721Speter   This program is distributed in the hope that it will be useful,
1117721Speter   but WITHOUT ANY WARRANTY; without even the implied warranty of
1217721Speter   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1325839Speter   GNU General Public License for more details.  */
1417721Speter
1517721Speter#include "cvs.h"
1617721Speter
1732785Speter#ifndef HAVE_UNISTD_H
1832785Speterextern int execvp PROTO((char *file, char **argv));
1917721Speter#endif
2017721Speter
2117721Speterstatic void run_add_arg PROTO((const char *s));
2217721Speter
2317721Speterextern char *strtok ();
2417721Speter
2517721Speter/*
2632785Speter * To exec a program under CVS, first call run_setup() to setup initial
2732785Speter * arguments.  The argument to run_setup will be parsed into whitespace
2832785Speter * separated words and added to the global run_argv list.
2917721Speter *
3017721Speter * Then, optionally call run_arg() for each additional argument that you'd like
3117721Speter * to pass to the executed program.
3217721Speter *
3317721Speter * Finally, call run_exec() to execute the program with the specified arguments.
3417721Speter * The execvp() syscall will be used, so that the PATH is searched correctly.
3517721Speter * File redirections can be performed in the call to run_exec().
3617721Speter */
3717721Speterstatic char **run_argv;
3817721Speterstatic int run_argc;
39175261Sobrienstatic size_t run_argc_allocated;
4017721Speter
41175261Sobrien
42175261Sobrien
43175261Sobrienvoid
44175261Sobrienrun_arg_free_p (int argc, char **argv)
45175261Sobrien{
46175261Sobrien    int i;
47175261Sobrien    for (i = 0; i < argc; i++)
48175261Sobrien	free (argv[i]);
49175261Sobrien}
50175261Sobrien
51175261Sobrien
52175261Sobrien
5317721Speter/* VARARGS */
5417721Spetervoid
5532785Speterrun_setup (prog)
5632785Speter    const char *prog;
5717721Speter{
5817721Speter    char *cp;
5925839Speter    char *run_prog;
6017721Speter
6117721Speter    /* clean out any malloc'ed values from run_argv */
62175261Sobrien    run_arg_free_p (run_argc, run_argv);
6317721Speter    run_argc = 0;
6417721Speter
6532785Speter    run_prog = xstrdup (prog);
6617721Speter
6717721Speter    /* put each word into run_argv, allocating it as we go */
6817721Speter    for (cp = strtok (run_prog, " \t"); cp; cp = strtok ((char *) NULL, " \t"))
6917721Speter	run_add_arg (cp);
7025839Speter    free (run_prog);
7117721Speter}
7217721Speter
7317721Spetervoid
7417721Speterrun_arg (s)
7517721Speter    const char *s;
7617721Speter{
7717721Speter    run_add_arg (s);
7817721Speter}
7917721Speter
80175261Sobrien
81175261Sobrien
82175261Sobrienvoid
83175261Sobrienrun_add_arg_p (iargc, iarg_allocated, iargv, s)
84175261Sobrien    int *iargc;
85175261Sobrien    size_t *iarg_allocated;
86175261Sobrien    char ***iargv;
8717721Speter    const char *s;
8817721Speter{
8917721Speter    /* allocate more argv entries if we've run out */
90175261Sobrien    if (*iargc >= *iarg_allocated)
9117721Speter    {
92175261Sobrien	*iarg_allocated += 50;
93175261Sobrien	*iargv = xrealloc (*iargv, *iarg_allocated * sizeof (char **));
9417721Speter    }
9517721Speter
9617721Speter    if (s)
97175261Sobrien	(*iargv)[(*iargc)++] = xstrdup (s);
9817721Speter    else
99175261Sobrien	(*iargv)[*iargc] = NULL;	/* not post-incremented on purpose! */
10017721Speter}
10117721Speter
102128266Speter
103128266Speter
104175261Sobrienstatic void
105175261Sobrienrun_add_arg (s)
106175261Sobrien    const char *s;
107175261Sobrien{
108175261Sobrien    run_add_arg_p (&run_argc, &run_argc_allocated, &run_argv, s);
109175261Sobrien}
110175261Sobrien
111175261Sobrien
112175261Sobrien
11317721Speterint
11417721Speterrun_exec (stin, stout, sterr, flags)
11532785Speter    const char *stin;
11632785Speter    const char *stout;
11732785Speter    const char *sterr;
11817721Speter    int flags;
11917721Speter{
12017721Speter    int shin, shout, sherr;
12117721Speter    int mode_out, mode_err;
12217721Speter    int status;
12317721Speter    int rc = -1;
12417721Speter    int rerrno = 0;
12517721Speter    int pid, w;
12617721Speter
12717721Speter#ifdef POSIX_SIGNALS
12817721Speter    sigset_t sigset_mask, sigset_omask;
12917721Speter    struct sigaction act, iact, qact;
13017721Speter
13117721Speter#else
13217721Speter#ifdef BSD_SIGNALS
13317721Speter    int mask;
13417721Speter    struct sigvec vec, ivec, qvec;
13517721Speter
13617721Speter#else
13717721Speter    RETSIGTYPE (*istat) (), (*qstat) ();
13817721Speter#endif
13917721Speter#endif
14017721Speter
14117721Speter    if (trace)
14217721Speter    {
14317721Speter#ifdef SERVER_SUPPORT
14425839Speter	cvs_outerr (server_active ? "S" : " ", 1);
14517721Speter#endif
14625839Speter	cvs_outerr ("-> system(", 0);
14717721Speter	run_print (stderr);
14825839Speter	cvs_outerr (")\n", 0);
14917721Speter    }
15017721Speter    if (noexec && (flags & RUN_REALLY) == 0)
151128266Speter	return 0;
15217721Speter
15317721Speter    /* make sure that we are null terminated, since we didn't calloc */
154128266Speter    run_add_arg ((char *)0);
15517721Speter
15617721Speter    /* setup default file descriptor numbers */
15717721Speter    shin = 0;
15817721Speter    shout = 1;
15917721Speter    sherr = 2;
16017721Speter
16117721Speter    /* set the file modes for stdout and stderr */
16217721Speter    mode_out = mode_err = O_WRONLY | O_CREAT;
16317721Speter    mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC);
16417721Speter    mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC);
16517721Speter
16617721Speter    if (stin && (shin = open (stin, O_RDONLY)) == -1)
16717721Speter    {
16817721Speter	rerrno = errno;
16917721Speter	error (0, errno, "cannot open %s for reading (prog %s)",
17017721Speter	       stin, run_argv[0]);
17117721Speter	goto out0;
17217721Speter    }
17317721Speter    if (stout && (shout = open (stout, mode_out, 0666)) == -1)
17417721Speter    {
17517721Speter	rerrno = errno;
17617721Speter	error (0, errno, "cannot open %s for writing (prog %s)",
17717721Speter	       stout, run_argv[0]);
17817721Speter	goto out1;
17917721Speter    }
18017721Speter    if (sterr && (flags & RUN_COMBINED) == 0)
18117721Speter    {
18217721Speter	if ((sherr = open (sterr, mode_err, 0666)) == -1)
18317721Speter	{
18417721Speter	    rerrno = errno;
18517721Speter	    error (0, errno, "cannot open %s for writing (prog %s)",
18617721Speter		   sterr, run_argv[0]);
18717721Speter	    goto out2;
18817721Speter	}
18917721Speter    }
19017721Speter
19117721Speter    /* Make sure we don't flush this twice, once in the subprocess.  */
192128266Speter    cvs_flushout();
193128266Speter    cvs_flusherr();
19417721Speter
19525839Speter    /* The output files, if any, are now created.  Do the fork and dups.
19625839Speter
19754427Speter       We use vfork not so much for a performance boost (the
19854427Speter       performance boost, if any, is modest on most modern unices),
19954427Speter       but for the sake of systems without a memory management unit,
20054427Speter       which find it difficult or impossible to implement fork at all
20154427Speter       (e.g. Amiga).  The other solution is spawn (see
20225839Speter       windows-NT/run.c).  */
20325839Speter
20417721Speter#ifdef HAVE_VFORK
20517721Speter    pid = vfork ();
20617721Speter#else
20717721Speter    pid = fork ();
20817721Speter#endif
20917721Speter    if (pid == 0)
21017721Speter    {
21117721Speter	if (shin != 0)
21217721Speter	{
21317721Speter	    (void) dup2 (shin, 0);
21417721Speter	    (void) close (shin);
21517721Speter	}
21617721Speter	if (shout != 1)
21717721Speter	{
21817721Speter	    (void) dup2 (shout, 1);
21917721Speter	    (void) close (shout);
22017721Speter	}
22117721Speter	if (flags & RUN_COMBINED)
22217721Speter	    (void) dup2 (1, 2);
22317721Speter	else if (sherr != 2)
22417721Speter	{
22517721Speter	    (void) dup2 (sherr, 2);
22617721Speter	    (void) close (sherr);
22717721Speter	}
22817721Speter
22966525Speter#ifdef SETXID_SUPPORT
23066525Speter	/*
23166525Speter	** This prevents a user from creating a privileged shell
23266525Speter	** from the text editor when the SETXID_SUPPORT option is selected.
23366525Speter	*/
23466525Speter	if (!strcmp (run_argv[0], Editor) && setegid (getgid ()))
23566525Speter	{
23666525Speter	    error (0, errno, "cannot set egid to gid");
23766525Speter	    _exit (127);
23866525Speter	}
23966525Speter#endif
24066525Speter
24117721Speter	/* dup'ing is done.  try to run it now */
24217721Speter	(void) execvp (run_argv[0], run_argv);
24317721Speter	error (0, errno, "cannot exec %s", run_argv[0]);
24417721Speter	_exit (127);
24517721Speter    }
24617721Speter    else if (pid == -1)
24717721Speter    {
24817721Speter	rerrno = errno;
24917721Speter	goto out;
25017721Speter    }
25117721Speter
25217721Speter    /* the parent.  Ignore some signals for now */
25317721Speter#ifdef POSIX_SIGNALS
25417721Speter    if (flags & RUN_SIGIGNORE)
25517721Speter    {
25617721Speter	act.sa_handler = SIG_IGN;
25717721Speter	(void) sigemptyset (&act.sa_mask);
25817721Speter	act.sa_flags = 0;
25917721Speter	(void) sigaction (SIGINT, &act, &iact);
26017721Speter	(void) sigaction (SIGQUIT, &act, &qact);
26117721Speter    }
26217721Speter    else
26317721Speter    {
26417721Speter	(void) sigemptyset (&sigset_mask);
26517721Speter	(void) sigaddset (&sigset_mask, SIGINT);
26617721Speter	(void) sigaddset (&sigset_mask, SIGQUIT);
26717721Speter	(void) sigprocmask (SIG_SETMASK, &sigset_mask, &sigset_omask);
26817721Speter    }
26917721Speter#else
27017721Speter#ifdef BSD_SIGNALS
27117721Speter    if (flags & RUN_SIGIGNORE)
27217721Speter    {
273128266Speter	memset ((char *)&vec, 0, sizeof (vec));
27417721Speter	vec.sv_handler = SIG_IGN;
27517721Speter	(void) sigvec (SIGINT, &vec, &ivec);
27617721Speter	(void) sigvec (SIGQUIT, &vec, &qvec);
27717721Speter    }
27817721Speter    else
27917721Speter	mask = sigblock (sigmask (SIGINT) | sigmask (SIGQUIT));
28017721Speter#else
28117721Speter    istat = signal (SIGINT, SIG_IGN);
28217721Speter    qstat = signal (SIGQUIT, SIG_IGN);
28317721Speter#endif
28417721Speter#endif
28517721Speter
28617721Speter    /* wait for our process to die and munge return status */
28717721Speter#ifdef POSIX_SIGNALS
28817721Speter    while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
28917721Speter	;
29017721Speter#else
29117721Speter    while ((w = wait (&status)) != pid)
29217721Speter    {
29317721Speter	if (w == -1 && errno != EINTR)
29417721Speter	    break;
29517721Speter    }
29617721Speter#endif
29717721Speter
29817721Speter    if (w == -1)
29917721Speter    {
30017721Speter	rc = -1;
30117721Speter	rerrno = errno;
30217721Speter    }
30317721Speter#ifndef VMS /* status is return status */
30417721Speter    else if (WIFEXITED (status))
30517721Speter	rc = WEXITSTATUS (status);
30617721Speter    else if (WIFSIGNALED (status))
30717721Speter    {
30817721Speter	if (WTERMSIG (status) == SIGPIPE)
30917721Speter	    error (1, 0, "broken pipe");
31017721Speter	rc = 2;
31117721Speter    }
31217721Speter    else
31317721Speter	rc = 1;
31417721Speter#else /* VMS */
31517721Speter    rc = WEXITSTATUS (status);
31617721Speter#endif /* VMS */
31717721Speter
31817721Speter    /* restore the signals */
31917721Speter#ifdef POSIX_SIGNALS
32017721Speter    if (flags & RUN_SIGIGNORE)
32117721Speter    {
322128266Speter	(void) sigaction (SIGINT, &iact, (struct sigaction *)NULL);
323128266Speter	(void) sigaction (SIGQUIT, &qact, (struct sigaction *)NULL);
32417721Speter    }
32517721Speter    else
326128266Speter	(void) sigprocmask (SIG_SETMASK, &sigset_omask, (sigset_t *)NULL);
32717721Speter#else
32817721Speter#ifdef BSD_SIGNALS
32917721Speter    if (flags & RUN_SIGIGNORE)
33017721Speter    {
331128266Speter	(void) sigvec (SIGINT, &ivec, (struct sigvec *)NULL);
332128266Speter	(void) sigvec (SIGQUIT, &qvec, (struct sigvec *)NULL);
33317721Speter    }
33417721Speter    else
33517721Speter	(void) sigsetmask (mask);
33617721Speter#else
33717721Speter    (void) signal (SIGINT, istat);
33817721Speter    (void) signal (SIGQUIT, qstat);
33917721Speter#endif
34017721Speter#endif
34117721Speter
34217721Speter    /* cleanup the open file descriptors */
34317721Speter  out:
34417721Speter    if (sterr)
34517721Speter	(void) close (sherr);
34681404Speter    else
34781404Speter	/* ensure things are received by the parent in the correct order
34881404Speter	 * relative to the protocol pipe
34981404Speter	 */
35081404Speter	cvs_flusherr();
35117721Speter  out2:
35217721Speter    if (stout)
35317721Speter	(void) close (shout);
35481404Speter    else
35581404Speter	/* ensure things are received by the parent in the correct order
35681404Speter	 * relative to the protocol pipe
35781404Speter	 */
35881404Speter	cvs_flushout();
35917721Speter  out1:
36017721Speter    if (stin)
36117721Speter	(void) close (shin);
36217721Speter
36317721Speter  out0:
36417721Speter    if (rerrno)
36517721Speter	errno = rerrno;
366128266Speter    return rc;
36717721Speter}
36817721Speter
369128266Speter
370128266Speter
37117721Spetervoid
37217721Speterrun_print (fp)
37317721Speter    FILE *fp;
37417721Speter{
37517721Speter    int i;
37625839Speter    void (*outfn) PROTO ((const char *, size_t));
37717721Speter
37825839Speter    if (fp == stderr)
37925839Speter	outfn = cvs_outerr;
38025839Speter    else if (fp == stdout)
38125839Speter	outfn = cvs_output;
38225839Speter    else
38332785Speter    {
38425839Speter	error (1, 0, "internal error: bad argument to run_print");
38532785Speter	/* Solely to placate gcc -Wall.
38632785Speter	   FIXME: it'd be better to use a function named `fatal' that
38732785Speter	   is known never to return.  Then kludges wouldn't be necessary.  */
38832785Speter	outfn = NULL;
38932785Speter    }
39025839Speter
39117721Speter    for (i = 0; i < run_argc; i++)
39217721Speter    {
39325839Speter	(*outfn) ("'", 1);
39425839Speter	(*outfn) (run_argv[i], 0);
39525839Speter	(*outfn) ("'", 1);
39617721Speter	if (i != run_argc - 1)
39725839Speter	    (*outfn) (" ", 1);
39817721Speter    }
39917721Speter}
40017721Speter
40132785Speter/* Return value is NULL for error, or if noexec was set.  If there was an
40232785Speter   error, return NULL and I'm not sure whether errno was set (the Red Hat
40332785Speter   Linux 4.1 popen manpage was kind of vague but discouraging; and the noexec
40432785Speter   case complicates this even aside from popen behavior).  */
40532785Speter
40617721SpeterFILE *
40717721Speterrun_popen (cmd, mode)
40817721Speter    const char *cmd;
40917721Speter    const char *mode;
41017721Speter{
41117721Speter    if (trace)
41254427Speter	(void) fprintf (stderr, "%s-> run_popen(%s,%s)\n",
41354427Speter			CLIENT_SERVER_STR, cmd, mode);
41417721Speter    if (noexec)
41517721Speter	return (NULL);
41617721Speter
41717721Speter    return (popen (cmd, mode));
41817721Speter}
41917721Speter
420175261Sobrien
421175261Sobrien
422175261Sobrien/* Work around an OpenSSH problem: it can put its standard file
423175261Sobrien   descriptors into nonblocking mode, which will mess us up if we
424175261Sobrien   share file descriptions with it.  The simplest workaround is
425175261Sobrien   to create an intervening process between OpenSSH and the
426175261Sobrien   actual stderr.  */
427175261Sobrien
428175261Sobrienstatic void
429175261Sobrienwork_around_openssh_glitch (void)
430175261Sobrien{
431175261Sobrien    pid_t pid;
432175261Sobrien    int stderr_pipe[2];
433175261Sobrien    struct stat sb;
434175261Sobrien
435175261Sobrien    /* Do nothing unless stderr is a file that is affected by
436175261Sobrien       nonblocking mode.  */
437175261Sobrien    if (!(fstat (STDERR_FILENO, &sb) == 0
438175261Sobrien          && (S_ISFIFO (sb.st_mode) || S_ISSOCK (sb.st_mode)
439175261Sobrien              || S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode))))
440175261Sobrien	return;
441175261Sobrien
442175261Sobrien    if (pipe (stderr_pipe) < 0)
443175261Sobrien	error (1, errno, "cannot create pipe");
444175261Sobrien    pid = fork ();
445175261Sobrien    if (pid < 0)
446175261Sobrien	error (1, errno, "cannot fork");
447175261Sobrien    if (pid != 0)
448175261Sobrien    {
449175261Sobrien	/* Still in child of original process.  Act like "cat -u".  */
450175261Sobrien	char buf[1 << 13];
451175261Sobrien	ssize_t inbytes;
452175261Sobrien	pid_t w;
453175261Sobrien	int status;
454175261Sobrien
455175261Sobrien	if (close (stderr_pipe[1]) < 0)
456175261Sobrien	    error (1, errno, "cannot close pipe");
457175261Sobrien
458175261Sobrien	while ((inbytes = read (stderr_pipe[0], buf, sizeof buf)) != 0)
459175261Sobrien	{
460175261Sobrien	    size_t outbytes = 0;
461175261Sobrien
462175261Sobrien	    if (inbytes < 0)
463175261Sobrien	    {
464175261Sobrien		if (errno == EINTR)
465175261Sobrien		    continue;
466175261Sobrien		error (1, errno, "reading from pipe");
467175261Sobrien	    }
468175261Sobrien
469175261Sobrien	    do
470175261Sobrien	    {
471175261Sobrien		ssize_t w = write (STDERR_FILENO,
472175261Sobrien				   buf + outbytes, inbytes - outbytes);
473175261Sobrien		if (w < 0)
474175261Sobrien		{
475175261Sobrien		    if (errno == EINTR)
476175261Sobrien			w = 0;
477175261Sobrien		    if (w < 0)
478175261Sobrien			_exit (1);
479175261Sobrien		}
480175261Sobrien		outbytes += w;
481175261Sobrien	    }
482175261Sobrien	    while (inbytes != outbytes);
483175261Sobrien	}
484175261Sobrien
485175261Sobrien	/* Done processing output from grandchild.  Propagate
486175261Sobrien	   its exit status back to the parent.  */
487175261Sobrien	while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR)
488175261Sobrien	    continue;
489175261Sobrien	if (w < 0)
490175261Sobrien	    error (1, errno, "waiting for child");
491175261Sobrien	if (!WIFEXITED (status))
492175261Sobrien	{
493175261Sobrien	    if (WIFSIGNALED (status))
494175261Sobrien		raise (WTERMSIG (status));
495175261Sobrien	    error (1, errno, "child did not exit cleanly");
496175261Sobrien	}
497175261Sobrien	_exit (WEXITSTATUS (status));
498175261Sobrien    }
499175261Sobrien
500175261Sobrien    /* Grandchild of original process.  */
501175261Sobrien    if (close (stderr_pipe[0]) < 0)
502175261Sobrien	error (1, errno, "cannot close pipe");
503175261Sobrien
504175261Sobrien    if (stderr_pipe[1] != STDERR_FILENO)
505175261Sobrien    {
506175261Sobrien	if (dup2 (stderr_pipe[1], STDERR_FILENO) < 0)
507175261Sobrien	    error (1, errno, "cannot dup2 pipe");
508175261Sobrien	if (close (stderr_pipe[1]) < 0)
509175261Sobrien	    error (1, errno, "cannot close pipe");
510175261Sobrien    }
511175261Sobrien}
512175261Sobrien
513175261Sobrien
514175261Sobrien
51517721Speterint
516175261Sobrienpiped_child (command, tofdp, fromfdp, fix_stderr)
517128266Speter     const char **command;
51817721Speter     int *tofdp;
51917721Speter     int *fromfdp;
520175261Sobrien     int fix_stderr;
52117721Speter{
52217721Speter    int pid;
52317721Speter    int to_child_pipe[2];
52417721Speter    int from_child_pipe[2];
52517721Speter
52617721Speter    if (pipe (to_child_pipe) < 0)
52717721Speter	error (1, errno, "cannot create pipe");
52817721Speter    if (pipe (from_child_pipe) < 0)
52917721Speter	error (1, errno, "cannot create pipe");
53017721Speter
53126801Speter#ifdef USE_SETMODE_BINARY
53226801Speter    setmode (to_child_pipe[0], O_BINARY);
53326801Speter    setmode (to_child_pipe[1], O_BINARY);
53426801Speter    setmode (from_child_pipe[0], O_BINARY);
53526801Speter    setmode (from_child_pipe[1], O_BINARY);
53626801Speter#endif
53726801Speter
53817721Speter    pid = fork ();
53917721Speter    if (pid < 0)
54017721Speter	error (1, errno, "cannot fork");
54117721Speter    if (pid == 0)
54217721Speter    {
54317721Speter	if (dup2 (to_child_pipe[0], STDIN_FILENO) < 0)
54454427Speter	    error (1, errno, "cannot dup2 pipe");
54517721Speter	if (close (to_child_pipe[1]) < 0)
54654427Speter	    error (1, errno, "cannot close pipe");
54717721Speter	if (close (from_child_pipe[0]) < 0)
54854427Speter	    error (1, errno, "cannot close pipe");
54917721Speter	if (dup2 (from_child_pipe[1], STDOUT_FILENO) < 0)
55054427Speter	    error (1, errno, "cannot dup2 pipe");
55117721Speter
552175261Sobrien        if (fix_stderr)
553175261Sobrien	    work_around_openssh_glitch ();
554175261Sobrien
555175261Sobrien	/* Okay to cast out const below - execvp don't return nohow.  */
556128266Speter	execvp ((char *)command[0], (char **)command);
55754427Speter	error (1, errno, "cannot exec %s", command[0]);
55817721Speter    }
55917721Speter    if (close (to_child_pipe[0]) < 0)
56054427Speter	error (1, errno, "cannot close pipe");
56117721Speter    if (close (from_child_pipe[1]) < 0)
56254427Speter	error (1, errno, "cannot close pipe");
56317721Speter
56417721Speter    *tofdp = to_child_pipe[1];
56517721Speter    *fromfdp = from_child_pipe[0];
56617721Speter    return pid;
56717721Speter}
56817721Speter
56917721Speter
57017721Spetervoid
57117721Speterclose_on_exec (fd)
57217721Speter     int fd;
57317721Speter{
57481404Speter#ifdef F_SETFD
575128266Speter    if (fcntl (fd, F_SETFD, 1) == -1)
57654427Speter	error (1, errno, "can't set close-on-exec flag on %d", fd);
57717721Speter#endif
57817721Speter}
579