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"
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 */
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
};
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
};
35 const struct optdesc opt_pty
= { "pty", NULL
, OPT_PTY
, GROUP_FORK
, PH_BIGEN
, TYPE_BOOL
, OFUNC_SPEC
};
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. */
52 struct opt
**optsp
, /* in: opts; out: opts for parent/child */
53 int *duptostderr
/* out: redirect stderr to output fd */
55 struct opt
*opts
; /* common options */
56 struct opt
*popts
= NULL
; /* parent options */
57 struct opt
*copts
; /* child options */
59 int d
, sv
[2], rdpip
[2], wrpip
[2];
60 int rw
= (xioflags
& XIO_ACCMODE
);
61 bool usepipes
= false;
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 */
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
];
74 pid_t pid
= 0; /* mostly int */
75 short fdi
= 0, fdo
= 1;
77 bool withstderr
= false;
81 int trigger
[2]; /* [0] watched by parent, [1] closed by child when ready */
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
);
90 retropt_bool(opts
, OPT_PIPES
, &usepipes
);
92 retropt_bool(opts
, OPT_PTY
, &usebestpty
);
94 retropt_bool(opts
, OPT_OPENPTY
, &useopenpty
);
96 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
97 retropt_bool(opts
, OPT_PTMX
, &useptmx
);
103 #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
107 if (usepipes
&& usepty
) {
108 Warn("_xioopen_foxec(): options \"pipes\" and \"pty\" must not be specified together; ignoring \"pipes\"");
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");
125 if (!(xioflags
&XIO_MAYCHILD
)) {
126 Error("cannot fork off child process here");
127 /*!! free something */
130 sfd
->flags
|= XIO_DOESCHILD
;
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)]),
137 Notice2("forking off child, using %s for %s",
138 &("socket\0\0pipes\0\0\0"[(usepipes
<<3)]),
140 #endif /* HAVE_PTY */
142 applyopts(sfd
, -1, opts
, PH_PREBIGEN
);
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 */
152 sfd
->flags
|= XIO_DOESEXEC
;
154 /* Only one process, no parent,child */
155 if ((copts
= moveopts(opts
, GROUP_ALL
)) == NULL
) {
156 /*!! free something */
161 if (sock1
->tag
== XIO_TAG_DUAL
) {
162 stream1
= &sock1
->dual
.stream
[0]->stream
;
163 stream2
= &sock1
->dual
.stream
[1]->stream
;
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");
180 /* remember: fdin is the fd where the sub program reads from, thus it is
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
));
189 /* make sure that the internal diagnostic socket pair fds do not conflict
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
));
205 /* make sure that the internal diagnostic socket pair fds do not conflict
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);*/
216 } else /* withfork */
220 #if defined(HAVE_DEV_PTMX)
221 # define PTMX "/dev/ptmx" /* Linux */
223 # define PTMX "/dev/ptc" /* AIX 4.3.3 */
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",
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 */
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 */
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 */
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
));
272 #endif /* HAVE_OPENPTY */
273 /* withfork use_pty */
274 if ((copts
= moveopts(opts
, GROUP_TERMIOS
|GROUP_FORK
|GROUP_EXEC
|GROUP_PROCESS
|GROUP_NAMED
)) == NULL
) {
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
);
288 /* end withfork, use_pty */
289 } else /* end withfork, use_pty */
290 #endif /* HAVE_PTY */
293 /* withfork usepipes */
294 struct opt
*popts2
= NULL
;
297 sfd
->dtype
= XIODATA_2PIPE
;
298 if (rw
!= XIO_WRONLY
) {
299 if (Pipe(rdpip
) < 0) {
300 Error2("pipe(%p): %s", rdpip
, strerror(errno
));
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
))
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
));
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
);
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 */
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];
350 applyopts(sfd
, -1, popts
, PH_FD
);
351 applyopts(sfd
, -1, popts
, PH_LATE
);
352 if (applyopts_single(sfd
, popts
, PH_LATE
) < 0)
355 /* end withfork, use_pipes */
357 /* withfork, socketpair */
360 retropt_int(opts
, OPT_PROTOCOL_FAMILY
, &d
);
361 result
= xiosocketpair(opts
, d
, SOCK_STREAM
, 0, sv
);
366 /* withfork socketpair */
367 if ((copts
= moveopts(opts
, GROUP_FORK
|GROUP_EXEC
|GROUP_PROCESS
)) == NULL
) {
371 applyopts(sfd
, sv
[0], copts
, PH_PASTSOCKET
);
372 applyopts(sfd
, sv
[1], popts
, PH_PASTSOCKET
);
374 applyopts_cloexec(sv
[0], copts
);
375 applyopts(sfd
, sv
[0], copts
, PH_FD
);
376 applyopts(sfd
, sv
[1], popts
, PH_FD
);
378 applyopts(sfd
, sv
[0], copts
, PH_PREBIND
);
379 applyopts(sfd
, sv
[0], copts
, PH_BIND
);
380 applyopts(sfd
, sv
[0], copts
, PH_PASTBIND
);
381 applyopts(sfd
, sv
[1], popts
, PH_PREBIND
);
382 applyopts(sfd
, sv
[1], popts
, PH_BIND
);
383 applyopts(sfd
, sv
[1], popts
, PH_PASTBIND
);
385 if (sfd
->howtoend
== END_UNSPEC
) {
386 sfd
->howtoend
= END_SHUTDOWN_KILL
;
389 /* this for parent, was after fork */
391 applyopts(sfd
, -1, popts
, PH_FD
);
392 /* end withfork, socketpair */
394 retropt_bool(copts
, OPT_STDERR
, &withstderr
);
396 xiosetchilddied(); /* set SIGCHLD handler */
399 Socketpair(PF_UNIX
, SOCK_STREAM
, 0, trigger
);
400 pid
= xio_fork(true, E_ERROR
, 0);
405 if (!withfork
|| pid
== 0) { /* in single process, or in child */
406 applyopts_optgroup(sfd
, -1, copts
, GROUP_PROCESS
);
408 Close(trigger
[0]); /* in child: not needed here */
409 /* The child should have default handling for SIGCHLD. */
410 /* In particular, it's not defined whether ignoring SIGCHLD is inheritable. */
411 if (Signal(SIGCHLD
, SIG_DFL
) == SIG_ERR
) {
412 Warn1("signal(SIGCHLD, SIG_DFL): %s", strerror(errno
));
417 applyopts_named(tn
, copts
, PH_PREOPEN
);
418 applyopts_named(tn
, copts
, PH_EARLY
);
419 applyopts_named(tn
, copts
, PH_FD
);
422 if ((ttyfd
= Open(tn
, O_RDWR
|O_NOCTTY
, 0620)) < 0) {
423 Warn2("open(\"%s\", O_RDWR|O_NOCTTY, 0620): %s", tn
, strerror(errno
));
425 /*0 Info2("open(\"%s\", O_RDWR|O_NOCTTY, 0620) -> %d", tn, ttyfd);*/
428 if ((tn
= Ttyname(ttyfd
)) == NULL
) {
429 Warn2("ttyname(%d): %s", ttyfd
, strerror(errno
));
434 /* Linux: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> -1 EINVAL */
435 /* AIX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 1 */
436 /* SunOS: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */
437 /* HP-UX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */
438 if (Ioctl(ttyfd
, I_FIND
, "ldterm\0") == 0) {
439 Ioctl(ttyfd
, I_PUSH
, "ptem\0\0\0"); /* 0 */ /* padding for AdressSanitizer */
440 Ioctl(ttyfd
, I_PUSH
, "ldterm\0"); /* 0 */
441 Ioctl(ttyfd
, I_PUSH
, "ttcompat"); /* HP-UX: -1 */
445 /* this for child, was after fork */
446 applyopts(sfd
, ttyfd
, copts
, PH_FD
);
448 Info1("opened pseudo terminal %s", tn
);
450 if (rw
!= XIO_RDONLY
&& fdi
!= ttyfd
) {
451 /* make sure that the internal diagnostic socket pair fds do not conflict
453 diag_reserve_fd(fdi
);
454 if (Dup2(ttyfd
, fdi
) < 0) {
455 Error3("dup2(%d, %d): %s", ttyfd
, fdi
, strerror(errno
));
457 /*0 Info2("dup2(%d, %d)", ttyfd, fdi);*/
459 if (rw
!= XIO_WRONLY
&& fdo
!= ttyfd
) {
460 /* make sure that the internal diagnostic socket pair fds do not conflict
462 diag_reserve_fd(fdo
);
463 if (Dup2(ttyfd
, fdo
) < 0) {
464 Error3("dup2(%d, %d): %s", ttyfd
, fdo
, strerror(errno
));
466 /*0 Info2("dup2(%d, %d)", ttyfd, fdo);*/
468 if ((rw
== XIO_RDONLY
|| fdi
!= ttyfd
) &&
469 (rw
== XIO_WRONLY
|| fdo
!= ttyfd
)) {
470 applyopts_cloexec(ttyfd
, copts
);
473 applyopts(sfd
, ttyfd
, copts
, PH_LATE
);
474 applyopts(sfd
, ttyfd
, copts
, PH_LATE2
);
476 #endif /* HAVE_PTY */
478 /* we might have a temporary conflict between what FDs are
479 currently allocated, and which are to be used. We try to find
480 a graceful solution via temporary descriptors */
483 if (rw
!= XIO_WRONLY
) Close(rdpip
[0]);
484 if (rw
!= XIO_RDONLY
) Close(wrpip
[1]);
485 if (fdi
== rdpip
[1]) { /* a conflict here */
486 if ((tmpi
= Dup(wrpip
[0])) < 0) {
487 Error2("dup(%d): %s", wrpip
[0], strerror(errno
));
490 /*0 Info2("dup(%d) -> %d", wrpip[0], tmpi);*/
493 if (fdo
== wrpip
[0]) { /* a conflict here */
494 if ((tmpo
= Dup(rdpip
[1])) < 0) {
495 Error2("dup(%d): %s", rdpip
[1], strerror(errno
));
498 /*0 Info2("dup(%d) -> %d", rdpip[1], tmpo);*/
502 if (rw
!= XIO_WRONLY
&& rdpip
[1] != fdo
) {
503 /* make sure that the internal diagnostic socket pair fds do not conflict
505 diag_reserve_fd(fdo
);
506 if (Dup2(rdpip
[1], fdo
) < 0) {
507 Error3("dup2(%d, %d): %s", rdpip
[1], fdo
, strerror(errno
));
512 if (rw
!= XIO_RDONLY
&& wrpip
[0] != fdi
) {
513 /* make sure that the internal diagnostic socket pair fds do not conflict
515 diag_reserve_fd(fdi
);
516 if (Dup2(wrpip
[0], fdi
) < 0) {
517 Error3("dup2(%d, %d): %s", wrpip
[0], fdi
, strerror(errno
));
521 /* applyopts_cloexec(fdi, *copts);*/ /* option is already consumed! */
524 applyopts(sfd
, fdi
, copts
, PH_LATE
);
525 applyopts(sfd
, fdo
, copts
, PH_LATE
);
526 applyopts(sfd
, fdi
, copts
, PH_LATE2
);
527 applyopts(sfd
, fdo
, copts
, PH_LATE2
);
529 } else { /* socketpair */
531 if (rw
!= XIO_RDONLY
&& fdi
!= sv
[1]) {
532 /* make sure that the internal diagnostic socket pair fds do not conflict
534 diag_reserve_fd(fdi
);
535 if (Dup2(sv
[1], fdi
) < 0) {
536 Error3("dup2(%d, %d): %s", sv
[1], fdi
, strerror(errno
));
538 /*0 Info2("dup2(%d, %d)", sv[1], fdi);*/
540 if (rw
!= XIO_WRONLY
&& fdo
!= sv
[1]) {
541 /* make sure that the internal diagnostic socket pair fds do not conflict
543 diag_reserve_fd(fdo
);
544 if (Dup2(sv
[1], fdo
) < 0) {
545 Error3("dup2(%d, %d): %s", sv
[1], fdo
, strerror(errno
));
547 /*0 Info2("dup2(%d, %d)", sv[1], fdo);*/
549 if (fdi
!= sv
[1] && fdo
!= sv
[1]) {
550 applyopts_cloexec(sv
[1], copts
);
554 applyopts(sfd
, fdi
, copts
, PH_LATE
);
555 applyopts(sfd
, fdi
, copts
, PH_LATE2
);
558 Info("notifying parent that child process is ready");
559 Close(trigger
[1]); /* in child, notify parent that ready */
563 applyopts(sfd
, -1, copts
, PH_LATE
);
564 applyopts(sfd
, -1, copts
, PH_LATE2
);
566 _xioopen_setdelayeduser();
574 return 0; /* indicate child process */
577 /* for parent (this is our socat process) */
578 Notice1("forked off child process "F_pid
, pid
);
579 Close(trigger
[1]); /* in parent */
584 if (Close(ttyfd
) < 0) {
585 Info2("close(%d): %s", ttyfd
, strerror(errno
));
589 #endif /* HAVE_PTY */
591 if (rw
== XIO_RDONLY
) Close(rdpip
[1]);
592 if (rw
== XIO_WRONLY
) Close(wrpip
[0]);
596 sfd
->para
.exec
.pid
= pid
;
598 if (applyopts_single(sfd
, popts
, PH_LATE
) < 0) return -1;
599 applyopts(sfd
, -1, popts
, PH_LATE
);
600 applyopts(sfd
, -1, popts
, PH_LATE2
);
601 applyopts(sfd
, -1, popts
, PH_PASTEXEC
);
602 if ((numleft
= leftopts(popts
)) > 0) {
604 Error1("INTERNAL: %d option(s) remained unused", numleft
);
609 struct pollfd fds
[1];
610 fds
[0].fd
= trigger
[0];
611 fds
[0].events
= POLLIN
|POLLHUP
;
613 Info("child process notified parent that it is ready");
617 applyopts(sfd
, ptyfd
, popts
, PH_LATE
);
618 #endif /* HAVE_PTY */
619 if (applyopts_single(sfd
, popts
, PH_LATE
) < 0)
623 return pid
; /* indicate parent (main) process */
625 #endif /* WITH_EXEC || WITH_SYSTEM */
628 int setopt_path(struct opt
*opts
, char **path
) {
629 if (retropt_string(opts
, OPT_PATH
, path
) >= 0) {
630 if (setenv("PATH", *path
, 1) < 0) {
631 Error1("setenv(\"PATH\", \"%s\", 1): insufficient space", *path
);