/* a signal handler that possibly passes the signal to sub processes */ void socatsignalpass(int sig) { int i; struct socat_sig_desc *sigdesc; int _errno; _errno = errno; diag_in_handler = 1; Notice1("socatsignalpass(%d)", sig); if ((sigdesc = socat_get_sig_desc(sig)) == NULL) { /* is async-signal-safe */ diag_in_handler = 0; errno = _errno; return; } for (i=0; i<sigdesc->sig_use; ++i) { if (sigdesc->sig_pids[i]) { if (Kill(sigdesc->sig_pids[i], sig) < 0) { Warn2("kill("F_pid", %d): %m", sigdesc->sig_pids[i], sig); } } } #if !HAVE_SIGACTION Signal(sig, socatsignalpass); #endif /* !HAVE_SIGACTION */ Debug("socatsignalpass() ->"); diag_in_handler = 0; errno = _errno; }
/* use some file descriptor and apply the options. Set the FD_CLOEXEC flag. */ static int xioopen_fdnum(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) { char *a1; int rw = (xioflags&XIO_ACCMODE); int numfd; int result; if (argc != 2) { Error3("%s:%s: wrong number of parameters (%d instead of 1)", argv[0], argv[1], argc-1); } numfd = strtoul(argv[1], &a1, 0); if (*a1 != '\0') { Error1("error in FD number \"%s\"", argv[1]); } /* we dont want to see these fds in child processes */ if (Fcntl_l(numfd, F_SETFD, FD_CLOEXEC) < 0) { Warn2("fcntl(%d, F_SETFD, FD_CLOEXEC): %s", numfd, strerror(errno)); } Notice2("using file descriptor %d for %s", numfd, ddirection[rw]); if ((result = xioopen_fd(opts, rw, &xfd->stream, numfd, dummy2, dummy3)) < 0) { return result; } return 0; }
/* fills the socket address struct and returns its effective length. abstract is usually 0; != 0 generates an abstract socket address on Linux. tight!=0 calculates the resulting length from the path length, not from the structures length; this is more common. the struct need not be initialized when calling this function. */ socklen_t xiosetunix(int pf, struct sockaddr_un *saun, const char *path, bool abstract, bool tight) { size_t pathlen; socklen_t len; socket_un_init(saun); #ifdef WITH_ABSTRACT_UNIXSOCKET if (abstract) { if ((pathlen = strlen(path)) >= sizeof(saun->sun_path)) { Warn2("socket address "F_Zu" characters long, truncating to "F_Zu"", pathlen+1, sizeof(saun->sun_path)); } saun->sun_path[0] = '\0'; /* so it's abstract */ strncpy(saun->sun_path+1, path, sizeof(saun->sun_path)-1); if (tight) { len = sizeof(struct sockaddr_un)-sizeof(saun->sun_path)+ MIN(pathlen+1, sizeof(saun->sun_path)); #if HAVE_STRUCT_SOCKADDR_SALEN saun->sun_len = len; #endif } else { len = sizeof(struct sockaddr_un); } return len; } #endif /* WITH_ABSTRACT_UNIXSOCKET */ if ((pathlen = strlen(path)) > sizeof(saun->sun_path)) { Warn2("unix socket address "F_Zu" characters long, truncating to "F_Zu"", pathlen, sizeof(saun->sun_path)); } strncpy(saun->sun_path, path, sizeof(saun->sun_path)); if (tight) { len = sizeof(struct sockaddr_un)-sizeof(saun->sun_path)+ MIN(pathlen, sizeof(saun->sun_path)); #if HAVE_STRUCT_SOCKADDR_SALEN saun->sun_len = len; #endif } else { len = sizeof(struct sockaddr_un); } return len; }
/* applies to fd all options belonging to phase */ int applyopts_named(const char *filename, struct opt *opts, unsigned int phase) { struct opt *opt; if (!opts) return 0; opt = opts; while (opt->desc != ODESC_END) { if (opt->desc == ODESC_DONE || opt->desc->phase != phase && phase != PH_ALL || !(opt->desc->group & GROUP_NAMED)) { ++opt; continue; } switch (opt->desc->optcode) { case OPT_GROUP_EARLY: case OPT_GROUP: if (Chown(filename, -1, opt->value.u_gidt) < 0) { Error3("chown(\"%s\", -1, "F_gid"): %s", filename, opt->value.u_gidt, strerror(errno)); } break; case OPT_USER_EARLY: case OPT_USER: if (Chown(filename, opt->value.u_uidt, -1) < 0) { Error3("chown(\"%s\", "F_uid", -1): %s", filename, opt->value.u_uidt, strerror(errno)); } break; case OPT_PERM_EARLY: case OPT_PERM: if (Chmod(filename, opt->value.u_modet) < 0) { Error3("chmod(\"%s\", "F_mode"): %s", filename, opt->value.u_modet, strerror(errno)); } break; case OPT_UNLINK_EARLY: case OPT_UNLINK: case OPT_UNLINK_LATE: if (Unlink(filename) < 0) { if (errno == ENOENT) { Warn2("unlink(\"%s\"): %s", filename, strerror(errno)); } else { Error2("unlink(\"%s\"): %s", filename, strerror(errno)); } } break; case OPT_UMASK: if (Umask(opt->value.u_modet) < 0) { /* linux docu says it always succeeds, but who believes it? */ Error2("umask("F_mode"): %s", opt->value.u_modet, strerror(errno)); } break; default: Error1("applyopts_named(): option \"%s\" not implemented", opt->desc->defname); break; } opt->desc = ODESC_DONE; ++opt; } return 0; }
static void socat_unlock(void) { if (!havelock) return; if (socat_opts.lock.lockfile) { if (Unlink(socat_opts.lock.lockfile) < 0) { Warn2("unlink(\"%s\"): %s", socat_opts.lock.lockfile, strerror(errno)); } else { Info1("released lock \"%s\"", socat_opts.lock.lockfile); } } }
int xiosetchilddied(void) { #if HAVE_SIGACTION struct sigaction act; memset(&act, 0, sizeof(struct sigaction)); act.sa_flags = SA_NOCLDSTOP|SA_RESTART|SA_SIGINFO #ifdef SA_NOMASK |SA_NOMASK #endif ; act.sa_sigaction = childdied; if (Sigaction(SIGCHLD, &act, NULL) < 0) { /*! man does not say that errno is defined */ Warn2("sigaction(SIGCHLD, %p, NULL): %s", childdied, strerror(errno)); } #else /* HAVE_SIGACTION */ if (Signal(SIGCHLD, childdied) == SIG_ERR) { Warn2("signal(SIGCHLD, %p): %s", childdied, strerror(errno)); } #endif /* !HAVE_SIGACTION */ return 0; }
eFlag AttSet::checkRedefinitions(Sit S) { for (int i = 0; i < number(); i++) if ((*this)[i] -> getRedefinition()) { XSLElement *redef = (*this)[i] -> getRedefinition(); Str fullNameAtt, fullNameThis; redef -> getOwner().expandQStr((*this)[i] -> getAttName(), fullNameAtt); redef -> getOwner().expandQStr(getName(), fullNameThis); S.setCurrVDoc(redef); Warn2(S, W2_ATTSET_REDEF, fullNameAtt, fullNameThis); } return OK; }
/* analyse a file system entry, referred by file name */ int filan_file(const char *filename, FILE *outfile) { int fd = -1; int result; #if HAVE_STAT64 struct stat64 buf = {0}; #else struct stat buf = {0}; #endif /* !HAVE_STAT64 */ if (filan_followsymlinks) { #if HAVE_STAT64 result = Stat64(filename, &buf); #else result = Stat(filename, &buf); #endif /* !HAVE_STAT64 */ if (result < 0) { Warn3("stat(\"%s\", %p): %s", filename, &buf, strerror(errno)); } } else { #if HAVE_STAT64 result = Lstat64(filename, &buf); #else result = Lstat(filename, &buf); #endif /* !HAVE_STAT64 */ if (result < 0) { Warn3("lstat(\"%s\", %p): %s", filename, &buf, strerror(errno)); } } switch (buf.st_mode&S_IFMT) { #ifdef S_IFSOCK case S_IFSOCK: /* probably, it's useless to make a socket and describe it */ break; #endif /* S_IFSOCK */ default: if ((fd = Open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK #ifdef O_LARGEFILE |O_LARGEFILE #endif , 0700)) < 0) { Warn2("open(\"%s\", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE, 0700): %s", filename, strerror(errno)); } } result = filan_stat(&buf, fd, -1, outfile); fputc('\n', outfile); return result; }
/* open for writing: if the filesystem entry already exists, the data is appended if it does not exist, a file is created and the data is appended */ static int xioopen_open(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3) { const char *filename = argv[1]; int rw = (xioflags & XIO_ACCMODE); bool exists; bool opt_unlink_close = false; int result; /* remove old file, or set user/permissions on old file; parse options */ if ((result = _xioopen_named_early(argc, argv, fd, groups, &exists, opts)) < 0) { return result; } retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); if (opt_unlink_close) { if ((fd->stream.unlink_close = strdup(filename)) == NULL) { Error1("strdup(\"%s\"): out of memory", filename); } fd->stream.opt_unlink_close = true; } Notice3("opening %s \"%s\" for %s", filetypenames[(result&S_IFMT)>>12], filename, ddirection[rw]); if ((result = _xioopen_open(filename, rw, opts)) < 0) return result; fd->stream.fd = result; #if WITH_TERMIOS if (Isatty(fd->stream.fd)) { if (Tcgetattr(fd->stream.fd, &fd->stream.savetty) < 0) { Warn2("cannot query current terminal settings on fd %d: %s", fd->stream.fd, strerror(errno)); } else { fd->stream.ttyvalid = true; } } #endif /* WITH_TERMIOS */ applyopts_named(filename, opts, PH_FD); applyopts(fd->stream.fd, opts, PH_FD); applyopts_cloexec(fd->stream.fd, opts); applyopts_fchown(fd->stream.fd, opts); if ((result = _xio_openlate(&fd->stream, opts)) < 0) return result; return 0; }
static int xioopen_system(int argc, const char *argv[], struct opt *opts, int xioflags, /* XIO_RDONLY etc. */ xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3 ) { int status; char *path = NULL; int duptostderr; int result; const char *string = argv[1]; status = _xioopen_foxec(xioflags, &fd->stream, groups, &opts, &duptostderr); if (status < 0) return status; if (status == 0) { /* child */ int numleft; /* do not shutdown connections that belong our parent */ sock[0] = NULL; sock[1] = NULL; if (setopt_path(opts, &path) < 0) { /* this could be dangerous, so let us abort this child... */ Exit(1); } if ((numleft = leftopts(opts)) > 0) { Error1("%d option(s) could not be used", numleft); showleft(opts); return STAT_NORETRY; } /* only now redirect stderr */ if (duptostderr >= 0) { diag_dup(); Dup2(duptostderr, 2); } Info1("executing shell command \"%s\"", string); errno=0; result = System(string); if (result != 0) { Warn2("system(\"%s\") returned with status %d", string, result); if (errno != 0) Warn1("system(): %s", strerror(errno)); } Exit(result>>8); /* this child process */ }
/* retrieve and apply options to a standard file descriptor. Do not set FD_CLOEXEC flag. */ int xioopen_fd(struct opt *opts, int rw, xiosingle_t *xfd, int numfd, int dummy2, int dummy3) { xfd->fd = numfd; xfd->howtoend = END_NONE; #if WITH_TERMIOS if (Isatty(xfd->fd)) { if (Tcgetattr(xfd->fd, &xfd->savetty) < 0) { Warn2("cannot query current terminal settings on fd %d: %s", xfd->fd, strerror(errno)); } else { xfd->ttyvalid = true; } } #endif /* WITH_TERMIOS */ if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); applyopts2(xfd->fd, opts, PH_INIT, PH_FD); return _xio_openlate(xfd, opts); }
static int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3) { /* we expect the form: filename */ const char *name; xiosingle_t *xfd = &xxfd->stream; int pf = PF_UNIX; int socktype = SOCK_DGRAM; int protocol = 0; union sockaddr_union us; socklen_t uslen; bool tight = true; bool opt_unlink_early = false; bool opt_unlink_close = true; int result; if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); return STAT_NORETRY; } name = argv[1]; retropt_socket_pf(opts, &pf); retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); uslen = xiosetunix(pf, &us.un, name, abstract, tight); #if 1 /*!!! why bind option? */ retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 1, 0, 0); #endif if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early); if (opt_unlink_early) { if (Unlink(name) < 0) { if (errno == ENOENT) { Warn2("unlink(\"%s\"): %s", name, strerror(errno)); } else { Error2("unlink(\"%s\"): %s", name, strerror(errno)); } } } retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); if (opt_unlink_close) { if ((xfd->unlink_close = strdup(name)) == NULL) { Error1("strdup(\"%s\"): out of memory", name); } xfd->opt_unlink_close = true; } } xfd->para.socket.la.soa.sa_family = pf; xfd->dtype = XIODATA_RECV; result = _xioopen_dgram_recv(xfd, xioflags, &us.soa, uslen, opts, pf, socktype, protocol, E_ERROR); return result; }
static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3) { /* we expect the form: filename */ const char *name; xiosingle_t *xfd = &xxfd->stream; int pf = PF_UNIX; int socktype = SOCK_STREAM; int protocol = 0; struct sockaddr_un us; socklen_t uslen; bool tight = true; struct opt *opts0 = NULL; pid_t pid = Getpid(); bool opt_unlink_early = false; bool opt_unlink_close = true; int result; if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); return STAT_NORETRY; } name = argv[1]; retropt_socket_pf(opts, &pf); retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); uslen = xiosetunix(pf, &us, name, abstract, tight); xfd->howtoend = END_SHUTDOWN; if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); } if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY; applyopts(-1, opts, PH_INIT); applyopts(-1, opts, PH_EARLY); if (!(ABSTRACT && abstract)) { if (opt_unlink_early) { if (Unlink(name) < 0) { if (errno == ENOENT) { Warn2("unlink(\"%s\"): %s", name, strerror(errno)); } else { Error2("unlink(\"%s\"): %s", name, strerror(errno)); } } } /* trying to set user-early, perm-early etc. here is useless because file system entry is available only past bind() call. */ applyopts_named(name, opts, PH_EARLY); /* umask! */ } opts0 = copyopts(opts, GROUP_ALL); if ((result = xioopen_listen(xfd, xioflags, (struct sockaddr *)&us, uslen, opts, opts0, pf, socktype, protocol)) != 0) return result; /* we set this option as late as now because we should not remove an existing entry when bind() failed */ if (!(ABSTRACT && abstract)) { if (opt_unlink_close) { if (pid == Getpid()) { if ((xfd->unlink_close = strdup(name)) == NULL) { Error1("strdup(\"%s\"): out of memory", name); } xfd->opt_unlink_close = true; } } } return 0; }
/* process a bidirectional "stdio" or "-" argument with options. generate a dual address. */ int xioopen_stdio_bi(xiofile_t *sock) { struct opt *optspr; unsigned int groups1 = addr_stdio.groups; int result; if (xioopen_makedual(sock) < 0) { return -1; } sock->dual.stream[0]->tag = XIO_TAG_RDONLY; sock->dual.stream[0]->fd = 0 /*stdin*/; sock->dual.stream[1]->tag = XIO_TAG_WRONLY; sock->dual.stream[1]->fd = 1 /*stdout*/; sock->dual.stream[0]->howtoend = sock->dual.stream[1]->howtoend = END_NONE; #if WITH_TERMIOS if (Isatty(sock->dual.stream[0]->fd)) { if (Tcgetattr(sock->dual.stream[0]->fd, &sock->dual.stream[0]->savetty) < 0) { Warn2("cannot query current terminal settings on fd %d: %s", sock->dual.stream[0]->fd, strerror(errno)); } else { sock->dual.stream[0]->ttyvalid = true; } } if (Isatty(sock->dual.stream[1]->fd)) { if (Tcgetattr(sock->dual.stream[1]->fd, &sock->dual.stream[1]->savetty) < 0) { Warn2("cannot query current terminal settings on fd %d: %s", sock->dual.stream[1]->fd, strerror(errno)); } else { sock->dual.stream[1]->ttyvalid = true; } } #endif /* WITH_TERMIOS */ /* options here are one-time and one-direction, no second use */ retropt_bool(sock->stream.opts, OPT_IGNOREEOF, &sock->dual.stream[0]->ignoreeof); /* extract opts that should be applied only once */ if ((optspr = copyopts(sock->stream.opts, GROUP_PROCESS)) == NULL) { return -1; } /* here we copy opts, because most have to be applied twice! */ if ((sock->dual.stream[1]->opts = copyopts(sock->stream.opts, GROUP_FD|GROUP_APPL|(groups1&~GROUP_PROCESS))) == NULL) { return -1; } sock->dual.stream[0]->opts = sock->stream.opts; sock->stream.opts = NULL; if (applyopts_single(sock->dual.stream[0], sock->dual.stream[0]->opts, PH_INIT) < 0) return -1; if (applyopts_single(sock->dual.stream[1], sock->dual.stream[1]->opts, PH_INIT) < 0) return -1; applyopts(-1, sock->dual.stream[0]->opts, PH_INIT); applyopts(-1, sock->dual.stream[1]->opts, PH_INIT); if ((result = applyopts(-1, optspr, PH_EARLY)) < 0) return result; if ((result = applyopts(-1, optspr, PH_PREOPEN)) < 0) return result; /* apply options to first FD */ if ((result = applyopts(sock->dual.stream[0]->fd, sock->dual.stream[0]->opts, PH_ALL)) < 0) { return result; } if ((result = _xio_openlate(sock->dual.stream[0], sock->dual.stream[0]->opts)) < 0) { return result; } #if 0 /* ignore this opt */ retropt_bool(sock->dual.stream[0]->opts, OPT_COOL_WRITE); #endif /* apply options to second FD */ if ((result = applyopts(sock->dual.stream[1]->fd, sock->dual.stream[1]->opts, PH_ALL)) < 0) { return result; } if ((result = _xio_openlate(sock->dual.stream[1], sock->dual.stream[1]->opts)) < 0) { return result; } #if 0 if ((result = _xio_openlate(sock->dual.stream[1], optspr)) < 0) { return result; } #endif Notice("reading from and writing to stdio"); return 0; }
/* the ultimate(?) socat resolver function node: the address to be resolved; supported forms: 1.2.3.4 (IPv4 address) [::2] (IPv6 address) hostname (hostname resolving to IPv4 or IPv6 address) hostname.domain (fq hostname resolving to IPv4 or IPv6 address) service: the port specification; may be numeric or symbolic family: PF_INET, PF_INET6, or PF_UNSPEC permitting both socktype: SOCK_STREAM, SOCK_DGRAM protocol: IPPROTO_UDP, IPPROTO_TCP sau: an uninitialized storage for the resulting socket address returns: STAT_OK, STAT_RETRYLATER */ int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, union sockaddr_union *sau, socklen_t *socklen, unsigned long res_opts0, unsigned long res_opts1) { int port = -1; /* port number in network byte order */ char *numnode = NULL; size_t nodelen; unsigned long save_res_opts = 0; #if HAVE_GETADDRINFO struct addrinfo hints = {0}; struct addrinfo *res = NULL; #else /* HAVE_GETIPNODEBYNAME || nothing */ struct hostent *host; #endif int error_num; #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { if (!(_res.options & RES_INIT)) { Res_init(); /*!!! returns -1 on error */ } save_res_opts = _res.options; _res.options &= ~res_opts0; _res.options |= res_opts1; Debug2("changed _res.options from 0x%lx to 0x%lx", save_res_opts, _res.options); } #endif /* HAVE_RESOLV_H */ memset(sau, 0, *socklen); sau->soa.sa_family = family; /* if service is numeric we don't want to have a lookup (might take long with NIS), so we handle this specially */ if (service && isdigit(service[0]&0xff)) { char *extra; port = htons(strtoul(service, &extra, 0)); if (*extra != '\0') { Warn2("xiogetaddrinfo(, \"%s\", ...): extra trailing data \"%s\"", service, extra); } service = NULL; } /* the resolver functions might handle numeric forms of node names by reverse lookup, that's not what we want. So we detect these and handle them specially */ if (node && isdigit(node[0]&0xff)) { #if HAVE_GETADDRINFO hints.ai_flags |= AI_NUMERICHOST; #endif /* HAVE_GETADDRINFO */ if (family == PF_UNSPEC) { family = PF_INET; #if HAVE_GETADDRINFO } else if (family == PF_INET6) { /* map "explicitely" into IPv6 address space; getipnodebyname() does this with AI_V4MAPPED, but not getaddrinfo() */ if ((numnode = Malloc(strlen(node)+7+1)) == NULL) { #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_NORETRY; } sprintf(numnode, "::ffff:%s", node); node = numnode; hints.ai_flags |= AI_NUMERICHOST; #endif /* HAVE_GETADDRINFO */ } #if WITH_IP6 } else if (node && node[0] == '[' && node[(nodelen=strlen(node))-1]==']') { if ((numnode = Malloc(nodelen-1)) == NULL) { #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_NORETRY; } strncpy(numnode, node+1, nodelen-2); numnode[nodelen-2] = '\0'; node = numnode; #if HAVE_GETADDRINFO hints.ai_flags |= AI_NUMERICHOST; #endif /* HAVE_GETADDRINFO */ if (family == PF_UNSPEC) family = PF_INET6; #endif /* WITH_IP6 */ } #if HAVE_GETADDRINFO if (node != NULL || service != NULL) { struct addrinfo *record; if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM) { /* actual socket type value is not supported - fallback to a good one */ socktype = SOCK_DGRAM; } if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) { /* actual protocol value is not supported - fallback to a good one */ if (socktype == SOCK_DGRAM) { protocol = IPPROTO_UDP; } else { protocol = IPPROTO_TCP; } } hints.ai_flags |= AI_PASSIVE; hints.ai_family = family; hints.ai_socktype = socktype; hints.ai_protocol = protocol; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; if ((error_num = Getaddrinfo(node, service, &hints, &res)) != 0) { Error7("getaddrinfo(\"%s\", \"%s\", {%d,%d,%d,%d}, {}): %s", node, service, hints.ai_flags, hints.ai_family, hints.ai_socktype, hints.ai_protocol, (error_num == EAI_SYSTEM)? strerror(errno):gai_strerror(error_num)); if (res != NULL) freeaddrinfo(res); if (numnode) free(numnode); #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_RETRYLATER; } service = NULL; /* do not resolve later again */ record = res; if (family == PF_UNSPEC && xioopts.preferred_ip == '0') { /* we just take the first result */ family = res[0].ai_addr->sa_family; } if (family == PF_UNSPEC) { int trypf; trypf = (xioopts.preferred_ip=='6'?PF_INET6:PF_INET); /* we must look for a matching entry */ while (record != NULL) { if (record->ai_family == trypf) { family = trypf; break; /* family and record set accordingly */ } record = record->ai_next; } if (record == NULL) { /* we did not find a "preferred" entry, take the first */ record = res; family = res[0].ai_addr->sa_family; } } switch (family) { #if WITH_IP4 case PF_INET: if (*socklen > record->ai_addrlen) { *socklen = record->ai_addrlen; } memcpy(&sau->ip4, record->ai_addr, *socklen); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: #if _AIX /* older AIX versions pass wrong length, so we correct it */ record->ai_addr->sa_len = sizeof(struct sockaddr_in6); #endif if (*socklen > record->ai_addrlen) { *socklen = record->ai_addrlen; } memcpy(&sau->ip6, record->ai_addr, *socklen); break; #endif /* WITH_IP6 */ default: Error1("address resolved to unknown protocol family %d", record->ai_addr->sa_family); break; } freeaddrinfo(res); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); break; #endif /* WITH_IP6 */ } } #elif HAVE_GETIPNODEBYNAME /* !HAVE_GETADDRINFO */ if (node != NULL) { /* first fallback is getipnodebyname() */ if (family == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 family = xioopts.default_ip=='6'?PF_INET6:PF_INET; #elif WITH_IP6 family = PF_INET6; #else family = PF_INET; #endif } host = Getipnodebyname(node, family, AI_V4MAPPED, &error_num); if (host == NULL) { const static char ai_host_not_found[] = "Host not found"; const static char ai_no_address[] = "No address"; const static char ai_no_recovery[] = "No recovery"; const static char ai_try_again[] = "Try again"; const char *error_msg = "Unknown error"; switch (error_num) { case HOST_NOT_FOUND: error_msg = ai_host_not_found; break; case NO_ADDRESS: error_msg = ai_no_address; case NO_RECOVERY: error_msg = ai_no_recovery; case TRY_AGAIN: error_msg = ai_try_again; } Error2("getipnodebyname(\"%s\", ...): %s", node, error_msg); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); sau->soa.sa_family = PF_INET; memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4); break; #endif #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); sau->soa.sa_family = PF_INET6; memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16); break; #endif } } freehostent(host); } #else /* !HAVE_GETIPNODEBYNAME */ if (node != NULL) { /* this is not a typical IP6 resolver function - but Linux "man gethostbyname" says that the only supported address type with this function is AF_INET _at present_, so maybe this fallback will be useful somewhere sometimesin a future even for IP6 */ if (family == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 family = xioopts.default_ip=='6'?PF_INET6:PF_INET; #elif WITH_IP6 family = PF_INET6; #else family = PF_INET; #endif } /*!!! try gethostbyname2 for IP6 */ if ((host = Gethostbyname(node)) == NULL) { Error2("gethostbyname(\"%s\"): %s", node, h_errno == NETDB_INTERNAL ? strerror(errno) : hstrerror(h_errno)); #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_RETRYLATER; } if (host->h_addrtype != family) { Error2("xioaddrinfo(): \"%s\" does not resolve to %s", node, family==PF_INET?"IP4":"IP6"); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); sau->soa.sa_family = PF_INET; memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); sau->soa.sa_family = PF_INET6; memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16); break; #endif /* WITH_IP6 */ } } } #endif #if WITH_TCP || WITH_UDP if (service) { port = parseport(service, protocol); } if (port >= 0) { switch (family) { #if WITH_IP4 case PF_INET: sau->ip4.sin_port = port; break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: sau->ip6.sin6_port = port; break; #endif /* WITH_IP6 */ } } #endif /* WITH_TCP || WITH_UDP */ if (numnode) free(numnode); #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif /* HAVE_RESOLV_H */ return STAT_OK; }
int procan(FILE *outfile) { /*filan(0, outfile);*/ /* controlling terminal */ fprintf(outfile, "process id = "F_pid"\n", Getpid()); fprintf(outfile, "process parent id = "F_pid"\n", Getppid()); { int fd; if ((fd = Open("/dev/tty", O_NOCTTY, 0)) < 0) { fprintf(outfile, "controlling terminal: -\n"); } else { #if 1 fprintf(outfile, "controlling terminal: \"%s\"\n", Ttyname(fd)); #else char procpath[PATH_MAX], devpath[PATH_MAX+1]; int devlen; sprintf(procpath, "/proc/"F_pid"/fd/%d", Getpid(), 0 /*! fd*/); if ((devlen = Readlink(procpath, devpath, sizeof(devpath))) < 0) { ; } else { devpath[devlen] = '\0'; fprintf(outfile, "controlling terminal: \"%s\"\n", devpath); } #endif } } fprintf(outfile, "process group id = "F_pid"\n", Getpgrp()); #if HAVE_GETSID fprintf(outfile, "process session id = "F_pid"\n", Getsid(0)); #endif fprintf(outfile, "process group id if fg process / stdin = "F_pid"\n", Tcgetpgrp(0)); fprintf(outfile, "process group id if fg process / stdout = "F_pid"\n", Tcgetpgrp(1)); fprintf(outfile, "process group id if fg process / stderr = "F_pid"\n", Tcgetpgrp(2)); { int fd; if ((fd = Open("/dev/tty", O_RDWR, 0600)) >= 0) { fprintf(outfile, "process has a controlling terminal\n"); Close(fd); } else { fprintf(outfile, "process does not have a controlling terminal\n"); } } /* process owner, groups */ fprintf(outfile, "user id = "F_uid"\n", Getuid()); fprintf(outfile, "effective user id = "F_uid"\n", Geteuid()); fprintf(outfile, "group id = "F_gid"\n", Getgid()); fprintf(outfile, "effective group id = "F_gid"\n", Getegid()); { struct rlimit rlim; fprintf(outfile, "\nRESOURCE LIMITS\n"); fprintf(outfile, "resource current maximum\n"); if (getrlimit(RLIMIT_CPU, &rlim) < 0) { Warn2("getrlimit(RLIMIT_CPU, %p): %s", &rlim, strerror(errno)); } else { fprintf(outfile, "cpu time (seconds) %16"F_rlim_max"%16"F_rlim_max"\n", rlim.rlim_cur, rlim.rlim_max); } if (getrlimit(RLIMIT_FSIZE, &rlim) < 0) { Warn2("getrlimit(RLIMIT_FSIZE, %p): %s", &rlim, strerror(errno)); } else { fprintf(outfile, "file size (blocks) %16"F_rlim_max"%16"F_rlim_max"\n", rlim.rlim_cur, rlim.rlim_max); } if (getrlimit(RLIMIT_DATA, &rlim) < 0) { Warn2("getrlimit(RLIMIT_DATA, %p): %s", &rlim, strerror(errno)); } else { fprintf(outfile, "data seg size (kbytes) %16"F_rlim_max"%16"F_rlim_max"\n", rlim.rlim_cur, rlim.rlim_max); } if (getrlimit(RLIMIT_STACK, &rlim) < 0) { Warn2("getrlimit(RLIMIT_STACK, %p): %s", &rlim, strerror(errno)); } else { fprintf(outfile, "stack size (blocks) %16"F_rlim_max"%16"F_rlim_max"\n", rlim.rlim_cur, rlim.rlim_max); } if (getrlimit(RLIMIT_CORE, &rlim) < 0) { Warn2("getrlimit(RLIMIT_CORE, %p): %s", &rlim, strerror(errno)); } else { fprintf(outfile, "core file size (blocks) %16"F_rlim_max"%16"F_rlim_max"\n", rlim.rlim_cur, rlim.rlim_max); } #ifdef RLIMIT_RSS /* Linux, AIX; not Cygwin */ if (getrlimit(RLIMIT_RSS, &rlim) < 0) { Warn2("getrlimit(RLIMIT_RSS, %p): %s", &rlim, strerror(errno)); } else { fprintf(outfile, "max resident set size %16"F_rlim_max"%16"F_rlim_max"\n", rlim.rlim_cur, rlim.rlim_max); } #endif #ifdef RLIMIT_NPROC /* Linux, not AIX, Cygwin */ if (getrlimit(RLIMIT_NPROC, &rlim) < 0) { Warn2("getrlimit(RLIMIT_NPROC, %p): %s", &rlim, strerror(errno)); } else { fprintf(outfile, "max user processes %16"F_rlim_max"%16"F_rlim_max"\n", rlim.rlim_cur, rlim.rlim_max); } #endif #ifdef RLIMIT_NOFILE /* not AIX 4.1 */ if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { Warn2("getrlimit(RLIMIT_NOFILE, %p): %s", &rlim, strerror(errno)); } else { fprintf(outfile, "open files %16"F_rlim_max"%16"F_rlim_max"\n", rlim.rlim_cur, rlim.rlim_max); } #endif #ifdef RLIMIT_MEMLOCK /* Linux, not AIX, Cygwin */ if (getrlimit(RLIMIT_MEMLOCK, &rlim) < 0) { Warn2("getrlimit(RLIMIT_MEMLOCK, %p): %s", &rlim, strerror(errno)); } else { fprintf(outfile, "max locked-in-memory address space %16"F_rlim_max"%16"F_rlim_max"\n", rlim.rlim_cur, rlim.rlim_max); } #endif #ifdef RLIMIT_AS if (getrlimit(RLIMIT_AS, &rlim) < 0) { Warn2("getrlimit(RLIMIT_AS, %p): %s", &rlim, strerror(errno)); } else { fprintf(outfile, "virtual memory (kbytes) %16"F_rlim_max"%16"F_rlim_max"\n", rlim.rlim_cur, rlim.rlim_max); } #endif } /* file descriptors */ /* what was this for?? */ /*Sleep(1);*/ return 0; }
static int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3) { /* we expect the form: filename */ const char *name; xiosingle_t *xfd = &xxfd->stream; int pf = PF_UNIX; int socktype = SOCK_DGRAM; int protocol = 0; struct sockaddr_un us; socklen_t uslen; bool tight = true; bool needbind = true; bool opt_unlink_early = false; bool opt_unlink_close = true; if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); return STAT_NORETRY; } name = argv[1]; retropt_socket_pf(opts, &pf); retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); uslen = xiosetunix(pf, &us, name, abstract, tight); xfd->howtoend = END_NONE; retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, 1, 0, 0); if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); } if (!(ABSTRACT && abstract)) { if (opt_unlink_early) { if (Unlink(name) < 0) { if (errno == ENOENT) { Warn2("unlink(\"%s\"): %s", name, strerror(errno)); } else { Error2("unlink(\"%s\"): %s", name, strerror(errno)); } } } else { struct stat buf; if (Lstat(name, &buf) == 0) { Error1("\"%s\" exists", name); return STAT_RETRYLATER; } } if (opt_unlink_close) { if ((xfd->unlink_close = strdup(name)) == NULL) { Error1("strdup(\"%s\"): out of memory", name); } xfd->opt_unlink_close = true; } /* trying to set user-early, perm-early etc. here is useless because file system entry is available only past bind() call. */ } applyopts_named(name, opts, PH_EARLY); /* umask! */ xfd->para.socket.la.soa.sa_family = pf; xfd->dtype = XIODATA_RECVFROM_ONE; /* this may fork */ return _xioopen_dgram_recvfrom(xfd, xioflags, needbind?(struct sockaddr *)&us:NULL, uslen, opts, pf, socktype, protocol, E_ERROR); }
/* the current socat/xio implementation knows two kinds of children: exec/system addresses perform a fork: their children are registered and their death's influence the parents' flow; listen-socket with fork children: these children are "anonymous" and their death does not affect the parent process (now; maybe we have a child process counter later) */ void childdied(int signum #if HAVE_SIGACTION , siginfo_t *siginfo, void *context #endif /* HAVE_SIGACTION */ ) { pid_t pid; int _errno; int status = 0; bool wassig = false; int i; struct _xiosigchld_child *entry; _errno = errno; /* save current value; e.g., select() on Cygwin seems to set it to EINTR _before_ handling the signal, and then passes the value left by the signal handler to the caller of select(), accept() etc. */ /* is not thread/signal save, but confused messages in rare cases are better than no messages at all */ Info1("childdied(signum=%d)", signum); do { pid = Waitpid(-1, &status, WNOHANG); if (pid == 0) { Msg(wassig?E_INFO:E_WARN, "waitpid(-1, {}, WNOHANG): no child has exited"); Info("childdied() finished"); errno = _errno; return; } else if (pid < 0 && errno == ECHILD) { Msg1(wassig?E_INFO:E_WARN, "waitpid(-1, {}, WNOHANG): %s", strerror(errno)); Info("childdied() finished"); errno = _errno; return; } wassig = true; if (pid < 0) { Warn2("waitpid(-1, {%d}, WNOHANG): %s", status, strerror(errno)); Info("childdied() finished"); errno = _errno; return; } #if 0 /*! indent */ /* check if it was a registered child process */ i = 0; while (i < XIO_MAXSOCK) { if (xio_checkchild(sock[i], i, pid)) break; ++i; } if (i == XIO_MAXSOCK) { Info2("childdied(%d): cannot identify child %d", signum, pid); if (diedunknown1 == 0) { diedunknown1 = pid; Debug("saving pid in diedunknown1"); } else if (diedunknown2 == 0) { diedunknown2 = pid; Debug("saving pid in diedunknown2"); } else if (diedunknown3 == 0) { diedunknown3 = pid; Debug("saving pid in diedunknown3"); } else { diedunknown4 = pid; Debug("saving pid in diedunknown4"); } } #else entry = _xiosigchld_find(pid); if (entry == NULL) { Info("dead child "F_pid" died unknown"); } else { (*entry->sigaction)(signum, siginfo, entry->context); xiosigchld_unregister(pid); } #endif if (WIFEXITED(status)) { if (WEXITSTATUS(status) == 0) { Info2("waitpid(): child %d exited with status %d", pid, WEXITSTATUS(status)); } else { Warn2("waitpid(): child %d exited with status %d", pid, WEXITSTATUS(status)); } } else if (WIFSIGNALED(status)) { Info2("waitpid(): child %d exited on signal %d", pid, WTERMSIG(status)); } else if (WIFSTOPPED(status)) { Info2("waitpid(): child %d stopped on signal %d", pid, WSTOPSIG(status)); } else { Warn1("waitpid(): cannot determine status of child %d", pid); } #if !HAVE_SIGACTION /* we might need to re-register our handler */ if (Signal(SIGCHLD, childdied) == SIG_ERR) { Warn2("signal(SIGCHLD, %p): %s", childdied, strerror(errno)); } #endif /* !HAVE_SIGACTION */ } while (1); Info("childdied() finished"); errno = _errno; }
/* analyze a file descriptor */ int filan_fd(int fd, FILE *outfile) { #if HAVE_STAT64 struct stat64 buf = {0}; #else struct stat buf = {0}; #endif /* !HAVE_STAT64 */ int result; Debug1("checking file descriptor %u", fd); #if HAVE_STAT64 result = Fstat64(fd, &buf); #else result = Fstat(fd, &buf); #endif /* !HAVE_STAT64 */ if (result < 0) { if (errno == EBADF) { Debug2("fstat(%d): %s", fd, strerror(errno)); } else { Warn2("fstat(%d): %s", fd, strerror(errno)); } return -1; } Debug2("fd %d is a %s", fd, getfiletypestring(buf.st_mode)); result = filan_stat(&buf, fd, fd, outfile); if (result >= 0) { /* even more dynamic info */ { /* see if data is available */ struct pollfd ufds; ufds.fd = fd; ufds.events = POLLIN|POLLPRI|POLLOUT #ifdef POLLRDNORM |POLLRDNORM #endif #ifdef POLLRDBAND |POLLRDBAND #endif |POLLWRNORM #ifdef POLLWRBAND |POLLWRBAND #endif #ifdef POLLMSG |POLLMSG #endif ; if (Poll(&ufds, 1, 0) < 0) { Warn4("poll({%d, %hd, %hd}, 1, 0): %s", ufds.fd, ufds.events, ufds.revents, strerror(errno)); } else { fputs("poll: ", outfile); if (ufds.revents & POLLIN) fputs("IN,", outfile); if (ufds.revents & POLLPRI) fputs("PRI,", outfile); if (ufds.revents & POLLOUT) fputs("OUT,", outfile); if (ufds.revents & POLLERR) fputs("ERR,", outfile); if (ufds.revents & POLLNVAL) fputs("NVAL,", outfile); #ifdef FIONREAD if (ufds.revents & POLLIN) { size_t sizet; if ((result = Ioctl(fd, FIONREAD, &sizet) >= 0)) { fprintf (outfile, "; FIONREAD="F_Zu, sizet); } } #endif /* defined(FIONREAD) */ #if _WITH_SOCKET && defined(MSG_DONTWAIT) if ((ufds.revents & POLLIN) && isasocket(fd)) { char _peername[SOCKADDR_MAX]; struct sockaddr *pa = (struct sockaddr *)_peername; struct msghdr msgh = {0}; char peekbuff[1]; /* [0] fails with some compilers */ #if HAVE_STRUCT_IOVEC struct iovec iovec; #endif char ctrlbuff[5120]; ssize_t bytes; fputs("; ", outfile); msgh.msg_name = pa; msgh.msg_namelen = sizeof(*pa); #if HAVE_STRUCT_IOVEC iovec.iov_base = peekbuff; iovec.iov_len = sizeof(peekbuff); msgh.msg_iov = &iovec; msgh.msg_iovlen = 1; #endif #if HAVE_STRUCT_MSGHDR_MSGCONTROL msgh.msg_control = ctrlbuff; #endif #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN msgh.msg_controllen = sizeof(ctrlbuff); #endif #if HAVE_STRUCT_MSGHDR_MSGFLAGS msgh.msg_flags = 0; #endif if ((bytes = Recvmsg(fd, &msgh, MSG_PEEK|MSG_DONTWAIT)) < 0) { Warn1("recvmsg(): %s", strerror(errno)); } else { fprintf(outfile, "recvmsg="F_Zd", ", bytes); } } #endif /* _WITH_SOCKET && defined(MSG_DONTWAIT) */ } } } fputc('\n', outfile); return 0; }
/* 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_foxec(): 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; } retropt_int(popts, OPT_LEFTINFD, &fdi); retropt_int(popts, OPT_LEFTOUTFD, &fdo); 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) { if (Signal(SIGCHLD, SIG_IGN) == SIG_ERR) { Warn1("signal(SIGCHLD, SIG_IGN): %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 */ }
/* process a bidirectional "stdio" or "-" argument with options. */ int xioopen_stdio_bi(xiofile_t *sock) { struct opt *opts1, *opts2, *optspr; unsigned int groups1 = xioaddr_stdio0.groups, groups2 = xioaddr_stdio0.groups; int result; sock->stream.rfd = 0 /*stdin*/; sock->stream.wfd = 1 /*stdout*/; #if WITH_TERMIOS if (Isatty(sock->stream.rfd)) { if (Tcgetattr(sock->stream.rfd, &sock->stream.savetty) < 0) { Warn2("cannot query current terminal settings on fd %d: %s", sock->stream.rfd, strerror(errno)); } else { sock->stream.ttyvalid = true; } } if (Isatty(sock->stream.wfd) && (sock->stream.wfd != sock->stream.rfd)) { if (Tcgetattr(sock->stream.wfd, &sock->stream.savetty) < 0) { Warn2("cannot query current terminal settings on fd %d: %s", sock->stream.wfd, strerror(errno)); } else { sock->stream.ttyvalid = true; } } #endif /* WITH_TERMIOS */ if (applyopts_single(&sock->stream, sock->stream.opts, PH_INIT) < 0) return -1; applyopts(-1, sock->stream.opts, PH_INIT); if (sock->stream.howtoshut == XIOSHUT_UNSPEC) sock->stream.howtoshut = XIOSHUT_NONE; /* options here are one-time and one-direction, no second use */ retropt_bool(sock->stream.opts, OPT_IGNOREEOF, &sock->stream.ignoreeof); /* extract opts that should be applied only once */ if ((optspr = copyopts(sock->stream.opts, GROUP_PROCESS)) == NULL) { return -1; } if ((result = applyopts(-1, optspr, PH_EARLY)) < 0) return result; if ((result = applyopts(-1, optspr, PH_PREOPEN)) < 0) return result; /* here we copy opts, because most have to be applied twice! */ if ((opts1 = copyopts(sock->stream.opts, GROUP_FD|GROUP_APPL|(groups1&~GROUP_PROCESS))) == NULL) { return -1; } /* apply options to first FD */ if ((result = applyopts(sock->stream.rfd, opts1, PH_ALL)) < 0) { return result; } if ((result = _xio_openlate(&sock->stream, opts1)) < 0) { return result; } if ((opts2 = copyopts(sock->stream.opts, GROUP_FD|GROUP_APPL|(groups2&~GROUP_PROCESS))) == NULL) { return -1; } /* apply options to second FD */ if ((result = applyopts(sock->stream.wfd, opts2, PH_ALL)) < 0) { return result; } if ((result = _xio_openlate(&sock->stream, opts2)) < 0) { return result; } if ((result = _xio_openlate(&sock->stream, optspr)) < 0) { return result; } Notice("reading from and writing to stdio"); return 0; }
/* applies and consumes the following options: PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE OFUNC_OFFSET OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC */ static int _xioopen_rawip_sendto(const char *hostname, const char *protname, struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int *pf) { char *garbage; xiosingle_t *xfd = &xxfd->stream; int rw = (xioflags&XIO_ACCMODE); union sockaddr_union us; socklen_t uslen; int feats = 1; /* option bind supports only address, not port */ int socktype = SOCK_RAW; int ipproto; bool needbind = false; int result; if ((ipproto = strtoul(protname, &garbage, 0)) >= 256) { Error3("xioopen_rawip_sendto(\"%s:%s\",,): protocol number exceeds 255 (%u)", hostname, protname, ipproto); return STAT_NORETRY; } else if (*garbage) { Warn2("xioopen_rawip_sendto(\"%s:%s\",,): trailing garbage in protocol specification", hostname, protname); /*return STAT_NORETRY;*/ } retropt_int(opts, OPT_PROTOCOL_FAMILY, pf); /* ...res_opts[] */ if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); xfd->salen = sizeof(xfd->peersa); if ((result = xiogetaddrinfo(hostname, NULL, *pf, socktype, ipproto, &xfd->peersa, &xfd->salen, xfd->para.socket.ip.res_opts[0], xfd->para.socket.ip.res_opts[1])) != STAT_OK) { return result; } if (*pf == PF_UNSPEC) { *pf = xfd->peersa.soa.sa_family; } uslen = socket_init(*pf, &us); xfd->dtype = XIODATA_RECVFROM_SKIPIP; if (retropt_bind(opts, *pf, socktype, ipproto, &us.soa, &uslen, feats, xfd->para.socket.ip.res_opts[0], xfd->para.socket.ip.res_opts[1]) != STAT_NOACTION) { needbind = true; } if ((result = _xioopen_dgram_sendto(needbind?&us:NULL, uslen, opts, xioflags, xfd, groups, *pf, socktype, ipproto)) != STAT_OK) { return result; } if (XIOWITHWR(rw)) xfd->wfd = xfd->rfd; if (!XIOWITHRD(rw)) xfd->rfd = -1; return STAT_OK; }
static int xioopen_pty(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) { /* we expect the form: filename */ int ptyfd = -1, ttyfd = -1; #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 */ char ptyname[MAXPTYNAMELEN]; char *tn = NULL; char *linkname = NULL; bool opt_unlink_close = true; /* remove symlink afterwards */ bool wait_slave = false; /* true would be better for many platforms, but some OSes cannot handle this, and for common default behaviour as well as backward compatibility we choose "no" as default */ struct timespec pollintv = { PTY_INTERVALL }; if (argc != 1) { Error2("%s: wrong number of parameters (%d instead of 0)", argv[0], argc-1); } xfd->stream.howtoend = END_CLOSE; if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); /* trying to set user-early, perm-early etc. here might be useless because file system entry is eventually available only past pty creation */ /* name not yet known; umask should not be handled with this function! */ /* umask does not affect resulting mode, on Linux 2.4 */ applyopts_named("", opts, PH_EARLY); /* umask! */ #if defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC) retropt_bool(opts, OPT_PTMX, &useptmx); #endif #if HAVE_OPENPTY retropt_bool(opts, OPT_OPENPTY, &useopenpty); #endif #if (defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)) # if HAVE_OPENPTY useopenpty = !useptmx; # else /* !HAVE_OPENPTY */ useptmx = true; # endif /* !HAVE_OPENPTY */ #else # if HAVE_OPENPTY useopenpty = true; # endif /* HAVE_OPENPTY */ #endif /* ! (defined(HAVE_DEV_PTMX) || defined(HAVE_DEV_PTC)) */ #if HAVE_POLL retropt_bool(opts, OPT_PTY_WAIT_SLAVE, &wait_slave); retropt_timespec(opts, OPT_PTY_INTERVALL, &pollintv); #endif /* HAVE_POLL */ if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0) return -1; applyopts2(-1, opts, PH_INIT, PH_EARLY); applyopts(-1, opts, PH_PREBIGEN); #if defined(HAVE_DEV_PTMX) # define PTMX "/dev/ptmx" /* Linux */ #elif HAVE_DEV_PTC # define PTMX "/dev/ptc" /* AIX 4.3.3 */ #endif #if HAVE_DEV_PTMX || HAVE_DEV_PTC if (useptmx) { if ((ptyfd = Open(PTMX, O_RDWR|O_NOCTTY, 0620)) < 0) { Warn1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620): %s", strerror(errno)); /*!*/ } else { ;/*0 Info1("open(\""PTMX"\", O_RDWR|O_NOCTTY, 0620) -> %d", ptyfd);*/ } if (ptyfd >= 0 && ttyfd < 0) { /* we used PTMX before forking */ /*0 extern char *ptsname(int);*/ #if HAVE_GRANTPT /* AIX, not Linux */ if (Grantpt(ptyfd)/*!*/ < 0) { Warn2("grantpt(%d): %s", ptyfd, strerror(errno)); } #endif /* HAVE_GRANTPT */ #if HAVE_UNLOCKPT if (Unlockpt(ptyfd)/*!*/ < 0) { Warn2("unlockpt(%d): %s", ptyfd, strerror(errno)); } #endif /* HAVE_UNLOCKPT */ #if HAVE_PROTOTYPE_LIB_ptsname /* AIX, not Linux */ if ((tn = Ptsname(ptyfd)) == NULL) { Warn2("ptsname(%d): %s", ptyfd, strerror(errno)); } else { Notice1("PTY is %s", tn); } #endif /* HAVE_PROTOTYPE_LIB_ptsname */ if (tn == NULL) { if ((tn = Ttyname(ptyfd)) == NULL) { Warn2("ttyname(%d): %s", ptyfd, strerror(errno)); } } ptyname[0] = '\0'; strncat(ptyname, tn, MAXPTYNAMELEN-1); } } #endif /* HAVE_DEV_PTMX || HAVE_DEV_PTC */ #if HAVE_OPENPTY if (ptyfd < 0) { int result; if ((result = Openpty(&ptyfd, &ttyfd, ptyname, NULL, NULL)) < 0) { Error4("openpty(%p, %p, %p, NULL, NULL): %s", &ptyfd, &ttyfd, ptyname, strerror(errno)); return -1; } Notice1("PTY is %s", ptyname); } #endif /* HAVE_OPENPTY */ if (!retropt_string(opts, OPT_SYMBOLIC_LINK, &linkname)) { if (Unlink(linkname) < 0 && errno != ENOENT) { Error2("unlink(\"%s\"): %s", linkname, strerror(errno)); } if (Symlink(ptyname, linkname) < 0) { Error3("symlink(\"%s\", \"%s\"): %s", ptyname, linkname, strerror(errno)); } if (opt_unlink_close) { if ((xfd->stream.unlink_close = strdup(linkname)) == NULL) { Error1("strdup(\"%s\"): out of memory", linkname); } xfd->stream.opt_unlink_close = true; } } applyopts_named(ptyname, opts, PH_PASTOPEN); applyopts_named(ptyname, opts, PH_FD); applyopts_cloexec(ptyfd, opts);/*!*/ xfd->stream.dtype = XIODATA_PTY; applyopts(ptyfd, opts, PH_FD); { /* special handling of user-late etc.; with standard behaviour (up to 1.7.1.1) they affected /dev/ptmx instead of /dev/pts/N */ uid_t uid = -1, gid = -1; mode_t perm; bool dont; dont = retropt_uid(opts, OPT_USER_LATE, &uid); dont &= retropt_gid(opts, OPT_GROUP_LATE, &gid); if (!dont) { if (Chown(ptyname, uid, gid) < 0) { Error4("chown(\"%s\", %d, %d): %s", ptyname, uid, gid, strerror(errno)); } } if (retropt_mode(opts, OPT_PERM_LATE, &perm) == 0) { if (Chmod(ptyname, perm) < 0) { Error3("chmod(\"%s\", %03o): %s", ptyname, perm, strerror(errno)); } } } xfd->stream.fd = ptyfd; applyopts(ptyfd, opts, PH_LATE); if (applyopts_single(&xfd->stream, opts, PH_LATE) < 0) return -1; #if HAVE_POLL /* if you can and wish: */ if (wait_slave) { /* try to wait until someone opens the slave side of the pty */ /* we want to get a HUP (hangup) condition on the pty */ #if HAVE_DEV_PTMX || HAVE_DEV_PTC if (useptmx) { ttyfd = Open(tn, O_RDWR|O_NOCTTY, 0620); Close(ttyfd); } #endif #if HAVE_OPENPTY if (useopenpty) { Close(ttyfd); } #endif /* HAVE_OPENPTY */ /* now we poll until the HUP vanishes - this indicates a slave conn. */ while (true) { struct pollfd ufd; ufd.fd = ptyfd; ufd.events = (POLLHUP); if (Poll(&ufd, 1, 0) < 0) { Error3("poll({%d, 0x%04hu,}, 1, 0): %s", ufd.fd, ufd.events, strerror(errno)); /*! close something */ return -1; } if (!(ufd.revents & POLLHUP)) { break; } Nanosleep(&pollintv, NULL); continue; } } #endif /* HAVE_POLL */ return STAT_OK; }
/* open a named or unnamed pipe/fifo */ static int xioopen_fifo(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int dummy1, int dummy2, int dummy3) { const char *pipename = argv[1]; int rw = (xioflags & XIO_ACCMODE); #if HAVE_STAT64 struct stat64 pipstat; #else struct stat pipstat; #endif /* !HAVE_STAT64 */ bool opt_unlink_early = false; bool opt_unlink_close = true; mode_t mode = 0666; int result; if (argc == 1) { return xioopen_fifo_unnamed(fd, fd->stream.opts); } if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); } if (applyopts_single(&fd->stream, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early); applyopts_named(pipename, opts, PH_EARLY); /* umask! */ applyopts(-1, opts, PH_EARLY); if (opt_unlink_early) { if (Unlink(pipename) < 0) { if (errno == ENOENT) { Warn2("unlink(%s): %s", pipename, strerror(errno)); } else { Error2("unlink(%s): %s", pipename, strerror(errno)); return STAT_RETRYLATER; } } } retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); retropt_modet(opts, OPT_PERM, &mode); if (applyopts_named(pipename, opts, PH_EARLY) < 0) { return STAT_RETRYLATER; } if (applyopts_named(pipename, opts, PH_PREOPEN) < 0) { return STAT_RETRYLATER; } if ( #if HAVE_STAT64 Stat64(pipename, &pipstat) < 0 #else Stat(pipename, &pipstat) < 0 #endif /* !HAVE_STAT64 */ ) { if (errno != ENOENT) { Error3("stat(\"%s\", %p): %s", pipename, &pipstat, strerror(errno)); } else { Debug1("xioopen_fifo(\"%s\"): does not exist, creating fifo", pipename); #if 0 result = Mknod(pipename, S_IFIFO|mode, 0); if (result < 0) { Error3("mknod(%s, %d, 0): %s", pipename, mode, strerror(errno)); return STAT_RETRYLATER; } #else result = Mkfifo(pipename, mode); if (result < 0) { Error3("mkfifo(%s, %d): %s", pipename, mode, strerror(errno)); return STAT_RETRYLATER; } #endif Notice2("created named pipe \"%s\" for %s", pipename, ddirection[rw]); applyopts_named(pipename, opts, PH_ALL); } if (opt_unlink_close) { if ((fd->stream.unlink_close = strdup(pipename)) == NULL) { Error1("strdup(\"%s\"): out of memory", pipename); } fd->stream.opt_unlink_close = true; } } else { /* exists */ Debug1("xioopen_fifo(\"%s\"): already exist, opening it", pipename); Notice3("opening %s \"%s\" for %s", filetypenames[(pipstat.st_mode&S_IFMT)>>12], pipename, ddirection[rw]); /*applyopts_early(pipename, opts);*/ applyopts_named(pipename, opts, PH_EARLY); } if ((result = _xioopen_open(pipename, rw, opts)) < 0) { return result; } fd->stream.fd = result; applyopts_named(pipename, opts, PH_FD); applyopts(fd->stream.fd, opts, PH_FD); applyopts_cloexec(fd->stream.fd, opts); return _xio_openlate(&fd->stream, opts); }
static int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int abstract, int dummy2, int dummy3) { /* we expect the form: filename */ const char *name; xiosingle_t *xfd = &xxfd->stream; int pf = PF_UNIX; int socktype = SOCK_DGRAM; int protocol = 0; union sockaddr_union us; socklen_t uslen; bool opt_unlink_early = false; bool opt_unlink_close = true; int result; if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); return STAT_NORETRY; } name = argv[1]; xfd->para.socket.un.tight = true; retropt_socket_pf(opts, &pf); xfd->howtoend = END_SHUTDOWN; if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY; applyopts(-1, opts, PH_INIT); applyopts_named(name, opts, PH_EARLY); /* umask! */ applyopts_offset(xfd, opts); if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); } applyopts(-1, opts, PH_EARLY); uslen = xiosetunix(pf, &us.un, name, abstract, xfd->para.socket.un.tight); #if 0 if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, (abstract<<1)|xfd->para.socket.un.tight, 0, 0) == STAT_OK) { } #endif if (!(ABSTRACT && abstract)) { if (opt_unlink_early) { if (Unlink(name) < 0) { if (errno == ENOENT) { Warn2("unlink(\"%s\"): %s", name, strerror(errno)); } else { Error2("unlink(\"%s\"): %s", name, strerror(errno)); } } } else { struct stat buf; if (Lstat(name, &buf) == 0) { Error1("\"%s\" exists", name); return STAT_RETRYLATER; } } if (opt_unlink_close) { if ((xfd->unlink_close = strdup(name)) == NULL) { Error1("strdup(\"%s\"): out of memory", name); } xfd->opt_unlink_close = true; } } applyopts_named(name, opts, PH_EARLY); /* umask! */ xfd->para.socket.la.soa.sa_family = pf; xfd->dtype = XIODATA_RECV; result = _xioopen_dgram_recv(xfd, xioflags, &us.soa, uslen, opts, pf, socktype, protocol, E_ERROR); return result; }