Version 1.8.0.2 - CVE-2024-54661: Arbitrary file overwrite in readline.sh
[socat.git] / xio-progcall.c
blobf980ef93c9a2525c97696f1ed25b02763dffde2b
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 */
5 /* this file contains common code dealing with program calls (exec, system) */
7 #include "xiosysincludes.h"
8 #include "xioopen.h"
10 #include "xio-process.h"
11 #include "xio-named.h"
12 #include "xio-progcall.h"
14 #include "xio-socket.h"
17 /* these options are used by address pty too */
18 #if HAVE_OPENPTY
19 const struct optdesc opt_openpty = { "openpty", NULL, OPT_OPENPTY, GROUP_PTY, PH_BIGEN, TYPE_BOOL, OFUNC_SPEC };
20 #endif /* HAVE_OPENPTY */
21 #if HAVE_DEV_PTMX || HAVE_DEV_PTC
22 const struct optdesc opt_ptmx = { "ptmx", NULL, OPT_PTMX, GROUP_PTY, PH_BIGEN, TYPE_BOOL, OFUNC_SPEC };
23 #endif
24 const struct optdesc opt_sitout_eio = { "sitout-eio", NULL, OPT_SITOUT_EIO, GROUP_PTY, PH_OFFSET, TYPE_TIMEVAL, OFUNC_OFFSET, XIO_OFFSETOF(para.exec.sitout_eio), XIO_SIZEOF(para.exec.sitout_eio) };
26 #if WITH_EXEC || WITH_SYSTEM
28 #define MAXPTYNAMELEN 64
30 const struct optdesc opt_fdin = { "fdin", NULL, OPT_FDIN, GROUP_FORK, PH_PASTBIGEN, TYPE_USHORT, OFUNC_SPEC };
31 const struct optdesc opt_fdout = { "fdout", NULL, OPT_FDOUT, GROUP_FORK, PH_PASTBIGEN, TYPE_USHORT, OFUNC_SPEC };
32 const struct optdesc opt_path = { "path", NULL, OPT_PATH, GROUP_EXEC, PH_PREEXEC, TYPE_STRING, OFUNC_SPEC };
33 const struct optdesc opt_pipes = { "pipes", NULL, OPT_PIPES, GROUP_FORK, PH_BIGEN, TYPE_BOOL, OFUNC_SPEC };
34 #if HAVE_PTY
35 const struct optdesc opt_pty = { "pty", NULL, OPT_PTY, GROUP_FORK, PH_BIGEN, TYPE_BOOL, OFUNC_SPEC };
36 #endif
37 const struct optdesc opt_stderr = { "stderr", NULL, OPT_STDERR, GROUP_FORK, PH_PASTFORK, TYPE_BOOL, OFUNC_SPEC };
38 const struct optdesc opt_nofork = { "nofork", NULL, OPT_NOFORK, GROUP_FORK, PH_BIGEN, TYPE_BOOL, OFUNC_SPEC };
39 const struct optdesc opt_sighup = { "sighup", NULL, OPT_SIGHUP, GROUP_PARENT, PH_LATE, TYPE_CONST, OFUNC_SIGNAL, SIGHUP };
40 const struct optdesc opt_sigint = { "sigint", NULL, OPT_SIGINT, GROUP_PARENT, PH_LATE, TYPE_CONST, OFUNC_SIGNAL, SIGINT };
41 const struct optdesc opt_sigquit = { "sigquit", NULL, OPT_SIGQUIT, GROUP_PARENT, PH_LATE, TYPE_CONST, OFUNC_SIGNAL, SIGQUIT };
44 /* fork for exec/system, but return before exec'ing.
45 return=0: is child process
46 return>0: is parent process
47 return<0: error occurred, assume parent process and no child exists !!!
49 int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
50 struct single *sfd,
51 groups_t groups,
52 struct opt **optsp, /* in: opts; out: opts for parent/child */
53 int *duptostderr /* out: redirect stderr to output fd */
54 ) {
55 struct opt *opts; /* common options */
56 struct opt *popts = NULL; /* parent options */
57 struct opt *copts; /* child options */
58 int numleft;
59 int sv[2], rdpip[2], wrpip[2];
60 int rw = (xioflags & XIO_ACCMODE);
61 bool usepipes = false;
62 #if HAVE_PTY
63 int ptyfd = -1, ttyfd = -1;
64 bool usebestpty = false; /* use the best available way to open pty */
65 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
66 bool useptmx = false; /* use /dev/ptmx or equivalent */
67 #endif
68 #if HAVE_OPENPTY
69 bool useopenpty = false; /* try only openpty */
70 #endif /* HAVE_OPENPTY */
71 bool usepty = false; /* any of the pty options is selected */
72 char ptyname[MAXPTYNAMELEN];
73 #endif /* HAVE_PTY */
74 pid_t pid = 0; /* mostly int */
75 short fdi = 0, fdo = 1;
76 short result;
77 bool withstderr = false;
78 bool nofork = false;
79 bool withfork;
80 char *tn = NULL;
81 int trigger[2]; /* [0] watched by parent, [1] closed by child when ready */
83 opts = *optsp;
84 if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1;
85 applyopts2(sfd, -1, opts, PH_INIT, PH_EARLY);
87 retropt_bool(opts, OPT_NOFORK, &nofork);
88 withfork = !nofork;
90 retropt_bool(opts, OPT_PIPES, &usepipes);
91 #if HAVE_PTY
92 retropt_bool(opts, OPT_PTY, &usebestpty);
93 #if HAVE_OPENPTY
94 retropt_bool(opts, OPT_OPENPTY, &useopenpty);
95 #endif
96 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
97 retropt_bool(opts, OPT_PTMX, &useptmx);
98 #endif
99 usepty = (usebestpty
100 #if HAVE_OPENPTY
101 || useopenpty
102 #endif
103 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
104 || useptmx
105 #endif
107 if (usepipes && usepty) {
108 Warn("_xioopen_foxec(): options \"pipes\" and \"pty\" must not be specified together; ignoring \"pipes\"");
109 usepipes = false;
111 #endif /* HAVE_PTY */
113 if (retropt_ushort(opts, OPT_FDIN, (unsigned short *)&fdi) >= 0) {
114 if ((xioflags&XIO_ACCMODE) == XIO_RDONLY) {
115 Error("_xioopen_foxec(): option fdin is useless in read-only mode");
118 if (retropt_ushort(opts, OPT_FDOUT, (unsigned short *)&fdo) >= 0) {
119 if ((xioflags&XIO_ACCMODE) == XIO_WRONLY) {
120 Error("_xioopen_foxec(): option fdout is useless in write-only mode");
124 if (withfork) {
125 if (!(xioflags&XIO_MAYCHILD)) {
126 Error("cannot fork off child process here");
127 /*!! free something */
128 return -1;
130 sfd->flags |= XIO_DOESCHILD;
132 #if HAVE_PTY
133 Notice2("forking off child, using %s for %s",
134 &("socket\0\0pipes\0\0\0pty\0\0\0\0\0"[(usepipes<<3)|(usepty<<4)]),
135 ddirection[rw]);
136 #else
137 Notice2("forking off child, using %s for %s",
138 &("socket\0\0pipes\0\0\0"[(usepipes<<3)]),
139 ddirection[rw]);
140 #endif /* HAVE_PTY */
142 applyopts(sfd, -1, opts, PH_PREBIGEN);
144 if (!withfork) {
145 /*0 struct single *stream1, *stream2;*/
147 if (!(xioflags & XIO_MAYEXEC /* means exec+nofork */)) {
148 Error("option nofork is not allowed here");
149 /*!! free something */
150 return -1;
152 sfd->flags |= XIO_DOESEXEC;
154 /* Only one process, no parent,child */
155 if ((copts = moveopts(opts, GROUP_ALL)) == NULL) {
156 /*!! free something */
157 return -1;
160 #if 0 /*!! */
161 if (sock1->tag == XIO_TAG_DUAL) {
162 stream1 = &sock1->dual.stream[0]->stream;
163 stream2 = &sock1->dual.stream[1]->stream;
164 } else {
165 stream1 = &sock1->stream;
166 stream2 = &sock1->stream;
168 if (stream1->dtype == DATA_READLINE || stream2->dtype == DATA_READLINE ||
169 stream1->dtype == DATA_OPENSSL || stream2->dtype == DATA_OPENSSL
171 Error("with option nofork, openssl and readline in address1 do not work");
173 if (stream1->lineterm != LINETERM_RAW ||
174 stream2->lineterm != LINETERM_RAW ||
175 stream1->ignoreeof || stream2->ignoreeof) {
176 Warn("due to option nofork, address1 options for lineterm and igoreeof do not apply");
178 #endif
180 /* remember: fdin is the fd where the sub program reads from, thus it is
181 sock0[]'s read fd */
182 /*! problem: when fdi==WRFD(sock[0]) or fdo==RDFD(sock[0]) */
183 if (rw != XIO_WRONLY) {
184 if (XIO_GETWRFD(sock[0]/*!!*/) == fdo) {
185 if (Fcntl_l(fdo, F_SETFD, 0) < 0) {
186 Warn2("fcntl(%d, F_SETFD, 0): %s", fdo, strerror(errno));
188 } else {
189 /* make sure that the internal diagnostic socket pair fds do not conflict
190 with our choices */
191 diag_reserve_fd(fdo);
192 if (Dup2(XIO_GETWRFD(sock[0]), fdo) < 0) {
193 Error3("dup2(%d, %d): %s",
194 XIO_GETWRFD(sock[0]), fdo, strerror(errno));
197 /*0 Info2("dup2(%d, %d)", XIO_GETRDFD(sock[0]), fdi);*/
199 if (rw != XIO_RDONLY) {
200 if (XIO_GETRDFD(sock[0]) == fdi) {
201 if (Fcntl_l(fdi, F_SETFD, 0) < 0) {
202 Warn2("fcntl(%d, F_SETFD, 0): %s", fdi, strerror(errno));
204 } else {
205 /* make sure that the internal diagnostic socket pair fds do not conflict
206 with our choices */
207 diag_reserve_fd(fdi);
208 if (Dup2(XIO_GETRDFD(sock[0]), fdi) < 0) {
209 Error3("dup2(%d, %d): %s)",
210 XIO_GETRDFD(sock[0]), fdi, strerror(errno));
212 /*0 Info2("dup2(%d, %d)", XIO_GETWRFD(sock[0]), fdo);*/
215 /* !withfork */
216 } else /* withfork */
217 #if HAVE_PTY
218 if (usepty) {
220 #if defined(HAVE_DEV_PTMX)
221 # define PTMX "/dev/ptmx" /* Linux */
222 #elif HAVE_DEV_PTC
223 # define PTMX "/dev/ptc" /* AIX 4.3.3 */
224 #endif
225 sfd->dtype = XIODATA_PTY;
226 #if HAVE_DEV_PTMX || HAVE_DEV_PTC
227 if (usebestpty || useptmx) {
228 if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) {
229 Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s",
230 strerror(errno));
231 /*!*/
232 } else {
233 /*0 Info2("open(\"%s\", O_RDWR|O_NOCTTY, 0620) -> %d", PTMX, ptyfd);*/
235 if (ptyfd >= 0 && ttyfd < 0) {
236 /* we used PTMX before forking */
237 extern char *ptsname(int);
239 #if HAVE_PROTOTYPE_LIB_ptsname /* AIX, not Linux */
240 if ((tn = Ptsname(ptyfd)) == NULL) {
241 Warn2("ptsname(%d): %s", ptyfd, strerror(errno));
243 #endif /* HAVE_PROTOTYPE_LIB_ptsname */
244 if (tn == NULL) {
245 if ((tn = Ttyname(ptyfd)) == NULL) {
246 Error2("ttyname(%d): %s", ptyfd, strerror(errno));
249 ptyname[0] = '\0'; strncat(ptyname, tn, MAXPTYNAMELEN-1);
250 #if HAVE_GRANTPT /* AIX, not Linux */
251 if (Grantpt(ptyfd)/*!*/ < 0) {
252 Warn2("grantpt(%d): %s", ptyfd, strerror(errno));
254 #endif /* HAVE_GRANTPT */
255 #if HAVE_UNLOCKPT
256 if (Unlockpt(ptyfd)/*!*/ < 0) {
257 Warn2("unlockpt(%d): %s", ptyfd, strerror(errno));
259 #endif /* HAVE_UNLOCKPT */
262 #endif /* HAVE_DEV_PTMX || HAVE_DEV_PTC */
263 #if HAVE_OPENPTY
264 if (ptyfd < 0) {
265 int result;
266 if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
267 Error4("openpty(%p, %p, %p, NULL, NULL): %s",
268 &ptyfd, &ttyfd, ptyname, strerror(errno));
269 return -1;
272 #endif /* HAVE_OPENPTY */
273 /* withfork use_pty */
274 if ((copts = moveopts(opts, GROUP_TERMIOS|GROUP_FORK|GROUP_EXEC|GROUP_PROCESS|GROUP_NAMED)) == NULL) {
275 return -1;
277 popts = opts;
278 applyopts_cloexec(ptyfd, popts);/*!*/
279 /* exec:...,pty did not kill child process under some circumstances */
280 if (sfd->howtoend == END_UNSPEC) {
281 sfd->howtoend = END_CLOSE_KILL;
284 /* this for parent, was after fork */
285 applyopts(sfd, ptyfd, popts, PH_FD);
286 sfd->fd = ptyfd;
288 /* end withfork, use_pty */
289 } else /* end withfork, use_pty */
290 #endif /* HAVE_PTY */
292 if (usepipes) {
293 /* withfork usepipes */
294 struct opt *popts2 = NULL;
296 if (rw == XIO_RDWR)
297 sfd->dtype = XIODATA_2PIPE;
298 if (rw != XIO_WRONLY) {
299 if (Pipe(rdpip) < 0) {
300 Error2("pipe(%p): %s", rdpip, strerror(errno));
301 return -1;
304 /*0 Info2("pipe({%d,%d})", rdpip[0], rdpip[1]);*/
305 /* rdpip[0]: read by socat; rdpip[1]: write by child */
307 /* withfork usepipes */
308 if ((copts = moveopts(opts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS))
309 == NULL) {
310 return -1;
312 popts = opts;
313 if (sfd->dtype == XIODATA_2PIPE)
314 popts2 = copyopts(popts, GROUP_ALL);
316 if (rw != XIO_WRONLY) {
317 applyopts_cloexec(rdpip[0], popts);
318 applyopts(sfd, rdpip[0], popts, PH_FD);
319 applyopts(sfd, rdpip[1], copts, PH_FD);
322 if (rw != XIO_RDONLY) {
323 if (Pipe(wrpip) < 0) {
324 Error2("pipe(%p): %s", wrpip, strerror(errno));
325 return -1;
329 /* wrpip[1]: write by socat; wrpip[0]: read by child */
330 if (rw != XIO_RDONLY) {
331 applyopts_cloexec(wrpip[1], popts);
332 if (sfd->dtype == XIODATA_2PIPE)
333 applyopts(NULL, wrpip[1], popts2, PH_FD);
334 else
335 applyopts(NULL, wrpip[1], popts, PH_FD);
336 applyopts(NULL, wrpip[0], copts, PH_FD);
338 if (sfd->howtoend == END_UNSPEC) {
339 sfd->howtoend = END_CLOSE_KILL;
342 /* this for parent, was after fork */
343 switch (rw) {
344 case XIO_RDONLY: sfd->fd = rdpip[0]; break;
345 case XIO_WRONLY: sfd->fd = wrpip[1]; break;
346 case XIO_RDWR: sfd->fd = rdpip[0];
347 sfd->para.exec.fdout = wrpip[1];
348 break;
350 applyopts(sfd, -1, popts, PH_FD);
351 applyopts(sfd, -1, popts, PH_LATE);
352 if (applyopts_single(sfd, popts, PH_LATE) < 0)
353 return -1;
355 /* end withfork, use_pipes */
356 } else {
357 /* withfork, socketpair */
358 int pf;
360 pf = AF_UNIX;
361 retropt_socket_pf(opts, &pf);
362 result = xiosocketpair(opts, pf, SOCK_STREAM, 0, sv);
363 if (result < 0) {
364 return -1;
367 /* withfork socketpair */
368 if ((copts = moveopts(opts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
369 return -1;
371 popts = opts;
372 applyopts(sfd, sv[0], copts, PH_PASTSOCKET);
373 applyopts(sfd, sv[1], popts, PH_PASTSOCKET);
375 applyopts_cloexec(sv[0], copts);
376 applyopts(sfd, sv[0], copts, PH_FD);
377 applyopts(sfd, sv[1], popts, PH_FD);
379 applyopts(sfd, sv[0], copts, PH_PREBIND);
380 applyopts(sfd, sv[0], copts, PH_BIND);
381 applyopts(sfd, sv[0], copts, PH_PASTBIND);
382 applyopts(sfd, sv[1], popts, PH_PREBIND);
383 applyopts(sfd, sv[1], popts, PH_BIND);
384 applyopts(sfd, sv[1], popts, PH_PASTBIND);
386 if (sfd->howtoend == END_UNSPEC) {
387 sfd->howtoend = END_SHUTDOWN_KILL;
390 /* this for parent, was after fork */
391 sfd->fd = sv[0];
392 applyopts(sfd, -1, popts, PH_FD);
393 /* end withfork, socketpair */
395 retropt_bool(copts, OPT_STDERR, &withstderr);
397 xiosetchilddied(); /* set SIGCHLD handler */
399 if (withfork) {
400 Socketpair(PF_UNIX, SOCK_STREAM, 0, trigger);
401 pid = xio_fork(true, E_ERROR, 0);
402 if (pid < 0) {
403 return -1;
406 if (!withfork || pid == 0) { /* in single process, or in child */
407 applyopts_optgroup(sfd, -1, copts, GROUP_PROCESS);
408 if (withfork) {
409 Close(trigger[0]); /* in child: not needed here */
410 /* The child should have default handling for SIGCHLD. */
411 /* In particular, it's not defined whether ignoring SIGCHLD is inheritable. */
412 if (Signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
413 Warn1("signal(SIGCHLD, SIG_DFL): %s", strerror(errno));
416 #if HAVE_PTY
417 if (usepty) {
418 applyopts_named(tn, copts, PH_PREOPEN);
419 applyopts_named(tn, copts, PH_EARLY);
420 applyopts_named(tn, copts, PH_FD);
422 if (ttyfd < 0) {
423 if ((ttyfd = Open(tn, O_RDWR|O_NOCTTY, 0620)) < 0) {
424 Warn2("open(\"%s\", O_RDWR|O_NOCTTY, 0620): %s", tn, strerror(errno));
425 } else {
426 /*0 Info2("open(\"%s\", O_RDWR|O_NOCTTY, 0620) -> %d", tn, ttyfd);*/
428 } else {
429 if ((tn = Ttyname(ttyfd)) == NULL) {
430 Warn2("ttyname(%d): %s", ttyfd, strerror(errno));
434 #ifdef I_PUSH
435 /* Linux: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> -1 EINVAL */
436 /* AIX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 1 */
437 /* SunOS: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */
438 /* HP-UX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */
439 if (Ioctl(ttyfd, I_FIND, "ldterm\0") == 0) {
440 Ioctl(ttyfd, I_PUSH, "ptem\0\0\0"); /* 0 */ /* padding for AdressSanitizer */
441 Ioctl(ttyfd, I_PUSH, "ldterm\0"); /* 0 */
442 Ioctl(ttyfd, I_PUSH, "ttcompat"); /* HP-UX: -1 */
444 #endif
446 /* this for child, was after fork */
447 applyopts(sfd, ttyfd, copts, PH_FD);
449 Info1("opened pseudo terminal %s", tn);
450 Close(ptyfd);
451 if (rw != XIO_RDONLY && fdi != ttyfd) {
452 /* make sure that the internal diagnostic socket pair fds do not conflict
453 with our choices */
454 diag_reserve_fd(fdi);
455 if (Dup2(ttyfd, fdi) < 0) {
456 Error3("dup2(%d, %d): %s", ttyfd, fdi, strerror(errno));
457 return -1; }
458 /*0 Info2("dup2(%d, %d)", ttyfd, fdi);*/
460 if (rw != XIO_WRONLY && fdo != ttyfd) {
461 /* make sure that the internal diagnostic socket pair fds do not conflict
462 with our choices */
463 diag_reserve_fd(fdo);
464 if (Dup2(ttyfd, fdo) < 0) {
465 Error3("dup2(%d, %d): %s", ttyfd, fdo, strerror(errno));
466 return -1; }
467 /*0 Info2("dup2(%d, %d)", ttyfd, fdo);*/
469 if ((rw == XIO_RDONLY || fdi != ttyfd) &&
470 (rw == XIO_WRONLY || fdo != ttyfd)) {
471 applyopts_cloexec(ttyfd, copts);
474 applyopts(sfd, ttyfd, copts, PH_LATE);
475 applyopts(sfd, ttyfd, copts, PH_LATE2);
476 } else
477 #endif /* HAVE_PTY */
478 if (usepipes) {
479 /* we might have a temporary conflict between what FDs are
480 currently allocated, and which are to be used. We try to find
481 a graceful solution via temporary descriptors */
482 int tmpi, tmpo;
484 if (rw != XIO_WRONLY) Close(rdpip[0]);
485 if (rw != XIO_RDONLY) Close(wrpip[1]);
486 if (fdi == rdpip[1]) { /* a conflict here */
487 if ((tmpi = Dup(wrpip[0])) < 0) {
488 Error2("dup(%d): %s", wrpip[0], strerror(errno));
489 return -1;
491 /*0 Info2("dup(%d) -> %d", wrpip[0], tmpi);*/
492 rdpip[1] = tmpi;
494 if (fdo == wrpip[0]) { /* a conflict here */
495 if ((tmpo = Dup(rdpip[1])) < 0) {
496 Error2("dup(%d): %s", rdpip[1], strerror(errno));
497 return -1;
499 /*0 Info2("dup(%d) -> %d", rdpip[1], tmpo);*/
500 wrpip[0] = tmpo;
503 if (rw != XIO_WRONLY && rdpip[1] != fdo) {
504 /* make sure that the internal diagnostic socket pair fds do not conflict
505 with our choices */
506 diag_reserve_fd(fdo);
507 if (Dup2(rdpip[1], fdo) < 0) {
508 Error3("dup2(%d, %d): %s", rdpip[1], fdo, strerror(errno));
509 return -1;
511 Close(rdpip[1]);
513 if (rw != XIO_RDONLY && wrpip[0] != fdi) {
514 /* make sure that the internal diagnostic socket pair fds do not conflict
515 with our choices */
516 diag_reserve_fd(fdi);
517 if (Dup2(wrpip[0], fdi) < 0) {
518 Error3("dup2(%d, %d): %s", wrpip[0], fdi, strerror(errno));
519 return -1;
521 Close(wrpip[0]);
522 /* applyopts_cloexec(fdi, *copts);*/ /* option is already consumed! */
525 applyopts(sfd, fdi, copts, PH_LATE);
526 applyopts(sfd, fdo, copts, PH_LATE);
527 applyopts(sfd, fdi, copts, PH_LATE2);
528 applyopts(sfd, fdo, copts, PH_LATE2);
530 } else { /* socketpair */
531 Close(sv[0]);
532 if (rw != XIO_RDONLY && fdi != sv[1]) {
533 /* make sure that the internal diagnostic socket pair fds do not conflict
534 with our choices */
535 diag_reserve_fd(fdi);
536 if (Dup2(sv[1], fdi) < 0) {
537 Error3("dup2(%d, %d): %s", sv[1], fdi, strerror(errno));
538 return -1; }
539 /*0 Info2("dup2(%d, %d)", sv[1], fdi);*/
541 if (rw != XIO_WRONLY && fdo != sv[1]) {
542 /* make sure that the internal diagnostic socket pair fds do not conflict
543 with our choices */
544 diag_reserve_fd(fdo);
545 if (Dup2(sv[1], fdo) < 0) {
546 Error3("dup2(%d, %d): %s", sv[1], fdo, strerror(errno));
547 return -1; }
548 /*0 Info2("dup2(%d, %d)", sv[1], fdo);*/
550 if (fdi != sv[1] && fdo != sv[1]) {
551 applyopts_cloexec(sv[1], copts);
552 Close(sv[1]);
555 applyopts(sfd, fdi, copts, PH_LATE);
556 applyopts(sfd, fdi, copts, PH_LATE2);
558 if (withfork) {
559 Info("notifying parent that child process is ready");
560 Close(trigger[1]); /* in child, notify parent that ready */
562 } /* withfork */
563 else {
564 applyopts(sfd, -1, copts, PH_LATE);
565 applyopts(sfd, -1, copts, PH_LATE2);
567 _xioopen_setdelayeduser();
568 if (withstderr) {
569 *duptostderr = fdo;
570 } else {
571 *duptostderr = -1;
574 *optsp = copts;
575 return 0; /* indicate child process */
578 /* for parent (this is our socat process) */
579 Notice1("forked off child process "F_pid, pid);
580 Close(trigger[1]); /* in parent */
582 #if HAVE_PTY
583 if (usepty) {
584 # if 0
585 if (Close(ttyfd) < 0) {
586 Info2("close(%d): %s", ttyfd, strerror(errno));
588 # endif
589 } else
590 #endif /* HAVE_PTY */
591 if (usepipes) {
592 if (rw == XIO_RDONLY) Close(rdpip[1]);
593 if (rw == XIO_WRONLY) Close(wrpip[0]);
594 } else {
595 Close(sv[1]);
597 sfd->para.exec.pid = pid;
599 if (applyopts_single(sfd, popts, PH_LATE) < 0) return -1;
600 applyopts(sfd, -1, popts, PH_LATE);
601 applyopts(sfd, -1, popts, PH_LATE2);
602 applyopts(sfd, -1, popts, PH_PASTEXEC);
603 if ((numleft = leftopts(popts)) > 0) {
604 showleft(popts);
605 Error1("INTERNAL: %d option(s) remained unused", numleft);
606 return STAT_NORETRY;
610 struct pollfd fds[1];
611 fds[0].fd = trigger[0];
612 fds[0].events = POLLIN|POLLHUP;
613 Poll(fds, 1, -1);
614 Info("child process notified parent that it is ready");
617 #if HAVE_PTY
618 applyopts(sfd, ptyfd, popts, PH_LATE);
619 #endif /* HAVE_PTY */
620 if (applyopts_single(sfd, popts, PH_LATE) < 0)
621 return -1;
623 *optsp = popts;
624 return pid; /* indicate parent (main) process */
626 #endif /* WITH_EXEC || WITH_SYSTEM */
629 int setopt_path(struct opt *opts, char **path) {
630 if (retropt_string(opts, OPT_PATH, path) >= 0) {
631 if (setenv("PATH", *path, 1) < 0) {
632 Error1("setenv(\"PATH\", \"%s\", 1): insufficient space", *path);
633 return -1;
636 return 0;