add listen-timeout to function as an accept timeout
[socat/sam.git] / xio-progcall.c
blob82172985b50f00cab6080928348cee680972113d
1 /* source: xio-progcall.c */
2 /* Copyright Gerhard Rieger 2001-2009 */
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-progcall.h"
13 #include "xio-socket.h"
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
24 #if WITH_EXEC || WITH_SYSTEM
26 #define MAXPTYNAMELEN 64
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 };
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 !!!
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;
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);
81 retropt_bool(popts, OPT_NOFORK, &nofork);
82 withfork = !nofork;
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
101 if (usepipes && usepty) {
102 Warn("_xioopen_foxec(): options \"pipes\" and \"pty\" must not be specified together; ignoring \"pipes\"");
103 usepipes = false;
105 #endif /* HAVE_PTY */
106 retropt_ushort(popts, OPT_FDIN, (unsigned short *)&fdi);
107 retropt_ushort(popts, OPT_FDOUT, (unsigned short *)&fdo);
109 if (withfork) {
110 if (!(xioflags&XIO_MAYCHILD)) {
111 Error("cannot fork off child process here");
112 /*!! free something */
113 return -1;
115 fd->flags |= XIO_DOESCHILD;
117 #if HAVE_PTY
118 Notice2("forking off child, using %s for %s",
119 &("socket\0\0pipes\0\0\0pty\0\0\0\0\0"[(usepipes<<3)|(usepty<<4)]),
120 ddirection[rw]);
121 #else
122 Notice2("forking off child, using %s for %s",
123 &("socket\0\0pipes\0\0\0"[(usepipes<<3)]),
124 ddirection[rw]);
125 #endif /* HAVE_PTY */
127 applyopts(-1, popts, PH_PREBIGEN);
129 if (!withfork) {
130 /*0 struct single *stream1, *stream2;*/
132 if (!(xioflags & XIO_MAYEXEC /* means exec+nofork */)) {
133 Error("option nofork is not allowed here");
134 /*!! free something */
135 return -1;
137 fd->flags |= XIO_DOESEXEC;
139 free(*copts);
140 *copts = moveopts(popts, GROUP_ALL);
142 #if 0 /*!! */
143 if (sock1->tag == XIO_TAG_DUAL) {
144 stream1 = &sock1->dual.stream[0]->stream;
145 stream2 = &sock1->dual.stream[1]->stream;
146 } else {
147 stream1 = &sock1->stream;
148 stream2 = &sock1->stream;
150 if (stream1->dtype == DATA_READLINE || stream2->dtype == DATA_READLINE ||
151 stream1->dtype == DATA_OPENSSL || stream2->dtype == DATA_OPENSSL
153 Error("with option nofork, openssl and readline in address1 do not work");
155 if (stream1->lineterm != LINETERM_RAW ||
156 stream2->lineterm != LINETERM_RAW ||
157 stream1->ignoreeof || stream2->ignoreeof) {
158 Warn("due to option nofork, address1 options for lineterm and igoreeof do not apply");
160 #endif
162 /*! problem: when fdi==WRFD(sock[0]) or fdo==RDFD(sock[0]) */
163 if (rw != XIO_WRONLY) {
164 if (XIO_GETRDFD(sock[0]/*!!*/) == fdi) {
165 if (Fcntl_l(fdi, F_SETFD, 0) < 0) {
166 Warn2("fcntl(%d, F_SETFD, 0): %s", fdi, strerror(errno));
168 if (Dup2(XIO_GETRDFD(sock[0]), fdi) < 0) {
169 Error3("dup2(%d, %d): %s",
170 XIO_GETRDFD(sock[0]), fdi, strerror(errno));
172 /*0 Info2("dup2(%d, %d)", XIO_GETRDFD(sock[0]), fdi);*/
173 } else {
174 if (Dup2(XIO_GETRDFD(sock[0]), fdi) < 0) {
175 Error3("dup2(%d, %d): %s",
176 XIO_GETRDFD(sock[0]), fdi, strerror(errno));
178 /*0 Info2("dup2(%d, %d)", XIO_GETRDFD(sock[0]), fdi);*/
181 if (rw != XIO_RDONLY) {
182 if (XIO_GETWRFD(sock[0]) == fdo) {
183 if (Fcntl_l(fdo, F_SETFD, 0) < 0) {
184 Warn2("fcntl(%d, F_SETFD, 0): %s", fdo, strerror(errno));
186 if (Dup2(XIO_GETWRFD(sock[0]), fdo) < 0) {
187 Error3("dup2(%d, %d): %s)",
188 XIO_GETWRFD(sock[0]), fdo, strerror(errno));
190 /*0 Info2("dup2(%d, %d)", XIO_GETWRFD(sock[0]), fdo);*/
191 } else {
192 if (Dup2(XIO_GETWRFD(sock[0]), fdo) < 0) {
193 Error3("dup2(%d, %d): %s)",
194 XIO_GETWRFD(sock[0]), fdo, strerror(errno));
196 /*0 Info2("dup2(%d, %d)", XIO_GETWRFD(sock[0]), fdo);*/
199 } else
200 #if HAVE_PTY
201 if (usepty) {
203 #if defined(HAVE_DEV_PTMX)
204 # define PTMX "/dev/ptmx" /* Linux */
205 #elif HAVE_DEV_PTC
206 # define PTMX "/dev/ptc" /* AIX 4.3.3 */
207 #endif
208 fd->dtype = XIODATA_PTY;
209 #if HAVE_DEV_PTMX || HAVE_DEV_PTC
210 if (usebestpty || useptmx) {
211 if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) {
212 Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s",
213 strerror(errno));
214 /*!*/
215 } else {
216 /*0 Info2("open(\"%s\", O_RDWR|O_NOCTTY, 0620) -> %d", PTMX, ptyfd);*/
218 if (ptyfd >= 0 && ttyfd < 0) {
219 char *tn = NULL;
220 /* we used PTMX before forking */
221 extern char *ptsname(int);
222 #if HAVE_GRANTPT /* AIX, not Linux */
223 if (Grantpt(ptyfd)/*!*/ < 0) {
224 Warn2("grantpt(%d): %s", ptyfd, strerror(errno));
226 #endif /* HAVE_GRANTPT */
227 #if HAVE_UNLOCKPT
228 if (Unlockpt(ptyfd)/*!*/ < 0) {
229 Warn2("unlockpt(%d): %s", ptyfd, strerror(errno));
231 #endif /* HAVE_UNLOCKPT */
232 #if HAVE_PTSNAME /* AIX, not Linux */
233 if ((tn = Ptsname(ptyfd)) == NULL) {
234 Warn2("ptsname(%d): %s", ptyfd, strerror(errno));
236 #endif /* HAVE_PTSNAME */
237 if (tn == NULL) {
238 if ((tn = Ttyname(ptyfd)) == NULL) {
239 Warn2("ttyname(%d): %s", ptyfd, strerror(errno));
242 strncpy(ptyname, tn, MAXPTYNAMELEN);
243 if ((ttyfd = Open(tn, O_RDWR|O_NOCTTY, 0620)) < 0) {
244 Warn2("open(\"%s\", O_RDWR|O_NOCTTY, 0620): %s", tn, strerror(errno));
245 } else {
246 /*0 Info2("open(\"%s\", O_RDWR|O_NOCTTY, 0620) -> %d", tn, ttyfd);*/
249 #ifdef I_PUSH
250 /* Linux: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> -1 EINVAL */
251 /* AIX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 1 */
252 /* SunOS: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */
253 /* HP-UX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */
254 if (Ioctl(ttyfd, I_FIND, "ldterm") == 0) {
255 Ioctl(ttyfd, I_PUSH, "ptem"); /* 0 */
256 Ioctl(ttyfd, I_PUSH, "ldterm"); /* 0 */
257 Ioctl(ttyfd, I_PUSH, "ttcompat"); /* HP-UX: -1 */
259 #endif
261 #if 0 /* the following block need not work */
263 if (ttyfd >= 0 && ((tn = Ttyname(ttyfd)) == NULL)) {
264 Warn2("ttyname(%d): %s", ttyfd, strerror(errno));
266 if (tn == NULL) {
267 Error("could not open pty");
268 return -1;
270 #endif
271 Info1("opened pseudo terminal %s", tn);
274 #endif /* HAVE_DEV_PTMX || HAVE_DEV_PTC */
275 #if HAVE_OPENPTY
276 if (ptyfd < 0) {
277 int result;
278 if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) {
279 Error4("openpty(%p, %p, %p, NULL, NULL): %s",
280 &ptyfd, &ttyfd, ptyname, strerror(errno));
281 return -1;
284 #endif /* HAVE_OPENPTY */
285 free(*copts);
286 if ((*copts = moveopts(popts, GROUP_TERMIOS|GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
287 return -1;
289 applyopts_cloexec(ptyfd, popts);/*!*/
290 /* exec:...,pty did not kill child process under some circumstances */
291 if (fd->howtoend == END_UNSPEC) {
292 fd->howtoend = END_CLOSE_KILL;
295 /* this for parent, was after fork */
296 applyopts(ptyfd, popts, PH_FD);
297 applyopts(ptyfd, popts, PH_LATE);
298 if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
300 fd->fd = ptyfd;
302 /* this for child, was after fork */
303 applyopts(ttyfd, *copts, PH_FD);
304 } else
305 #endif /* HAVE_PTY */
306 if (usepipes) {
307 struct opt *popts2, *copts2;
309 if (rw == XIO_RDWR)
310 fd->dtype = XIODATA_2PIPE;
311 if (rw != XIO_WRONLY) {
312 if (Pipe(rdpip) < 0) {
313 Error2("pipe(%p): %s", rdpip, strerror(errno));
314 return -1;
317 /*0 Info2("pipe({%d,%d})", rdpip[0], rdpip[1]);*/
318 /* rdpip[0]: read by socat; rdpip[1]: write by child */
319 free(*copts);
320 if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS))
321 == NULL) {
322 return -1;
325 popts2 = copyopts(popts, GROUP_ALL);
326 copts2 = copyopts(*copts, GROUP_ALL);
328 if (rw != XIO_WRONLY) {
329 applyopts_cloexec(rdpip[0], popts);
330 applyopts(rdpip[0], popts, PH_FD);
331 applyopts(rdpip[1], *copts, PH_FD);
334 if (rw != XIO_RDONLY) {
335 if (Pipe(wrpip) < 0) {
336 Error2("pipe(%p): %s", wrpip, strerror(errno));
337 return -1;
340 /*0 Info2("pipe({%d,%d})", wrpip[0], wrpip[1]);*/
342 /* wrpip[1]: write by socat; wrpip[0]: read by child */
343 if (rw != XIO_RDONLY) {
344 applyopts_cloexec(wrpip[1], popts2);
345 applyopts(wrpip[1], popts2, PH_FD);
346 applyopts(wrpip[0], copts2, PH_FD);
348 if (fd->howtoend == END_UNSPEC) {
349 fd->howtoend = END_CLOSE_KILL;
352 /* this for parent, was after fork */
353 switch (rw) {
354 case XIO_RDONLY: fd->fd = rdpip[0]; break;
355 case XIO_WRONLY: fd->fd = wrpip[1]; break;
356 case XIO_RDWR: fd->fd = rdpip[0];
357 fd->para.exec.fdout = wrpip[1];
358 break;
360 applyopts(fd->fd, popts, PH_FD);
361 applyopts(fd->fd, popts, PH_LATE);
362 if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
363 } else {
364 d = AF_UNIX;
365 retropt_int(popts, OPT_PROTOCOL_FAMILY, &d);
366 result = xiosocketpair(popts, d, SOCK_STREAM, 0, sv);
367 if (result < 0) {
368 return -1;
370 /*0 Info5("socketpair(%d, %d, %d, {%d,%d})",
371 d, type, protocol, sv[0], sv[1]);*/
372 free(*copts);
373 if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
374 return -1;
376 applyopts(sv[0], *copts, PH_PASTSOCKET);
377 applyopts(sv[1], popts, PH_PASTSOCKET);
379 applyopts_cloexec(sv[0], *copts);
380 applyopts(sv[0], *copts, PH_FD);
381 applyopts(sv[1], popts, PH_FD);
383 applyopts(sv[0], *copts, PH_PREBIND);
384 applyopts(sv[0], *copts, PH_BIND);
385 applyopts(sv[0], *copts, PH_PASTBIND);
386 applyopts(sv[1], popts, PH_PREBIND);
387 applyopts(sv[1], popts, PH_BIND);
388 applyopts(sv[1], popts, PH_PASTBIND);
390 if (fd->howtoend == END_UNSPEC) {
391 fd->howtoend = END_SHUTDOWN_KILL;
394 /* this for parent, was after fork */
395 fd->fd = sv[0];
396 applyopts(fd->fd, popts, PH_FD);
397 applyopts(fd->fd, popts, PH_LATE);
398 if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
400 /*0 if ((optpr = copyopts(*copts, GROUP_PROCESS)) == NULL)
401 return -1;*/
402 retropt_bool(*copts, OPT_STDERR, &withstderr);
404 xiosetchilddied(); /* set SIGCHLD handler */
406 if (withfork) {
407 pid = xio_fork(true, E_ERROR);
408 if (pid < 0) {
409 return -1;
412 if (!withfork || pid == 0) { /* child */
413 uid_t user;
414 gid_t group;
416 if (withfork) {
417 if (Signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
418 Warn1("signal(SIGCHLD, SIG_IGN): %s", strerror(errno));
421 #if HAVE_PTY
422 if (usepty) {
423 Close(ptyfd);
424 if (rw != XIO_RDONLY && fdi != ttyfd) {
425 if (Dup2(ttyfd, fdi) < 0) {
426 Error3("dup2(%d, %d): %s", ttyfd, fdi, strerror(errno));
427 return -1; }
428 /*0 Info2("dup2(%d, %d)", ttyfd, fdi);*/
430 if (rw != XIO_WRONLY && fdo != ttyfd) {
431 if (Dup2(ttyfd, fdo) < 0) {
432 Error3("dup2(%d, %d): %s", ttyfd, fdo, strerror(errno));
433 return -1; }
434 /*0 Info2("dup2(%d, %d)", ttyfd, fdo);*/
436 if ((rw == XIO_RDONLY || fdi != ttyfd) &&
437 (rw == XIO_WRONLY || fdo != ttyfd)) {
438 applyopts_cloexec(ttyfd, *copts);
441 applyopts(ttyfd, *copts, PH_LATE);
443 applyopts(ttyfd, *copts, PH_LATE2);
444 } else
445 #endif /* HAVE_PTY */
446 if (usepipes) {
447 /* we might have a temporary conflict between what FDs are
448 currently allocated, and which are to be used. We try to find
449 a graceful solution via temporary descriptors */
450 int tmpi, tmpo;
452 if (rw != XIO_WRONLY) Close(rdpip[0]);
453 if (rw != XIO_RDONLY) Close(wrpip[1]);
454 if (fdi == rdpip[1]) { /* a conflict here */
455 if ((tmpi = Dup(wrpip[0])) < 0) {
456 Error2("dup(%d): %s", wrpip[0], strerror(errno));
457 return -1;
459 /*0 Info2("dup(%d) -> %d", wrpip[0], tmpi);*/
460 rdpip[1] = tmpi;
462 if (fdo == wrpip[0]) { /* a conflict here */
463 if ((tmpo = Dup(rdpip[1])) < 0) {
464 Error2("dup(%d): %s", rdpip[1], strerror(errno));
465 return -1;
467 /*0 Info2("dup(%d) -> %d", rdpip[1], tmpo);*/
468 wrpip[0] = tmpo;
471 if (rw != XIO_WRONLY && rdpip[1] != fdo) {
472 if (Dup2(rdpip[1], fdo) < 0) {
473 Error3("dup2(%d, %d): %s", rdpip[1], fdo, strerror(errno));
474 return -1;
476 Close(rdpip[1]);
477 /*0 Info2("dup2(%d, %d)", rdpip[1], fdo);*/
478 /*0 applyopts_cloexec(fdo, *copts);*/
480 if (rw != XIO_RDONLY && wrpip[0] != fdi) {
481 if (Dup2(wrpip[0], fdi) < 0) {
482 Error3("dup2(%d, %d): %s", wrpip[0], fdi, strerror(errno));
483 return -1;
485 Close(wrpip[0]);
486 /*0 Info2("dup2(%d, %d)", wrpip[0], fdi);*/
487 /*0 applyopts_cloexec(wrpip[0], *copts);*/ /* option is already consumed! */
488 /* applyopts_cloexec(fdi, *copts);*/ /* option is already consumed! */
491 applyopts(fdi, *copts, PH_LATE);
492 applyopts(fdo, *copts, PH_LATE);
493 applyopts(fdi, *copts, PH_LATE2);
494 applyopts(fdo, *copts, PH_LATE2);
496 } else { /* socketpair */
497 Close(sv[0]);
498 if (rw != XIO_RDONLY && fdi != sv[1]) {
499 if (Dup2(sv[1], fdi) < 0) {
500 Error3("dup2(%d, %d): %s", sv[1], fdi, strerror(errno));
501 return -1; }
502 /*0 Info2("dup2(%d, %d)", sv[1], fdi);*/
504 if (rw != XIO_WRONLY && fdo != sv[1]) {
505 if (Dup2(sv[1], fdo) < 0) {
506 Error3("dup2(%d, %d): %s", sv[1], fdo, strerror(errno));
507 return -1; }
508 /*0 Info2("dup2(%d, %d)", sv[1], fdo);*/
510 if (fdi != sv[1] && fdo != sv[1]) {
511 applyopts_cloexec(sv[1], *copts);
512 Close(sv[1]);
515 applyopts(fdi, *copts, PH_LATE);
516 applyopts(fdi, *copts, PH_LATE2);
518 } /* withfork */
519 else {
520 applyopts(-1, *copts, PH_LATE);
521 applyopts(-1, *copts, PH_LATE2);
523 _xioopen_setdelayeduser();
524 /* set group before user - maybe you are not permitted afterwards */
525 if (retropt_gidt(*copts, OPT_SETGID, &group) >= 0) {
526 Setgid(group);
528 if (retropt_uidt(*copts, OPT_SETUID, &user) >= 0) {
529 Setuid(user);
531 if (withstderr) {
532 *duptostderr = fdo;
533 } else {
534 *duptostderr = -1;
537 return 0; /* indicate child process */
540 /* for parent (this is our socat process) */
541 Notice1("forked off child process "F_pid, pid);
543 #if 0
544 if ((popts = copyopts(*copts,
545 GROUP_FD|GROUP_TERMIOS|GROUP_FORK|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_FIFO)) == NULL)
546 return STAT_RETRYLATER;
547 #endif
549 #if HAVE_PTY
550 if (usepty) {
551 if (Close(ttyfd) < 0) {
552 Info2("close(%d): %s", ttyfd, strerror(errno));
554 } else
555 #endif /* HAVE_PTY */
556 if (usepipes) {
557 Close(rdpip[1]);
558 Close(wrpip[0]);
559 } else {
560 Close(sv[1]);
562 fd->para.exec.pid = pid;
564 if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
565 applyopts_signal(fd, popts);
566 if ((numleft = leftopts(popts)) > 0) {
567 Error1("%d option(s) could not be used", numleft);
568 showleft(popts);
569 return STAT_NORETRY;
572 return pid; /* indicate parent (main) process */
574 #endif /* WITH_EXEC || WITH_SYSTEM */
577 int setopt_path(struct opt *opts, char **path) {
578 if (retropt_string(opts, OPT_PATH, path) >= 0) {
579 if (setenv("PATH", *path, 1) < 0) {
580 Error1("setenv(\"PATH\", \"%s\", 1): insufficient space", *path);
581 return -1;
584 return 0;