1 /* source: xio-progcall.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
4 
5 /* this file contains common code dealing with program calls (exec, system) */
6 
7 #include "xiosysincludes.h"
8 #include "xioopen.h"
9 
10 #include "xio-process.h"
11 #include "xio-progcall.h"
12 
13 #include "xio-socket.h"
14 
15 
16 /* these options are used by address pty too */
17 #if HAVE_OPENPTY
18 const struct optdesc opt_openpty = { "openpty",   NULL, OPT_OPENPTY,     GROUP_PTY,   PH_BIGEN, TYPE_BOOL, 	OFUNC_SPEC };
19 #endif /* HAVE_OPENPTY */
20 #if HAVE_DEV_PTMX || HAVE_DEV_PTC
21 const struct optdesc opt_ptmx    = { "ptmx",      NULL, OPT_PTMX,        GROUP_PTY,   PH_BIGEN, TYPE_BOOL, 	OFUNC_SPEC };
22 #endif
23 
24 #if WITH_EXEC || WITH_SYSTEM
25 
26 #define MAXPTYNAMELEN 64
27 
28 const struct optdesc opt_fdin    = { "fdin",      NULL, OPT_FDIN,        GROUP_FORK,   PH_PASTBIGEN,   TYPE_USHORT,	OFUNC_SPEC };
29 const struct optdesc opt_fdout   = { "fdout",     NULL, OPT_FDOUT,       GROUP_FORK,   PH_PASTBIGEN,   TYPE_USHORT,	OFUNC_SPEC };
30 const struct optdesc opt_path    = { "path",      NULL, OPT_PATH,        GROUP_EXEC,   PH_PREEXEC,     TYPE_STRING,	OFUNC_SPEC };
31 const struct optdesc opt_pipes   = { "pipes",     NULL, OPT_PIPES,       GROUP_FORK,   PH_BIGEN,       TYPE_BOOL, 	OFUNC_SPEC };
32 #if HAVE_PTY
33 const struct optdesc opt_pty     = { "pty",       NULL, OPT_PTY,         GROUP_FORK,   PH_BIGEN, TYPE_BOOL, 	OFUNC_SPEC };
34 #endif
35 const struct optdesc opt_stderr  = { "stderr",    NULL, OPT_STDERR,      GROUP_FORK,   PH_PASTFORK,        TYPE_BOOL,	OFUNC_SPEC };
36 const struct optdesc opt_nofork  = { "nofork",    NULL, OPT_NOFORK,      GROUP_FORK,   PH_BIGEN,       TYPE_BOOL,       OFUNC_SPEC };
37 const struct optdesc opt_sighup  = { "sighup",    NULL, OPT_SIGHUP,      GROUP_PARENT, PH_LATE,        TYPE_CONST,      OFUNC_SIGNAL, SIGHUP };
38 const struct optdesc opt_sigint  = { "sigint",    NULL, OPT_SIGINT,      GROUP_PARENT, PH_LATE,        TYPE_CONST,      OFUNC_SIGNAL, SIGINT };
39 const struct optdesc opt_sigquit = { "sigquit",   NULL, OPT_SIGQUIT,     GROUP_PARENT, PH_LATE,        TYPE_CONST,      OFUNC_SIGNAL, SIGQUIT };
40 
41 
42 /* fork for exec/system, but return before exec'ing.
43    return=0: is child process
44    return>0: is parent process
45    return<0: error occurred, assume parent process and no child exists !!!
46  */
_xioopen_foxec(int xioflags,struct single * fd,unsigned groups,struct opt ** copts,int * duptostderr)47 int _xioopen_foxec(int xioflags,	/* XIO_RDONLY etc. */
48 		struct single *fd,
49 		unsigned groups,
50 		   struct opt **copts,	/* in: opts; out: opts for child */
51 		   int *duptostderr	/* out: redirect stderr to output fd */
52 		) {
53    struct opt *popts;	/* parent process options */
54    int numleft;
55    int d, sv[2], rdpip[2], wrpip[2];
56    int rw = (xioflags & XIO_ACCMODE);
57    bool usepipes = false;
58 #if HAVE_PTY
59    int ptyfd = -1, ttyfd = -1;
60    bool usebestpty = false;	/* use the best available way to open pty */
61 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
62    bool useptmx = false;	/* use /dev/ptmx or equivalent */
63 #endif
64 #if HAVE_OPENPTY
65    bool useopenpty = false;	/* try only openpty */
66 #endif	/* HAVE_OPENPTY */
67    bool usepty = false;		/* any of the pty options is selected */
68    char ptyname[MAXPTYNAMELEN];
69 #endif /* HAVE_PTY */
70    pid_t pid = 0;	/* mostly int */
71    short fdi = 0, fdo = 1;
72    short result;
73    bool withstderr = false;
74    bool nofork = false;
75    bool withfork;
76 
77    popts = moveopts(*copts, GROUP_ALL);
78    if (applyopts_single(fd, popts, PH_INIT) < 0)  return -1;
79    applyopts2(-1, popts, PH_INIT, PH_EARLY);
80 
81    retropt_bool(popts, OPT_NOFORK, &nofork);
82    withfork = !nofork;
83 
84    retropt_bool(popts, OPT_PIPES, &usepipes);
85 #if HAVE_PTY
86    retropt_bool(popts, OPT_PTY, &usebestpty);
87 #if HAVE_OPENPTY
88    retropt_bool(popts, OPT_OPENPTY, &useopenpty);
89 #endif
90 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
91    retropt_bool(popts, OPT_PTMX, &useptmx);
92 #endif
93    usepty = (usebestpty
94 #if HAVE_OPENPTY
95 	     || useopenpty
96 #endif
97 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
98 	     || useptmx
99 #endif
100 	     );
101    if (usepipes && usepty) {
102       Warn("_xioopen_foxec(): options \"pipes\" and \"pty\" must not be specified together; ignoring \"pipes\"");
103       usepipes = false;
104    }
105 #endif /* HAVE_PTY */
106 
107    if (retropt_ushort(popts, OPT_FDIN,  (unsigned short *)&fdi) >= 0) {
108       if ((xioflags&XIO_ACCMODE) == XIO_RDONLY) {
109 	 Error("_xioopen_foxec(): option fdin is useless in read-only mode");
110       }
111    }
112    if (retropt_ushort(popts, OPT_FDOUT, (unsigned short *)&fdo) >= 0) {
113       if ((xioflags&XIO_ACCMODE) == XIO_WRONLY) {
114 	 Error("_xioopen_foxec(): option fdout is useless in write-only mode");
115       }
116    }
117 
118    if (withfork) {
119       if (!(xioflags&XIO_MAYCHILD)) {
120 	 Error("cannot fork off child process here");
121 	 /*!! free something */
122 	 return -1;
123       }
124       fd->flags |= XIO_DOESCHILD;
125 
126 #if HAVE_PTY
127       Notice2("forking off child, using %s for %s",
128 	    &("socket\0\0pipes\0\0\0pty\0\0\0\0\0"[(usepipes<<3)|(usepty<<4)]),
129 	      ddirection[rw]);
130 #else
131       Notice2("forking off child, using %s for %s",
132 	      &("socket\0\0pipes\0\0\0"[(usepipes<<3)]),
133 	      ddirection[rw]);
134 #endif /* HAVE_PTY */
135    }
136    applyopts(-1, popts, PH_PREBIGEN);
137 
138    if (!withfork) {
139       /*0 struct single *stream1, *stream2;*/
140 
141       if (!(xioflags & XIO_MAYEXEC /* means exec+nofork */)) {
142 	 Error("option nofork is not allowed here");
143 	 /*!! free something */
144 	 return -1;
145       }
146       fd->flags |= XIO_DOESEXEC;
147 
148       free(*copts);
149       *copts = moveopts(popts, GROUP_ALL);
150 
151 #if 0 /*!! */
152       if (sock1->tag == XIO_TAG_DUAL) {
153 	 stream1 = &sock1->dual.stream[0]->stream;
154 	 stream2 = &sock1->dual.stream[1]->stream;
155       } else {
156 	 stream1 = &sock1->stream;
157 	 stream2 = &sock1->stream;
158       }
159       if (stream1->dtype == DATA_READLINE || stream2->dtype == DATA_READLINE ||
160 	  stream1->dtype == DATA_OPENSSL  || stream2->dtype == DATA_OPENSSL
161 	  ) {
162 	 Error("with option nofork, openssl and readline in address1 do not work");
163       }
164       if (stream1->lineterm != LINETERM_RAW ||
165 	  stream2->lineterm != LINETERM_RAW ||
166 	  stream1->ignoreeof || stream2->ignoreeof) {
167 	 Warn("due to option nofork, address1 options for lineterm and igoreeof do not apply");
168       }
169 #endif
170 
171       /* remember: fdin is the fd where the sub program reads from, thus it is
172 	 sock0[]'s read fd */
173       /*! problem: when fdi==WRFD(sock[0]) or fdo==RDFD(sock[0]) */
174       if (rw != XIO_WRONLY) {
175 	 if (XIO_GETWRFD(sock[0]/*!!*/) == fdo) {
176 	    if (Fcntl_l(fdo, F_SETFD, 0) < 0) {
177 	       Warn2("fcntl(%d, F_SETFD, 0): %s", fdo, strerror(errno));
178 	    }
179 	 } else {
180 	    /* make sure that the internal diagnostic socket pair fds do not conflict
181 	       with our choices */
182 	    diag_reserve_fd(fdo);
183 	    if (Dup2(XIO_GETWRFD(sock[0]), fdo) < 0) {
184 	       Error3("dup2(%d, %d): %s",
185 		      XIO_GETWRFD(sock[0]), fdo, strerror(errno));
186 	    }
187 	 }
188 	 /*0 Info2("dup2(%d, %d)", XIO_GETRDFD(sock[0]), fdi);*/
189       }
190       if (rw != XIO_RDONLY) {
191 	 if (XIO_GETRDFD(sock[0]) == fdi) {
192 	    if (Fcntl_l(fdi, F_SETFD, 0) < 0) {
193 	       Warn2("fcntl(%d, F_SETFD, 0): %s", fdi, strerror(errno));
194 	    }
195 	 } else {
196 	    /* make sure that the internal diagnostic socket pair fds do not conflict
197 	       with our choices */
198 	    diag_reserve_fd(fdi);
199 	    if (Dup2(XIO_GETRDFD(sock[0]), fdi) < 0) {
200 	       Error3("dup2(%d, %d): %s)",
201 		      XIO_GETRDFD(sock[0]), fdi, strerror(errno));
202 	    }
203 	    /*0 Info2("dup2(%d, %d)", XIO_GETWRFD(sock[0]), fdo);*/
204 	 }
205       }
206    } else
207 #if HAVE_PTY
208    if (usepty) {
209 
210 #if defined(HAVE_DEV_PTMX)
211 #  define PTMX "/dev/ptmx"	/* Linux */
212 #elif HAVE_DEV_PTC
213 #  define PTMX "/dev/ptc"	/* AIX 4.3.3 */
214 #endif
215       fd->dtype = XIODATA_PTY;
216 #if HAVE_DEV_PTMX || HAVE_DEV_PTC
217       if (usebestpty || useptmx) {
218 	 if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) {
219 	    Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s",
220 		  strerror(errno));
221 	    /*!*/
222 	 } else {
223 	    /*0 Info2("open(\"%s\", O_RDWR|O_NOCTTY, 0620) -> %d", PTMX, ptyfd);*/
224 	 }
225 	 if (ptyfd >= 0 && ttyfd < 0) {
226 	    char *tn = NULL;
227 	    /* we used PTMX before forking */
228 	    extern char *ptsname(int);
229 #if HAVE_GRANTPT	/* AIX, not Linux */
230 	    if (Grantpt(ptyfd)/*!*/ < 0) {
231 	       Warn2("grantpt(%d): %s", ptyfd, strerror(errno));
232 	    }
233 #endif /* HAVE_GRANTPT */
234 #if HAVE_UNLOCKPT
235 	    if (Unlockpt(ptyfd)/*!*/ < 0) {
236 	       Warn2("unlockpt(%d): %s", ptyfd, strerror(errno));
237 	    }
238 #endif /* HAVE_UNLOCKPT */
239 #if HAVE_PROTOTYPE_LIB_ptsname	/* AIX, not Linux */
240 	    if ((tn = Ptsname(ptyfd)) == NULL) {
241 	       Warn2("ptsname(%d): %s", ptyfd, strerror(errno));
242 	    }
243 #endif /* HAVE_PROTOTYPE_LIB_ptsname */
244 	    if (tn == NULL) {
245 	       if ((tn = Ttyname(ptyfd)) == NULL) {
246 		  Error2("ttyname(%d): %s", ptyfd, strerror(errno));
247 	       }
248 	    }
249 	    ptyname[0] = '\0'; strncat(ptyname, tn, MAXPTYNAMELEN-1);
250 	    if ((ttyfd = Open(tn, O_RDWR|O_NOCTTY, 0620)) < 0) {
251 	       Warn2("open(\"%s\", O_RDWR|O_NOCTTY, 0620): %s", tn, strerror(errno));
252 	    } else {
253 	       /*0 Info2("open(\"%s\", O_RDWR|O_NOCTTY, 0620) -> %d", tn, ttyfd);*/
254 	    }
255 
256 #ifdef I_PUSH
257 	    /* Linux: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> -1 EINVAL */
258 	    /* AIX:   I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 1 */
259 	    /* SunOS: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */
260 	    /* HP-UX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */
261 	    if (Ioctl(ttyfd, I_FIND, "ldterm\0") == 0) {
262 	       Ioctl(ttyfd, I_PUSH, "ptem\0\0\0");	/* 0 */ /* padding for AdressSanitizer */
263 	       Ioctl(ttyfd, I_PUSH, "ldterm\0");	/* 0 */
264 	       Ioctl(ttyfd, I_PUSH, "ttcompat");	/* HP-UX: -1 */
265 	    }
266 #endif
267 
268 #if 0	    /* the following block need not work */
269 
270 	    if (ttyfd >= 0 && ((tn = Ttyname(ttyfd)) == NULL)) {
271 	       Warn2("ttyname(%d): %s", ttyfd, strerror(errno));
272 	    }
273 	    if (tn == NULL) {
274 	       Error("could not open pty");
275 	       return -1;
276 	    }
277 #endif
278 	    Info1("opened pseudo terminal %s", tn);
279 	 }
280       }
281 #endif /* HAVE_DEV_PTMX || HAVE_DEV_PTC */
282 #if HAVE_OPENPTY
283       if (ptyfd < 0) {
284 	 int result;
285 	 if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
286 	    Error4("openpty(%p, %p, %p, NULL, NULL): %s",
287 		   &ptyfd, &ttyfd, ptyname, strerror(errno));
288 	    return -1;
289 	 }
290       }
291 #endif /* HAVE_OPENPTY */
292       free(*copts);
293       if ((*copts = moveopts(popts, GROUP_TERMIOS|GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
294 	 return -1;
295       }
296       applyopts_cloexec(ptyfd, popts);/*!*/
297       /* exec:...,pty did not kill child process under some circumstances */
298       if (fd->howtoend == END_UNSPEC) {
299 	 fd->howtoend = END_CLOSE_KILL;
300       }
301 
302       /* this for parent, was after fork */
303       applyopts(ptyfd, popts, PH_FD);
304       applyopts(ptyfd, popts, PH_LATE);
305       if (applyopts_single(fd, popts, PH_LATE) < 0)  return -1;
306 
307       fd->fd = ptyfd;
308 
309       /* this for child, was after fork */
310       applyopts(ttyfd, *copts, PH_FD);
311    } else
312 #endif /* HAVE_PTY */
313    if (usepipes) {
314       struct opt *popts2, *copts2;
315 
316       if (rw == XIO_RDWR)
317 	 fd->dtype = XIODATA_2PIPE;
318       if (rw != XIO_WRONLY) {
319 	 if (Pipe(rdpip) < 0) {
320 	    Error2("pipe(%p): %s", rdpip, strerror(errno));
321 	    return -1;
322 	 }
323       }
324       /*0 Info2("pipe({%d,%d})", rdpip[0], rdpip[1]);*/
325       /* rdpip[0]: read by socat; rdpip[1]: write by child */
326       free(*copts);
327       if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS))
328 	  == NULL) {
329 	 return -1;
330       }
331 
332       popts2 = copyopts(popts, GROUP_ALL);
333       copts2 = copyopts(*copts, GROUP_ALL);
334 
335       if (rw != XIO_WRONLY) {
336 	 applyopts_cloexec(rdpip[0], popts);
337 	 applyopts(rdpip[0], popts, PH_FD);
338 	 applyopts(rdpip[1], *copts, PH_FD);
339       }
340 
341       if (rw != XIO_RDONLY) {
342 	 if (Pipe(wrpip) < 0) {
343 	    Error2("pipe(%p): %s", wrpip, strerror(errno));
344 	    return -1;
345 	 }
346       }
347       /*0 Info2("pipe({%d,%d})", wrpip[0], wrpip[1]);*/
348 
349       /* wrpip[1]: write by socat; wrpip[0]: read by child */
350       if (rw != XIO_RDONLY) {
351 	 applyopts_cloexec(wrpip[1], popts2);
352 	 applyopts(wrpip[1], popts2, PH_FD);
353 	 applyopts(wrpip[0], copts2, PH_FD);
354       }
355       if (fd->howtoend == END_UNSPEC) {
356 	 fd->howtoend = END_CLOSE_KILL;
357       }
358 
359       /* this for parent, was after fork */
360       switch (rw) {
361       case XIO_RDONLY: fd->fd = rdpip[0]; break;
362       case XIO_WRONLY: fd->fd = wrpip[1]; break;
363       case XIO_RDWR:   fd->fd = rdpip[0];
364 	 fd->para.exec.fdout = wrpip[1];
365 	 break;
366       }
367       applyopts(fd->fd, popts, PH_FD);
368       applyopts(fd->fd, popts, PH_LATE);
369       if (applyopts_single(fd, popts, PH_LATE) < 0)  return -1;
370    } else {
371       d = AF_UNIX;
372       retropt_int(popts, OPT_PROTOCOL_FAMILY, &d);
373       result = xiosocketpair(popts, d, SOCK_STREAM, 0, sv);
374       if (result < 0) {
375 	 return -1;
376       }
377       /*0 Info5("socketpair(%d, %d, %d, {%d,%d})",
378 	d, type, protocol, sv[0], sv[1]);*/
379       free(*copts);
380       if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
381 	 return -1;
382       }
383       applyopts(sv[0], *copts, PH_PASTSOCKET);
384       applyopts(sv[1], popts, PH_PASTSOCKET);
385 
386       applyopts_cloexec(sv[0], *copts);
387       applyopts(sv[0], *copts, PH_FD);
388       applyopts(sv[1], popts, PH_FD);
389 
390       applyopts(sv[0], *copts, PH_PREBIND);
391       applyopts(sv[0], *copts, PH_BIND);
392       applyopts(sv[0], *copts, PH_PASTBIND);
393       applyopts(sv[1], popts, PH_PREBIND);
394       applyopts(sv[1], popts, PH_BIND);
395       applyopts(sv[1], popts, PH_PASTBIND);
396 
397       if (fd->howtoend == END_UNSPEC) {
398 	 fd->howtoend = END_SHUTDOWN_KILL;
399       }
400 
401       /* this for parent, was after fork */
402       fd->fd = sv[0];
403       applyopts(fd->fd, popts, PH_FD);
404       applyopts(fd->fd, popts, PH_LATE);
405       if (applyopts_single(fd, popts, PH_LATE) < 0)  return -1;
406    }
407    /*0   if ((optpr = copyopts(*copts, GROUP_PROCESS)) == NULL)
408      return -1;*/
409    retropt_bool(*copts, OPT_STDERR, &withstderr);
410 
411    xiosetchilddied();	/* set SIGCHLD handler */
412 
413    if (withfork) {
414       pid = xio_fork(true, E_ERROR);
415       if (pid < 0) {
416 	 return -1;
417       }
418    }
419    if (!withfork || pid == 0) {	/* child */
420       if (withfork) {
421 	 /* The child should have default handling for SIGCHLD. */
422 	 /* In particular, it's not defined whether ignoring SIGCHLD is inheritable. */
423 	 if (Signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
424 	    Warn1("signal(SIGCHLD, SIG_DFL): %s", strerror(errno));
425 	 }
426 
427 #if HAVE_PTY
428 	 if (usepty) {
429 	    Close(ptyfd);
430 	    if (rw != XIO_RDONLY && fdi != ttyfd) {
431 	       /* make sure that the internal diagnostic socket pair fds do not conflict
432 		  with our choices */
433 	       diag_reserve_fd(fdi);
434 	       if (Dup2(ttyfd, fdi) < 0) {
435 		  Error3("dup2(%d, %d): %s", ttyfd, fdi, strerror(errno));
436 		  return -1; }
437 	       /*0 Info2("dup2(%d, %d)", ttyfd, fdi);*/
438 	    }
439 	    if (rw != XIO_WRONLY && fdo != ttyfd) {
440 	       /* make sure that the internal diagnostic socket pair fds do not conflict
441 		  with our choices */
442 	       diag_reserve_fd(fdo);
443 	       if (Dup2(ttyfd, fdo) < 0) {
444 		  Error3("dup2(%d, %d): %s", ttyfd, fdo, strerror(errno));
445 		  return -1; }
446 	       /*0 Info2("dup2(%d, %d)", ttyfd, fdo);*/
447 	    }
448 	    if ((rw == XIO_RDONLY || fdi != ttyfd) &&
449 		(rw == XIO_WRONLY || fdo != ttyfd)) {
450 	       applyopts_cloexec(ttyfd, *copts);
451 	    }
452 
453 	    applyopts(ttyfd, *copts, PH_LATE);
454 
455 	    applyopts(ttyfd, *copts, PH_LATE2);
456 	 } else
457 #endif /* HAVE_PTY */
458 	    if (usepipes) {
459 	       /* we might have a temporary conflict between what FDs are
460 		  currently allocated, and which are to be used. We try to find
461 		  a graceful solution via temporary descriptors */
462 	       int tmpi, tmpo;
463 
464 	       if (rw != XIO_WRONLY)  Close(rdpip[0]);
465 	       if (rw != XIO_RDONLY)  Close(wrpip[1]);
466 	       if (fdi == rdpip[1]) {	/* a conflict here */
467 		  if ((tmpi = Dup(wrpip[0])) < 0) {
468 		     Error2("dup(%d): %s", wrpip[0], strerror(errno));
469 		     return -1;
470 		  }
471 		  /*0 Info2("dup(%d) -> %d", wrpip[0], tmpi);*/
472 		  rdpip[1] = tmpi;
473 	       }
474 	       if (fdo == wrpip[0]) {	/* a conflict here */
475 		  if ((tmpo = Dup(rdpip[1])) < 0) {
476 		     Error2("dup(%d): %s", rdpip[1], strerror(errno));
477 		     return -1;
478 		  }
479 		  /*0 Info2("dup(%d) -> %d", rdpip[1], tmpo);*/
480 		  wrpip[0] = tmpo;
481 	       }
482 
483 	       if (rw != XIO_WRONLY && rdpip[1] != fdo) {
484 		  /* make sure that the internal diagnostic socket pair fds do not conflict
485 		     with our choices */
486 		  diag_reserve_fd(fdo);
487 		  if (Dup2(rdpip[1], fdo) < 0) {
488 		     Error3("dup2(%d, %d): %s", rdpip[1], fdo, strerror(errno));
489 		     return -1;
490 		  }
491 		  Close(rdpip[1]);
492 		  /*0 Info2("dup2(%d, %d)", rdpip[1], fdo);*/
493 		  /*0 applyopts_cloexec(fdo, *copts);*/
494 	       }
495 	       if (rw != XIO_RDONLY && wrpip[0] != fdi) {
496 		  /* make sure that the internal diagnostic socket pair fds do not conflict
497 		     with our choices */
498 		  diag_reserve_fd(fdi);
499 		  if (Dup2(wrpip[0], fdi) < 0) {
500 		     Error3("dup2(%d, %d): %s", wrpip[0], fdi, strerror(errno));
501 		     return -1;
502 		  }
503 		  Close(wrpip[0]);
504 		  /*0 Info2("dup2(%d, %d)", wrpip[0], fdi);*/
505 		  /*0 applyopts_cloexec(wrpip[0], *copts);*/	/* option is already consumed! */
506 		  /* applyopts_cloexec(fdi, *copts);*/	/* option is already consumed! */
507 	       }
508 
509 	       applyopts(fdi, *copts, PH_LATE);
510 	       applyopts(fdo, *copts, PH_LATE);
511 	       applyopts(fdi, *copts, PH_LATE2);
512 	       applyopts(fdo, *copts, PH_LATE2);
513 
514 	    } else {	/* socketpair */
515 	       Close(sv[0]);
516 	       if (rw != XIO_RDONLY && fdi != sv[1]) {
517 		  /* make sure that the internal diagnostic socket pair fds do not conflict
518 		     with our choices */
519 		  diag_reserve_fd(fdi);
520 		  if (Dup2(sv[1], fdi) < 0) {
521 		     Error3("dup2(%d, %d): %s", sv[1], fdi, strerror(errno));
522 		     return -1; }
523 		  /*0 Info2("dup2(%d, %d)", sv[1], fdi);*/
524 	       }
525 	       if (rw != XIO_WRONLY && fdo != sv[1]) {
526 		  /* make sure that the internal diagnostic socket pair fds do not conflict
527 		     with our choices */
528 		  diag_reserve_fd(fdo);
529 		  if (Dup2(sv[1], fdo) < 0) {
530 		     Error3("dup2(%d, %d): %s", sv[1], fdo, strerror(errno));
531 		     return -1; }
532 		  /*0 Info2("dup2(%d, %d)", sv[1], fdo);*/
533 	       }
534 	       if (fdi != sv[1] && fdo != sv[1]) {
535 		  applyopts_cloexec(sv[1], *copts);
536 		  Close(sv[1]);
537 	       }
538 
539 	       applyopts(fdi, *copts, PH_LATE);
540 	       applyopts(fdi, *copts, PH_LATE2);
541 	    }
542       } /* withfork */
543       else {
544 	 applyopts(-1, *copts, PH_LATE);
545 	 applyopts(-1, *copts, PH_LATE2);
546       }
547       _xioopen_setdelayeduser();
548       if (withstderr) {
549 	 *duptostderr = fdo;
550       } else {
551 	 *duptostderr = -1;
552       }
553 
554       return 0;	/* indicate child process */
555    }
556 
557    /* for parent (this is our socat process) */
558    Notice1("forked off child process "F_pid, pid);
559 
560 #if 0
561    if ((popts = copyopts(*copts,
562 			 GROUP_FD|GROUP_TERMIOS|GROUP_FORK|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_FIFO)) == NULL)
563       return STAT_RETRYLATER;
564 #endif
565 
566 #if HAVE_PTY
567    if (usepty) {
568       if (Close(ttyfd) < 0) {
569 	 Info2("close(%d): %s", ttyfd, strerror(errno));
570       }
571    } else
572 #endif /* HAVE_PTY */
573    if (usepipes) {
574       if (rw == XIO_RDONLY)  Close(rdpip[1]);
575       if (rw == XIO_WRONLY)  Close(wrpip[0]);
576    } else {
577       Close(sv[1]);
578    }
579    fd->para.exec.pid = pid;
580 
581    if (applyopts_single(fd, popts, PH_LATE) < 0)  return -1;
582    applyopts_signal(fd, popts);
583    if ((numleft = leftopts(popts)) > 0) {
584       Error1("%d option(s) could not be used", numleft);
585       showleft(popts);
586       return STAT_NORETRY;
587    }
588 
589    return pid;	/* indicate parent (main) process */
590 }
591 #endif /* WITH_EXEC || WITH_SYSTEM */
592 
593 
setopt_path(struct opt * opts,char ** path)594 int setopt_path(struct opt *opts, char **path) {
595    if (retropt_string(opts, OPT_PATH, path) >= 0) {
596       if (setenv("PATH", *path, 1) < 0) {
597 	 Error1("setenv(\"PATH\", \"%s\", 1): insufficient space", *path);
598 	 return -1;
599       }
600    }
601    return 0;
602 }
603