mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 15:32:35 +00:00
959 lines
30 KiB
C
959 lines
30 KiB
C
/* source: xio-progcall.c */
|
|
/* Copyright Gerhard Rieger */
|
|
/* Published under the GNU General Public License V.2, see file COPYING */
|
|
|
|
/* this file contains common code dealing with program calls (exec, system) */
|
|
|
|
#include "xiosysincludes.h"
|
|
#include "xioopen.h"
|
|
#include "xiosigchld.h"
|
|
|
|
#include "xio-process.h"
|
|
#include "xio-progcall.h"
|
|
|
|
#include "xio-socket.h"
|
|
|
|
|
|
static int reassignfds(int oldfd1, int oldfd2, int newfd1, int newfd2);
|
|
|
|
|
|
/* these options are used by address pty too */
|
|
#if HAVE_OPENPTY
|
|
const struct optdesc opt_openpty = { "openpty", NULL, OPT_OPENPTY, GROUP_PTY, PH_BIGEN, TYPE_BOOL, OFUNC_SPEC };
|
|
#endif /* HAVE_OPENPTY */
|
|
#if HAVE_DEV_PTMX || HAVE_DEV_PTC
|
|
const struct optdesc opt_ptmx = { "ptmx", NULL, OPT_PTMX, GROUP_PTY, PH_BIGEN, TYPE_BOOL, OFUNC_SPEC };
|
|
#endif
|
|
|
|
#if WITH_EXEC || WITH_SYSTEM
|
|
|
|
#define MAXPTYNAMELEN 64
|
|
|
|
const struct optdesc opt_leftfd = { "leftfd", "left", OPT_LEFTFD, GROUP_FORK, PH_PASTBIGEN, TYPE_INT, OFUNC_SPEC };
|
|
const struct optdesc opt_leftinfd = { "leftin", "fdin", OPT_LEFTINFD, GROUP_FORK, PH_PASTBIGEN, TYPE_INT, OFUNC_SPEC };
|
|
const struct optdesc opt_leftoutfd = { "leftout", "fdout", OPT_LEFTOUTFD, GROUP_FORK, PH_PASTBIGEN, TYPE_INT, OFUNC_SPEC };
|
|
const struct optdesc opt_rightfd = { "rightfd", "right", OPT_RIGHTFD, GROUP_FORK, PH_PASTBIGEN, TYPE_INT, OFUNC_SPEC };
|
|
const struct optdesc opt_rightinfd = { "rightinfd", "rightin", OPT_RIGHTINFD, GROUP_FORK, PH_PASTBIGEN, TYPE_INT, OFUNC_SPEC };
|
|
const struct optdesc opt_rightoutfd = { "rightoutfd", "rightout", OPT_RIGHTOUTFD, GROUP_FORK, PH_PASTBIGEN, TYPE_INT, OFUNC_SPEC };
|
|
const struct optdesc opt_path = { "path", NULL, OPT_PATH, GROUP_EXEC, PH_PREEXEC, TYPE_STRING, OFUNC_SPEC };
|
|
const struct optdesc opt_pipes = { "pipes", NULL, OPT_PIPES, GROUP_FORK, PH_BIGEN, TYPE_BOOL, OFUNC_SPEC };
|
|
#if HAVE_PTY
|
|
const struct optdesc opt_pty = { "pty", NULL, OPT_PTY, GROUP_FORK, PH_BIGEN, TYPE_BOOL, OFUNC_SPEC };
|
|
#endif
|
|
const struct optdesc opt_commtype= { "commtype", NULL, OPT_COMMTYPE, GROUP_FORK, PH_BIGEN, TYPE_STRING, OFUNC_SPEC };
|
|
const struct optdesc opt_stderr = { "stderr", NULL, OPT_STDERR, GROUP_FORK, PH_PASTFORK, TYPE_BOOL, OFUNC_SPEC };
|
|
const struct optdesc opt_nofork = { "nofork", NULL, OPT_NOFORK, GROUP_FORK, PH_BIGEN, TYPE_BOOL, OFUNC_SPEC };
|
|
const struct optdesc opt_sighup = { "sighup", NULL, OPT_SIGHUP, GROUP_PARENT, PH_LATE, TYPE_CONST, OFUNC_SIGNAL, SIGHUP };
|
|
const struct optdesc opt_sigint = { "sigint", NULL, OPT_SIGINT, GROUP_PARENT, PH_LATE, TYPE_CONST, OFUNC_SIGNAL, SIGINT };
|
|
const struct optdesc opt_sigquit = { "sigquit", NULL, OPT_SIGQUIT, GROUP_PARENT, PH_LATE, TYPE_CONST, OFUNC_SIGNAL, SIGQUIT };
|
|
|
|
int getcommtype(const char *commname) {
|
|
struct wordent commnames[] = {
|
|
{ "pipes", (void *)XIOCOMM_PIPES },
|
|
{ "pty", (void *)XIOCOMM_PTY },
|
|
{ "ptys", (void *)XIOCOMM_PTYS },
|
|
{ "socketpair", (void *)XIOCOMM_SOCKETPAIR },
|
|
{ "socketpairs", (void *)XIOCOMM_SOCKETPAIRS },
|
|
{ "tcp", (void *)XIOCOMM_TCP },
|
|
{ "tcp4", (void *)XIOCOMM_TCP4 },
|
|
{ "tcp4listen", (void *)XIOCOMM_TCP4_LISTEN },
|
|
{ NULL }
|
|
} ;
|
|
const struct wordent *comm_ent;
|
|
comm_ent = keyw((struct wordent *)commnames, commname,
|
|
sizeof(commnames)/sizeof(struct wordent));
|
|
if (!comm_ent) {
|
|
return -1;
|
|
}
|
|
return (int)comm_ent->desc;
|
|
}
|
|
|
|
/* fork for exec/system, but return before exec'ing.
|
|
return=0: is child process
|
|
return>0: is parent process
|
|
return<0: error occurred, assume parent process and no child exists !!!
|
|
function formerly known as _xioopen_foxec()
|
|
*/
|
|
int _xioopen_progcall(int xioflags, /* XIO_RDONLY etc. */
|
|
struct single *xfd,
|
|
unsigned groups,
|
|
struct opt **copts, /* in: opts; out: opts for child */
|
|
int *duptostderr,
|
|
bool inter, /* is interaddr, not endpoint */
|
|
int form /* with interaddr: =2: FDs 1,0--4,3
|
|
=1: FDs 1--0 */
|
|
) {
|
|
struct single *fd = xfd;
|
|
struct opt *popts; /* parent process options */
|
|
int numleft;
|
|
int sv[2], rdpip[2], wrpip[2];
|
|
int saverfd = -1, savewfd = -1; /* with inter addr, save assigned right fds */
|
|
int rw = (xioflags & XIO_ACCMODE);
|
|
char *commname;
|
|
int commtype = XIOCOMM_SOCKETPAIRS;
|
|
bool usepipes = false;
|
|
#if HAVE_PTY
|
|
int ptyfd = -1, ttyfd = -1;
|
|
bool usebestpty = false; /* use the best available way to open pty */
|
|
#if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
|
|
bool useptmx = false; /* use /dev/ptmx or equivalent */
|
|
#endif
|
|
#if HAVE_OPENPTY
|
|
bool useopenpty = false; /* try only openpty */
|
|
#endif /* HAVE_OPENPTY */
|
|
bool usepty = false; /* any of the pty options is selected */
|
|
char ptyname[MAXPTYNAMELEN];
|
|
#endif /* HAVE_PTY */
|
|
pid_t pid = 0; /* mostly int */
|
|
int leftfd[2] = { 0, 1 };
|
|
# define fdi (leftfd[0])
|
|
# define fdo (leftfd[1])
|
|
int rightfd[2] = { 3, 4 };
|
|
# define rightin (rightfd[0])
|
|
# define rightout (rightfd[1])
|
|
short result;
|
|
bool withstderr = false;
|
|
bool nofork = false;
|
|
bool withfork;
|
|
|
|
popts = moveopts(*copts, GROUP_ALL);
|
|
if (applyopts_single(fd, popts, PH_INIT) < 0) return -1;
|
|
applyopts2(-1, popts, PH_INIT, PH_EARLY);
|
|
|
|
retropt_bool(popts, OPT_NOFORK, &nofork);
|
|
withfork = !nofork;
|
|
|
|
if ((retropt_string(popts, OPT_COMMTYPE, &commname)) >= 0) {
|
|
if ((commtype = getcommtype(commname)) < 0) {
|
|
Error1("bad communication type \"%s\"", commname);
|
|
commtype = XIOCOMM_SOCKETPAIRS;
|
|
}
|
|
}
|
|
|
|
retropt_bool(popts, OPT_PIPES, &usepipes);
|
|
#if HAVE_PTY
|
|
retropt_bool(popts, OPT_PTY, &usebestpty);
|
|
#if HAVE_OPENPTY
|
|
retropt_bool(popts, OPT_OPENPTY, &useopenpty);
|
|
#endif
|
|
#if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
|
|
retropt_bool(popts, OPT_PTMX, &useptmx);
|
|
#endif
|
|
usepty = (usebestpty
|
|
#if HAVE_OPENPTY
|
|
|| useopenpty
|
|
#endif
|
|
#if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)
|
|
|| useptmx
|
|
#endif
|
|
);
|
|
if (usepipes && usepty) {
|
|
Warn("_xioopen_progcall(): options \"pipes\" and \"pty\" must not be specified together; ignoring \"pipes\"");
|
|
usepipes = false;
|
|
}
|
|
#endif /* HAVE_PTY */
|
|
|
|
if (usepty) {
|
|
commtype = XIOCOMM_PTY;
|
|
} else if (usepipes) {
|
|
commtype = XIOCOMM_PIPES;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
/* retrieve options regarding file descriptors */
|
|
if (!retropt_int(popts, OPT_LEFTFD, &fdi)) {
|
|
fdo = fdi;
|
|
}
|
|
|
|
if (retropt_int(popts, OPT_LEFTINFD, (unsigned short *)&fdi) >= 0) {
|
|
if ((xioflags&XIO_ACCMODE) == XIO_RDONLY) {
|
|
Error("_xioopen_progcall(): option fdin is useless in read-only mode");
|
|
}
|
|
}
|
|
if (retropt_int(popts, OPT_LEFTOUTFD, (unsigned short *)&fdo) >= 0) {
|
|
if ((xioflags&XIO_ACCMODE) == XIO_WRONLY) {
|
|
Error("_xioopen_progcall(): option fdout is useless in write-only mode");
|
|
}
|
|
}
|
|
|
|
if (!retropt_int(popts, OPT_RIGHTFD, &rightin)) {
|
|
rightout = rightin;
|
|
}
|
|
retropt_int(popts, OPT_RIGHTINFD, &rightin);
|
|
retropt_int(popts, OPT_RIGHTOUTFD, &rightout);
|
|
/* when the superordinate communication type provides two distinct fds we
|
|
cannot pass just one fd to the program */
|
|
if (rw == XIO_RDWR && rightin==rightout) {
|
|
struct stat rstat, wstat;
|
|
if (Fstat(xfd->rfd, &rstat) < 0)
|
|
Error2("fstat(%d, ...): %s", xfd->rfd, strerror(errno));
|
|
if (Fstat(xfd->wfd, &wstat) < 0)
|
|
Error2("fstat(%d, ...): %s", xfd->wfd, strerror(errno));
|
|
if (memcmp(&rstat, &wstat, sizeof(rstat))) {
|
|
Error("exec/system: your rightfd options require the same FD for both directions but the communication environment provides two different FDs");
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
if (rw == XIO_WRONLY) {
|
|
if (fd->howtoclose == XIOCLOSE_UNSPEC) {
|
|
fd->howtoclose = XIOCLOSE_SLEEP_SIGTERM;
|
|
}
|
|
}
|
|
if (withfork) {
|
|
const char *typename;
|
|
if (!(xioflags&XIO_MAYCHILD)) {
|
|
Error("fork for exec not allowed in this context");
|
|
/*!! free something */
|
|
return -1;
|
|
}
|
|
fd->flags |= XIO_DOESCHILD;
|
|
|
|
switch (commtype) {
|
|
case XIOCOMM_PIPES: typename = "pipes"; break;
|
|
#if HAVE_PTY
|
|
case XIOCOMM_PTY: typename = "pty"; break;
|
|
case XIOCOMM_PTYS: typename = "two pty's"; break;
|
|
#endif /* HAVE_PTY */
|
|
case XIOCOMM_SOCKETPAIR: typename = "socketpair"; break;
|
|
case XIOCOMM_SOCKETPAIRS: typename = "two socketpairs"; break;
|
|
#if _WITH_TCP
|
|
case XIOCOMM_TCP: typename = "TCP socket pair"; break;
|
|
case XIOCOMM_TCP4: typename = "TCP4 socket pair"; break;
|
|
case XIOCOMM_TCP4_LISTEN: typename = "TCP4 listen socket pair"; break;
|
|
#endif
|
|
default: typename = NULL; break;
|
|
}
|
|
Notice2("forking off child, using %s for %s",
|
|
typename, ddirection[rw]);
|
|
}
|
|
applyopts(-1, popts, PH_PREBIGEN);
|
|
|
|
if (inter) {
|
|
saverfd = xfd->rfd;
|
|
savewfd = xfd->wfd;
|
|
xfd->howtoshut = XIOSHUT_UNSPEC;
|
|
xfd->howtoclose = XIOCLOSE_UNSPEC;
|
|
}
|
|
|
|
if (!withfork) {
|
|
/*0 struct single *stream1, *stream2;*/
|
|
|
|
free(*copts);
|
|
*copts = moveopts(popts, GROUP_ALL);
|
|
/* what if WE are sock1 ? */
|
|
#if 1
|
|
if (!(xioflags & XIO_MAYEXEC /* means exec+nofork */)) {
|
|
Error("nofork option is not allowed here");
|
|
/*!! free something */
|
|
return -1;
|
|
}
|
|
fd->flags |= XIO_DOESEXEC;
|
|
#else /*!! */
|
|
if (sock1 == NULL) {
|
|
Fatal("nofork option must no be applied to first socat address");
|
|
}
|
|
#endif
|
|
if (fd->howtoclose == XIOCLOSE_UNSPEC) {
|
|
fd->howtoclose = XIOCLOSE_CLOSE;
|
|
}
|
|
|
|
#if 0 /*!! */
|
|
if (sock1->tag == XIO_TAG_DUAL) {
|
|
stream1 = &sock1->dual.stream[0]->stream;
|
|
stream2 = &sock1->dual.stream[1]->stream;
|
|
} else {
|
|
stream1 = &sock1->stream;
|
|
stream2 = &sock1->stream;
|
|
}
|
|
if (stream1->dtype == DATA_READLINE || stream2->dtype == DATA_READLINE ||
|
|
stream1->dtype == DATA_OPENSSL || stream2->dtype == DATA_OPENSSL
|
|
) {
|
|
Error("with option nofork, openssl and readline in address1 do not work");
|
|
}
|
|
if (stream1->lineterm != LINETERM_RAW ||
|
|
stream2->lineterm != LINETERM_RAW ||
|
|
stream1->ignoreeof || stream2->ignoreeof) {
|
|
Warn("due to option nofork, address1 options for lineterm and igoreeof do not apply");
|
|
}
|
|
#endif
|
|
|
|
/*! problem: when fdi==WRFD(sock[0]) or fdo==RDFD(sock[0]) */
|
|
if (rw != XIO_WRONLY) {
|
|
if (XIO_GETRDFD(sock[0]/*!!!*/) == fdi) {
|
|
if (Fcntl_l(fdi, F_SETFD, 0) < 0) {
|
|
Warn2("fcntl(%d, F_SETFD, 0): %s", fdi, strerror(errno));
|
|
}
|
|
if (Dup2(XIO_GETRDFD(sock[0]), fdi) < 0) {
|
|
Error3("dup2(%d, %d): %s",
|
|
XIO_GETRDFD(sock[0]), fdi, strerror(errno));
|
|
}
|
|
/*0 Info2("dup2(%d, %d)", XIO_GETRDFD(sock[0]), fdi);*/
|
|
} else {
|
|
if (Dup2(XIO_GETRDFD(sock[0]), fdi) < 0) {
|
|
Error3("dup2(%d, %d): %s",
|
|
XIO_GETRDFD(sock[0]), fdi, strerror(errno));
|
|
}
|
|
/*0 Info2("dup2(%d, %d)", XIO_GETRDFD(sock[0]), fdi);*/
|
|
}
|
|
}
|
|
if (rw != XIO_RDONLY) {
|
|
if (XIO_GETWRFD(sock[0]) == fdo) {
|
|
if (Fcntl_l(fdo, F_SETFD, 0) < 0) {
|
|
Warn2("fcntl(%d, F_SETFD, 0): %s", fdo, strerror(errno));
|
|
}
|
|
if (Dup2(XIO_GETWRFD(sock[0]), fdo) < 0) {
|
|
Error3("dup2(%d, %d): %s)",
|
|
XIO_GETWRFD(sock[0]), fdo, strerror(errno));
|
|
}
|
|
/*0 Info2("dup2(%d, %d)", XIO_GETWRFD(sock[0]), fdo);*/
|
|
} else {
|
|
if (Dup2(XIO_GETWRFD(sock[0]), fdo) < 0) {
|
|
Error3("dup2(%d, %d): %s)",
|
|
XIO_GETWRFD(sock[0]), fdo, strerror(errno));
|
|
}
|
|
/*0 Info2("dup2(%d, %d)", XIO_GETWRFD(sock[0]), fdo);*/
|
|
}
|
|
}
|
|
} else /* withfork */
|
|
/* create fd pair(s), set related xfd parameters, and apply options */
|
|
switch (commtype) {
|
|
|
|
#if HAVE_PTY
|
|
case XIOCOMM_PTY:
|
|
/*!indent*/
|
|
#if defined(HAVE_DEV_PTMX)
|
|
# define PTMX "/dev/ptmx" /* Linux */
|
|
#elif HAVE_DEV_PTC
|
|
# define PTMX "/dev/ptc" /* AIX 4.3.3 */
|
|
#endif
|
|
fd->dtype = XIODATA_PTY;
|
|
#if 0
|
|
if (fd->howtoshut == XIOSHUT_UNSPEC) {
|
|
fd->howtoshut = XIOSHUTRD_SIGTERM|XIOSHUTWR_SIGHUP;
|
|
}
|
|
if (fd->howtoclose == XIOCLOSE_UNSPEC) {
|
|
fd->howtoclose = XIOCLOSE_CLOSE_SIGTERM;
|
|
}
|
|
#endif
|
|
|
|
if (xiopty(usebestpty||useptmx, &ttyfd, &ptyfd) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
free(*copts);
|
|
if ((*copts = moveopts(popts, GROUP_TERMIOS|GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
|
|
return -1;
|
|
}
|
|
applyopts_cloexec(ptyfd, popts);/*!*/
|
|
|
|
/* exec:...,pty did not kill child process under some circumstances */
|
|
if (fd->howtoshut == XIOSHUT_UNSPEC) {
|
|
fd->howtoshut = XIOSHUTRD_SIGTERM|XIOSHUTWR_SIGHUP;
|
|
}
|
|
if (fd->howtoclose == XIOCLOSE_UNSPEC) {
|
|
fd->howtoclose = XIOCLOSE_CLOSE_SIGTERM;
|
|
}
|
|
|
|
/* this for parent, was after fork */
|
|
applyopts(ptyfd, popts, PH_FD);
|
|
applyopts(ptyfd, popts, PH_LATE);
|
|
if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
|
|
|
|
if (XIOWITHRD(rw)) fd->rfd = ptyfd;
|
|
if (XIOWITHWR(rw)) fd->wfd = ptyfd;
|
|
|
|
/* this for child, was after fork */
|
|
applyopts(ttyfd, *copts, PH_FD);
|
|
|
|
break;
|
|
#endif /* HAVE_PTY */
|
|
|
|
case XIOCOMM_PIPES: {
|
|
/*!indent*/
|
|
struct opt *popts2, *copts2;
|
|
|
|
if (rw == XIO_RDWR) {
|
|
fd->dtype = XIODATA_2PIPE;
|
|
}
|
|
if (fd->howtoshut == XIOSHUT_UNSPEC || fd->howtoshut == XIOSHUT_DOWN) {
|
|
fd->howtoshut = XIOSHUT_CLOSE;
|
|
}
|
|
if (fd->howtoclose == XIOCLOSE_UNSPEC) {
|
|
fd->howtoclose = XIOCLOSE_CLOSE;
|
|
}
|
|
|
|
if (rw != XIO_WRONLY) {
|
|
if (Pipe(rdpip) < 0) {
|
|
Error2("pipe(%p): %s", rdpip, strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
/*0 Info2("pipe({%d,%d})", rdpip[0], rdpip[1]);*/
|
|
/* rdpip[0]: read by socat; rdpip[1]: write by child */
|
|
free(*copts);
|
|
if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS))
|
|
== NULL) {
|
|
return -1;
|
|
}
|
|
|
|
popts2 = copyopts(popts, GROUP_ALL);
|
|
copts2 = copyopts(*copts, GROUP_ALL);
|
|
|
|
if (rw != XIO_WRONLY) {
|
|
applyopts_cloexec(rdpip[0], popts);
|
|
applyopts(rdpip[0], popts, PH_FD);
|
|
applyopts(rdpip[1], *copts, PH_FD);
|
|
}
|
|
|
|
if (rw != XIO_RDONLY) {
|
|
if (Pipe(wrpip) < 0) {
|
|
Error2("pipe(%p): %s", wrpip, strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
/*0 Info2("pipe({%d,%d})", wrpip[0], wrpip[1]);*/
|
|
|
|
/* wrpip[1]: write by socat; wrpip[0]: read by child */
|
|
if (rw != XIO_RDONLY) {
|
|
applyopts_cloexec(wrpip[1], popts2);
|
|
applyopts(wrpip[1], popts2, PH_FD);
|
|
applyopts(wrpip[0], copts2, PH_FD);
|
|
}
|
|
|
|
/* this for parent, was after fork */
|
|
if (XIOWITHRD(rw)) fd->rfd = rdpip[0];
|
|
if (XIOWITHWR(rw)) fd->wfd = wrpip[1];
|
|
applyopts(fd->rfd, popts, PH_FD);
|
|
applyopts(fd->rfd, popts, PH_LATE);
|
|
if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
|
|
break;
|
|
}
|
|
|
|
case XIOCOMM_SOCKETPAIR: {
|
|
/*!indent*/
|
|
int pf = AF_UNIX;
|
|
retropt_int(popts, OPT_PROTOCOL_FAMILY, &pf);
|
|
result = xiosocketpair(popts, pf, SOCK_STREAM, 0, sv);
|
|
if (result < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (xfd->howtoshut == XIOSHUT_UNSPEC) {
|
|
xfd->howtoshut = XIOSHUT_DOWN;
|
|
}
|
|
if (xfd->howtoclose == XIOCLOSE_UNSPEC) {
|
|
xfd->howtoclose = XIOCLOSE_CLOSE;
|
|
}
|
|
|
|
/*0 Info5("socketpair(%d, %d, %d, {%d,%d})",
|
|
d, type, protocol, sv[0], sv[1]);*/
|
|
free(*copts);
|
|
if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
|
|
return -1;
|
|
}
|
|
applyopts(sv[0], *copts, PH_PASTSOCKET);
|
|
applyopts(sv[1], popts, PH_PASTSOCKET);
|
|
|
|
applyopts_cloexec(sv[0], *copts);
|
|
applyopts(sv[0], *copts, PH_FD);
|
|
applyopts(sv[1], popts, PH_FD);
|
|
|
|
applyopts(sv[0], *copts, PH_PREBIND);
|
|
applyopts(sv[0], *copts, PH_BIND);
|
|
applyopts(sv[0], *copts, PH_PASTBIND);
|
|
applyopts(sv[1], popts, PH_PREBIND);
|
|
applyopts(sv[1], popts, PH_BIND);
|
|
applyopts(sv[1], popts, PH_PASTBIND);
|
|
|
|
Warn1("xio-progcall.c: fd->howtoshut == %d", fd->howtoshut);
|
|
if (inter || fd->howtoshut == XIOSHUT_UNSPEC) {
|
|
fd->howtoshut = XIOSHUT_DOWN;
|
|
}
|
|
if (fd->howtoclose == XIOCLOSE_UNSPEC) {
|
|
fd->howtoclose = XIOCLOSE_SIGTERM;
|
|
}
|
|
|
|
/* this for parent, was after fork */
|
|
/*!!!*/ Warn2("2: fd->rfd==%d, fd->wfd==%d", fd->rfd, fd->wfd);
|
|
if (XIOWITHRD(rw)) fd->rfd = sv[0];
|
|
if (XIOWITHWR(rw)) fd->wfd = sv[0];
|
|
/*!!!*/ Warn2("3: fd->rfd==%d, fd->wfd==%d", fd->rfd, fd->wfd);
|
|
applyopts(fd->rfd, popts, PH_FD);
|
|
applyopts(fd->rfd, popts, PH_LATE);
|
|
if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
|
|
}
|
|
break;
|
|
|
|
case XIOCOMM_TCP:
|
|
case XIOCOMM_TCP4: {
|
|
/*!indent*/
|
|
int pf = AF_INET;
|
|
xiofd_t socatfd, execfd;
|
|
retropt_int(popts, OPT_PROTOCOL_FAMILY, &pf);
|
|
if (xiocommpair(commtype, XIOWITHWR(rw), XIOWITHRD(rw),
|
|
0, &socatfd, &execfd) < 0) {
|
|
return -1;
|
|
}
|
|
free(*copts);
|
|
if ((*copts = moveopts(popts, GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
|
|
return -1;
|
|
}
|
|
sv[0] = socatfd.rfd; /*!!! r/w */
|
|
sv[1] = execfd.wfd;
|
|
applyopts(socatfd.rfd, *copts, PH_PASTSOCKET);
|
|
applyopts(execfd.rfd, popts, PH_PASTSOCKET);
|
|
|
|
applyopts_cloexec(sv[0], *copts);
|
|
applyopts(sv[0], *copts, PH_FD);
|
|
applyopts(sv[1], popts, PH_FD);
|
|
|
|
applyopts(sv[0], *copts, PH_PREBIND);
|
|
applyopts(sv[0], *copts, PH_BIND);
|
|
applyopts(sv[0], *copts, PH_PASTBIND);
|
|
applyopts(sv[1], popts, PH_PREBIND);
|
|
applyopts(sv[1], popts, PH_BIND);
|
|
applyopts(sv[1], popts, PH_PASTBIND);
|
|
|
|
Warn1("xio-progcall.c: fd->howtoshut == %d", fd->howtoshut);
|
|
if (inter || fd->howtoshut == XIOSHUT_UNSPEC) {
|
|
fd->howtoshut = XIOSHUT_DOWN;
|
|
}
|
|
if (fd->howtoclose == XIOCLOSE_UNSPEC) {
|
|
fd->howtoclose = XIOCLOSE_SIGTERM;
|
|
}
|
|
|
|
/* this for parent, was after fork */
|
|
if (XIOWITHRD(rw)) fd->rfd = sv[0];
|
|
if (XIOWITHWR(rw)) fd->wfd = sv[0];
|
|
applyopts(fd->rfd, popts, PH_FD);
|
|
applyopts(fd->rfd, popts, PH_LATE);
|
|
if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
|
|
}
|
|
break;
|
|
|
|
#if LATER
|
|
case XIOCOMM_TCP4_LISTEN: {
|
|
/*!indent*/
|
|
int pf = AF_INET;
|
|
xiofd_t socatfd, execfd;
|
|
retropt_int(popts, OPT_PROTOCOL_FAMILY, &pf);
|
|
if (xiocommpair(commtype, XIOWITHWR(rw), XIOWITHRD(rw),
|
|
0, &socatfd, &execfd) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
free(*copts);
|
|
if ((*copts = moveopts(popts, GROUP_TERMIOS|GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
|
|
return -1;
|
|
}
|
|
applyopts_cloexec(ptyfd, popts);/*!*/
|
|
}
|
|
break;
|
|
#endif /* LATER */
|
|
|
|
case XIOCOMM_SOCKETPAIRS:
|
|
case XIOCOMM_PTYS: {
|
|
xiofd_t socatfd, execfd;
|
|
struct termios andmask, ormask;
|
|
switch (commtype) {
|
|
case XIOCOMM_SOCKETPAIRS:
|
|
if (xiocommpair(commtype, XIOWITHWR(rw), XIOWITHRD(rw),
|
|
0, &socatfd, &execfd, PF_UNIX, SOCK_STREAM, 0) < 0)
|
|
return -1;
|
|
break;
|
|
case XIOCOMM_PTYS:
|
|
if (xiocommpair(commtype, XIOWITHWR(rw), XIOWITHRD(rw),
|
|
0, &socatfd, &execfd, &andmask, &ormask) < 0)
|
|
return -1;
|
|
break;
|
|
}
|
|
|
|
free(*copts);
|
|
if ((*copts = copyopts(popts, GROUP_TERMIOS|GROUP_FORK)) == NULL) {
|
|
return -1;
|
|
}
|
|
if (socatfd.rfd >= 0) {
|
|
applyopts_cloexec(socatfd.rfd, *copts);/*!*/
|
|
applyopts(socatfd.rfd, *copts, PH_FD);
|
|
applyopts(socatfd.rfd, *copts, PH_LATE);
|
|
}
|
|
if (applyopts_single(xfd, *copts, PH_LATE) < 0) return -1;
|
|
|
|
free(*copts);
|
|
if ((*copts = moveopts(popts, GROUP_TERMIOS|GROUP_FORK|GROUP_EXEC|GROUP_PROCESS)) == NULL) {
|
|
return -1;
|
|
}
|
|
if (socatfd.wfd >= 0) {
|
|
applyopts_cloexec(socatfd.wfd, *copts);/*!*/
|
|
applyopts(socatfd.wfd, *copts, PH_FD);
|
|
applyopts(socatfd.wfd, *copts, PH_LATE);
|
|
}
|
|
if (applyopts_single(xfd, *copts, PH_LATE) < 0) return -1;
|
|
|
|
if (XIOWITHRD(rw)) xfd->rfd = socatfd.rfd;
|
|
if (XIOWITHWR(rw)) xfd->wfd = socatfd.wfd;
|
|
xfd->dtype = socatfd.dtype;
|
|
if (xfd->howtoshut == XIOSHUT_UNSPEC)
|
|
xfd->howtoshut = socatfd.howtoshut;
|
|
if (fd->howtoclose == XIOCLOSE_UNSPEC) {
|
|
fd->howtoclose = XIOWITHRD(rw)?XIOCLOSE_CLOSE_SIGTERM:XIOCLOSE_SLEEP_SIGTERM;
|
|
}
|
|
wrpip[0] = execfd.rfd;
|
|
rdpip[1] = execfd.wfd;
|
|
rdpip[0] = socatfd.rfd;
|
|
wrpip[1] = socatfd.wfd;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Error1("_xioopen_progcall() internal: commtype %d not handled",
|
|
commtype);
|
|
break;
|
|
}
|
|
|
|
/*0 if ((optpr = copyopts(*copts, GROUP_PROCESS)) == NULL)
|
|
return -1;*/
|
|
retropt_bool(*copts, OPT_STDERR, &withstderr);
|
|
|
|
xiosetchilddied(); /* set SIGCHLD handler */
|
|
|
|
xiosetchilddied(); /* set SIGCHLD handler */
|
|
|
|
if (withfork) {
|
|
sigset_t set, oldset;
|
|
|
|
sigemptyset(&set);
|
|
sigaddset(&set, SIGCHLD);
|
|
Sigprocmask(SIG_BLOCK, &set, &oldset); /* disable SIGCHLD */
|
|
pid = xio_fork(true, E_ERROR);
|
|
if (pid < 0) {
|
|
Sigprocmask(SIG_SETMASK, &oldset, NULL);
|
|
Error1("fork(): %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (pid > 0) {
|
|
/* for parent (this is our socat process) */
|
|
xiosigchld_register(pid, xiosigaction_child, fd);
|
|
Sigprocmask(SIG_SETMASK, &oldset, NULL); /* enable SIGCHLD */
|
|
}
|
|
|
|
if (pid == 0) { /* child */
|
|
/* drop parents locks, reset FIPS... */
|
|
if (xio_forked_inchild() != 0) {
|
|
Exit(1);
|
|
}
|
|
Sigprocmask(SIG_SETMASK, &oldset, NULL); /* enable SIGCHLD */
|
|
}
|
|
}
|
|
if (!withfork || pid == 0) { /* child */
|
|
uid_t user;
|
|
gid_t group;
|
|
|
|
if (withfork) {
|
|
/* The child should have default handling for SIGCHLD. */
|
|
/* In particular, it's not defined whether ignoring SIGCHLD is inheritable. */
|
|
if (Signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
|
|
Warn1("signal(SIGCHLD, SIG_DFL): %s", strerror(errno));
|
|
}
|
|
|
|
/* dup2() the fds to desired values, close old fds, and apply late
|
|
options */
|
|
switch (commtype) {
|
|
#if HAVE_PTY
|
|
case XIOCOMM_PTY:
|
|
if (rw != XIO_RDONLY && fdi != ttyfd) {
|
|
if (Dup2(ttyfd, fdi) < 0) {
|
|
Error3("dup2(%d, %d): %s", ttyfd, fdi, strerror(errno));
|
|
return -1; }
|
|
/*0 Info2("dup2(%d, %d)", ttyfd, fdi);*/
|
|
}
|
|
if (rw != XIO_WRONLY && fdo != ttyfd) {
|
|
if (Dup2(ttyfd, fdo) < 0) {
|
|
Error3("dup2(%d, %d): %s", ttyfd, fdo, strerror(errno));
|
|
return -1; }
|
|
/*0 Info2("dup2(%d, %d)", ttyfd, fdo);*/
|
|
}
|
|
if ((rw == XIO_RDONLY || fdi != ttyfd) &&
|
|
(rw == XIO_WRONLY || fdo != ttyfd)) {
|
|
applyopts_cloexec(ttyfd, *copts);
|
|
}
|
|
|
|
applyopts(ttyfd, *copts, PH_LATE);
|
|
applyopts(ttyfd, *copts, PH_LATE2);
|
|
break;
|
|
#endif /* HAVE_PTY */
|
|
|
|
case XIOCOMM_PIPES:
|
|
case XIOCOMM_SOCKETPAIRS:
|
|
case XIOCOMM_PTYS:
|
|
{
|
|
/*!indent*/
|
|
/* we might have a temporary conflict between what FDs are
|
|
currently allocated, and which are to be used. We try to find
|
|
a graceful solution via temporary descriptors */
|
|
int tmpi, tmpo;
|
|
|
|
/* needed with system() (not with exec()) */
|
|
if (XIOWITHRD(rw)) Close(rdpip[0]);
|
|
if (XIOWITHWR(rw)) Close(wrpip[1]);
|
|
#if 0
|
|
/*! might not be needed */
|
|
if (XIOWITHRD(rw)) Close(rdpip[0]);
|
|
if (XIOWITHWR(rw)) Close(wrpip[1]);
|
|
|
|
if (fdi == rdpip[1]) { /* a conflict here */
|
|
if ((tmpi = Dup(wrpip[0])) < 0) {
|
|
Error2("dup(%d): %s", wrpip[0], strerror(errno));
|
|
return -1;
|
|
}
|
|
/*0 Info2("dup(%d) -> %d", wrpip[0], tmpi);*/
|
|
rdpip[1] = tmpi;
|
|
}
|
|
if (fdo == wrpip[0]) { /* a conflict here */
|
|
if ((tmpo = Dup(rdpip[1])) < 0) {
|
|
Error2("dup(%d): %s", rdpip[1], strerror(errno));
|
|
return -1;
|
|
}
|
|
/*0 Info2("dup(%d) -> %d", rdpip[1], tmpo);*/
|
|
wrpip[0] = tmpo;
|
|
}
|
|
|
|
if (rw != XIO_WRONLY && rdpip[1] != fdo) {
|
|
if (Dup2(rdpip[1], fdo) < 0) {
|
|
Error3("dup2(%d, %d): %s", rdpip[1], fdo, strerror(errno));
|
|
return -1;
|
|
}
|
|
Close(rdpip[1]);
|
|
/*0 Info2("dup2(%d, %d)", rdpip[1], fdo);*/
|
|
/*0 applyopts_cloexec(fdo, *copts);*/
|
|
}
|
|
if (rw != XIO_RDONLY && wrpip[0] != fdi) {
|
|
if (Dup2(wrpip[0], fdi) < 0) {
|
|
Error3("dup2(%d, %d): %s", wrpip[0], fdi, strerror(errno));
|
|
return -1;
|
|
}
|
|
Close(wrpip[0]);
|
|
/*0 Info2("dup2(%d, %d)", wrpip[0], fdi);*/
|
|
/*0 applyopts_cloexec(wrpip[0], *copts);*/ /* option is already consumed! */
|
|
/* applyopts_cloexec(fdi, *copts);*/ /* option is already consumed! */
|
|
}
|
|
#else
|
|
result = reassignfds(XIOWITHWR(rw)?wrpip[0]:-1,
|
|
XIOWITHRD(rw)?rdpip[1]:-1,
|
|
fdi, fdo);
|
|
if (result < 0) return result;
|
|
#endif
|
|
applyopts(fdi, *copts, PH_LATE);
|
|
applyopts(fdo, *copts, PH_LATE);
|
|
applyopts(fdi, *copts, PH_LATE2);
|
|
applyopts(fdo, *copts, PH_LATE2);
|
|
break;
|
|
}
|
|
case XIOCOMM_SOCKETPAIR:
|
|
case XIOCOMM_TCP:
|
|
case XIOCOMM_TCP4:
|
|
case XIOCOMM_TCP4_LISTEN:
|
|
/*!indent*/
|
|
if (rw != XIO_RDONLY && fdi != sv[1]) {
|
|
if (Dup2(sv[1], fdi) < 0) {
|
|
Error3("dup2(%d, %d): %s", sv[1], fdi, strerror(errno));
|
|
return -1; }
|
|
/*0 Info2("dup2(%d, %d)", sv[1], fdi);*/
|
|
}
|
|
if (rw != XIO_WRONLY && fdo != sv[1]) {
|
|
if (Dup2(sv[1], fdo) < 0) {
|
|
Error3("dup2(%d, %d): %s", sv[1], fdo, strerror(errno));
|
|
return -1; }
|
|
/*0 Info2("dup2(%d, %d)", sv[1], fdo);*/
|
|
}
|
|
if (fdi != sv[1] && fdo != sv[1]) {
|
|
applyopts_cloexec(sv[1], *copts);
|
|
Close(sv[1]);
|
|
}
|
|
|
|
applyopts(fdi, *copts, PH_LATE);
|
|
applyopts(fdi, *copts, PH_LATE2);
|
|
Close(sv[1]);
|
|
break;
|
|
|
|
default:
|
|
Error1("_xioopen_progcall() internal: commtype %d not handled",
|
|
commtype);
|
|
break;
|
|
|
|
}
|
|
|
|
/* in case of an inter address, assign the right side FDs (e.g. 3 and 4) */
|
|
if (inter) {
|
|
Info2("preparing the right side FDs %d and %d for exec process",
|
|
rightin, rightout);
|
|
result = reassignfds(XIOWITHRD(rw)?saverfd:-1,
|
|
XIOWITHWR(rw)?savewfd:-1,
|
|
rightin, form==2?rightout:STDOUT_FILENO);
|
|
if (result < 0) return result;
|
|
if (form == 2) {
|
|
Fcntl_l(rightin, F_SETFD, 0);
|
|
Fcntl_l(rightout, F_SETFD, 0);
|
|
}
|
|
}
|
|
|
|
} /* withfork */
|
|
else /* !withfork */ {
|
|
applyopts(-1, *copts, PH_LATE);
|
|
applyopts(-1, *copts, PH_LATE2);
|
|
}
|
|
_xioopen_setdelayeduser();
|
|
/* set group before user - maybe you are not permitted afterwards */
|
|
if (retropt_gidt(*copts, OPT_SETGID, &group) >= 0) {
|
|
Setgid(group);
|
|
}
|
|
if (retropt_uidt(*copts, OPT_SETUID, &user) >= 0) {
|
|
Setuid(user);
|
|
}
|
|
if (withstderr) {
|
|
*duptostderr = fdo;
|
|
} else {
|
|
*duptostderr = -1;
|
|
}
|
|
|
|
return 0; /* indicate child process */
|
|
}
|
|
|
|
/* for parent (this is our socat process) */
|
|
Notice1("forked off child process "F_pid, pid);
|
|
|
|
#if 0
|
|
if ((popts = copyopts(*copts,
|
|
GROUP_FD|GROUP_TERMIOS|GROUP_FORK|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_FIFO)) == NULL)
|
|
return STAT_RETRYLATER;
|
|
#endif
|
|
|
|
/* in parent: close fds that are only needed in child */
|
|
switch (commtype) {
|
|
#if HAVE_PTY
|
|
case XIOCOMM_PTY:
|
|
if (Close(ttyfd) < 0) {
|
|
Info2("close(%d): %s", ttyfd, strerror(errno));
|
|
}
|
|
break;
|
|
#endif /* HAVE_PTY */
|
|
case XIOCOMM_SOCKETPAIR:
|
|
case XIOCOMM_TCP:
|
|
case XIOCOMM_TCP4:
|
|
case XIOCOMM_TCP4_LISTEN:
|
|
Close(sv[1]);
|
|
break;
|
|
case XIOCOMM_PIPES:
|
|
default:
|
|
if (XIOWITHWR(rw)) Close(wrpip[0]);
|
|
if (XIOWITHRD(rw)) Close(rdpip[1]);
|
|
break;
|
|
}
|
|
|
|
fd->child.pid = pid;
|
|
|
|
if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
|
|
applyopts_signal(fd, popts);
|
|
if ((numleft = leftopts(popts)) > 0) {
|
|
Error1("%d option(s) could not be used", numleft);
|
|
showleft(popts);
|
|
return STAT_NORETRY;
|
|
}
|
|
|
|
if (inter) {
|
|
if (XIOWITHRD(rw)) Close(saverfd);
|
|
if (XIOWITHWR(rw)) Close(savewfd);
|
|
}
|
|
|
|
return pid; /* indicate parent (main) process */
|
|
}
|
|
|
|
#endif /* WITH_EXEC || WITH_SYSTEM */
|
|
|
|
|
|
int setopt_path(struct opt *opts, char **path) {
|
|
if (retropt_string(opts, OPT_PATH, path) >= 0) {
|
|
if (setenv("PATH", *path, 1) < 0) {
|
|
Error1("setenv(\"PATH\", \"%s\", 1): insufficient space", *path);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* like dup(), but prints error on failure */
|
|
static int xiodup(int oldfd) {
|
|
int result;
|
|
if ((result = Dup(oldfd)) >= 0) return result;
|
|
Error2("dup2(%d): %s", oldfd, strerror(errno));
|
|
return result;
|
|
}
|
|
|
|
/* like dup2(), but prints error on failure and returns 0 on success */
|
|
static int xiodup2(int oldfd, int newfd) {
|
|
int result;
|
|
if ((result = Dup2(oldfd, newfd)) >= 0) return 0;
|
|
Error3("dup2(%d, %d): %s", oldfd, newfd, strerror(errno));
|
|
return result;
|
|
}
|
|
|
|
/* move the filedescriptors from the old handles to the new handles.
|
|
old -1 handles are ignored, new -1 handles are not closed.
|
|
returns 0 on success, -1 if a dup error occurred, or 1 on a close error
|
|
*/
|
|
static int reassignfds(int oldfd1, int oldfd2, int newfd1, int newfd2) {
|
|
int tmpfd;
|
|
int result;
|
|
|
|
Debug4("reassignfds(%d, %d, %d, %d)", oldfd1, oldfd2, newfd1, newfd2);
|
|
if (oldfd1 == newfd1) {
|
|
Fcntl_l(newfd1, F_SETFD, 0);
|
|
oldfd1 = -1;
|
|
}
|
|
if (oldfd2 == newfd2) {
|
|
Fcntl_l(newfd2, F_SETFD, 0);
|
|
oldfd2 = -1;
|
|
}
|
|
|
|
if (oldfd1 < 0 && oldfd2 < 0) return 0;
|
|
|
|
if (oldfd2 < 0) {
|
|
if ((result = xiodup2(oldfd1, newfd1)) < 0) return result;
|
|
if (newfd2 != oldfd1) if (Close(oldfd1) < 0) return 1;
|
|
return 0;
|
|
}
|
|
|
|
if (oldfd1 < 0) {
|
|
if ((result = xiodup2(oldfd2, newfd2)) < 0) return result;
|
|
if (oldfd2 >= 0) if (Close(oldfd2) < 0) return 1;
|
|
return 0;
|
|
}
|
|
|
|
if (oldfd2 == newfd1) {
|
|
if (oldfd1 == newfd2) {
|
|
/* exchange them */
|
|
if ((tmpfd = xiodup(oldfd2)) < 0) return tmpfd;
|
|
if ((result = xiodup2(oldfd1, newfd1)) < 0) return result;
|
|
if ((result = xiodup2(tmpfd, newfd2)) < 0) return result;
|
|
if (Close(tmpfd) < 0) return 1;
|
|
} else {
|
|
if ((result = xiodup2(oldfd2, newfd2)) < 0) return result;
|
|
if ((result = xiodup2(oldfd1, newfd1)) < 0) return result;
|
|
if (Close(oldfd1) < 0) return 1;
|
|
}
|
|
} else {
|
|
if (oldfd1 == newfd2) {
|
|
if ((result = xiodup2(oldfd1, newfd1)) < 0) return result;
|
|
if ((result = xiodup2(oldfd2, newfd2)) < 0) return result;
|
|
if (Close(oldfd2) < 0) return 1;
|
|
} else {
|
|
if ((result = xiodup2(oldfd1, newfd1)) < 0) return result;
|
|
if ((result = xiodup2(oldfd2, newfd2)) < 0) return result;
|
|
if (Close(oldfd1) < 0 || Close(oldfd2) < 0) return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|