/* perform actions that are common to all NAMED group addresses: checking if the entry exists, parsing options, ev.removing old filesystem entry or setting early owners and permissions. It applies options of PH_EARLY and PH_PREOPEN. If the path exists, its st_mode field is returned. After this sub you may proceed with open() or whatever... */ int _xioopen_named_early(int argc, const char *argv[], xiofile_t *xfd, int groups, bool *exists, struct opt *opts) { const char *path = argv[1]; unsigned int iogroups = 0; #if HAVE_STAT64 struct stat64 statbuf; #else struct stat statbuf; #endif /* !HAVE_STAT64 */ bool opt_unlink_early = false; if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", argv[0]?argv[0]:"<named>", argc); } statbuf.st_mode = 0; /* find the appropriate groupbits */ if ( #if HAVE_STAT64 Stat64(path, &statbuf) < 0 #else Stat(path, &statbuf) < 0 #endif /* !HAVE_STAT64 */ ) { if (errno != ENOENT) { Error2("stat(\"%s\"): %s", path, strerror(errno)); return STAT_RETRYLATER; } iogroups = GROUP_REG; *exists = false; } else { iogroups = _groupbits(statbuf.st_mode); *exists = true; } if (applyopts_single(&xfd->stream, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early); if (*exists && opt_unlink_early) { Info1("\"%s\" already exists; removing it", path); if (Unlink(path) < 0) { Error2("unlink(\"%s\"): %s", path, strerror(errno)); *exists = true; } else { *exists = false; } } applyopts(-1, opts, PH_EARLY); applyopts_named(path, opts, PH_EARLY); if (*exists) { applyopts_named(path, opts, PH_PREOPEN); } else { dropopts(opts, PH_PREOPEN); } return statbuf.st_mode; }
/* retrieve the OPEN group options and perform the open() call. returns the file descriptor or a negative value. Applies options of phases PREOPEN, OPEN, PASTOPEN, and FD */ int _xioopen_open(const char *path, int rw, struct opt *opts) { mode_t mode = 0666; flags_t flags = rw; bool flag; int fd; applyopts_named(path, opts, PH_PREOPEN); /* this only applies pure OPEN flags, not mixed OPEN/FCNTL options */ applyopts_flags(opts, GROUP_OPEN, &flags); /* we have to handle mixed OPEN/FCNTL flags specially */ if (retropt_bool(opts, OPT_O_APPEND, &flag) >= 0 && flag) flags |= O_APPEND; if (retropt_bool(opts, OPT_O_NONBLOCK, &flag) >= 0 && flag) flags |= O_NONBLOCK; #ifdef O_ASYNC if (retropt_bool(opts, OPT_O_ASYNC, &flag) >= 0 && flag) flags |= O_ASYNC; #endif if (retropt_bool(opts, OPT_O_TRUNC, &flag) >= 0 && flag) flags |= O_TRUNC; #ifdef O_BINARY if (retropt_bool(opts, OPT_O_BINARY, &flag) >= 0 && flag) flags |= O_BINARY; #endif #ifdef O_TEXT if (retropt_bool(opts, OPT_O_TEXT, &flag) >= 0 && flag) flags |= O_TEXT; #endif #ifdef O_NOINHERIT if (retropt_bool(opts, OPT_O_NOINHERIT, &flag) >= 0 && flag) flags |= O_NOINHERIT; #endif #ifdef O_NOATIME if (retropt_bool(opts, OPT_O_NOATIME, &flag) >= 0 && flag) flags |= O_NOATIME; #endif retropt_modet(opts, OPT_PERM, &mode); if ((fd = Open(path, flags, mode)) < 0) { Error4("open(\"%s\", 0%lo, 0%03o): %s", path, flags, mode, strerror(errno)); return STAT_RETRYLATER; } /*0 Info4("open(\"%s\", 0%o, 0%03o) -> %d", path, flags, mode, fd);*/ applyopts_named(path, opts, PH_PASTOPEN); #if 0 applyopts_named(path, opts, PH_FD); applyopts(fd, opts, PH_FD); applyopts_cloexec(fd, opts); #endif return fd; }
/* 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_creat(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) { const char *filename = argv[1]; int rw = (xioflags&XIO_ACCMODE); int fd; 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, xfd, groups, &exists, opts)) < 0) { return result; } retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); if (opt_unlink_close) { if ((xfd->stream.unlink_close = strdup(filename)) == NULL) { Error1("strdup(\"%s\"): out of memory", filename); } xfd->stream.opt_unlink_close = true; } Notice2("creating regular file \"%s\" for %s", filename, ddirection[rw]); if ((fd = _xioopen_creat(filename, rw, opts)) < 0) return fd; if (XIOWITHRD(rw)) xfd->stream.rfd = fd; if (XIOWITHWR(rw)) xfd->stream.wfd = fd; applyopts_named(filename, opts, PH_PASTOPEN); if ((result = applyopts2(fd, opts, PH_PASTOPEN, PH_LATE2)) < 0) return result; applyopts_cloexec(fd, opts); applyopts_fchown(fd, opts); if ((result = _xio_openlate(&xfd->stream, opts)) < 0) return result; return 0; }
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; }
static int xioopen_tun(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int dummy1, int dummy2, int dummy3) { char *tundevice = NULL; char *tunname = NULL, *tuntype = NULL; int pf = /*! PF_UNSPEC*/ PF_INET; struct xiorange network; bool no_pi = false; const char *namedargv[] = { "tun", NULL, NULL }; int rw = (xioflags & XIO_ACCMODE); bool exists; struct ifreq ifr; int sockfd; char *ifaddr; int result; if (argc > 2 || argc < 0) { Error2("%s: wrong number of parameters (%d instead of 0 or 1)", argv[0], argc-1); } if (retropt_string(opts, OPT_TUN_DEVICE, &tundevice) != 0) { tundevice = strdup("/dev/net/tun"); } /*! socket option here? */ retropt_socket_pf(opts, &pf); namedargv[1] = tundevice; /* open the tun cloning device */ if ((result = _xioopen_named_early(2, namedargv, xfd, groups, &exists, opts)) < 0) { return result; } /*========================= the tunnel interface =========================*/ Notice("creating tunnel network interface"); if ((result = _xioopen_open(tundevice, rw, opts)) < 0) return result; xfd->stream.fd = result; /* prepare configuration of the new network interface */ memset(&ifr, 0,sizeof(ifr)); if (retropt_string(opts, OPT_TUN_NAME, &tunname) == 0) { strncpy(ifr.ifr_name, tunname, IFNAMSIZ); /* ok */ free(tunname); } else { ifr.ifr_name[0] = '\0'; } ifr.ifr_flags = IFF_TUN; if (retropt_string(opts, OPT_TUN_TYPE, &tuntype) == 0) { if (!strcmp(tuntype, "tap")) { ifr.ifr_flags = IFF_TAP; } else if (strcmp(tuntype, "tun")) { Error1("unknown tun-type \"%s\"", tuntype); } } if (retropt_bool(opts, OPT_IFF_NO_PI, &no_pi) == 0) { if (no_pi) { ifr.ifr_flags |= IFF_NO_PI; #if 0 /* not neccessary for now */ } else { ifr.ifr_flags &= ~IFF_NO_PI; #endif } } if (Ioctl(xfd->stream.fd, TUNSETIFF, &ifr) < 0) { Error3("ioctl(%d, TUNSETIFF, {\"%s\"}: %s", xfd->stream.fd, ifr.ifr_name, strerror(errno)); Close(xfd->stream.fd); } /*===================== setting interface properties =====================*/ /* we seem to need a socket for manipulating the interface */ if ((sockfd = Socket(PF_INET, SOCK_DGRAM, 0)) < 0) { Error1("socket(PF_INET, SOCK_DGRAM, 0): %s", strerror(errno)); sockfd = xfd->stream.fd; /* desparate fallback attempt */ } /*--------------------- setting interface address and netmask ------------*/ if (argc == 2) { if ((ifaddr = strdup(argv[1])) == NULL) { Error1("strdup(\"%s\"): out of memory", argv[1]); return STAT_RETRYLATER; } if ((result = xioparsenetwork(ifaddr, pf, &network)) != STAT_OK) { /*! recover */ return result; } socket_init(pf, (union sockaddr_union *)&ifr.ifr_addr); ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr = network.netaddr.ip4.sin_addr; if (Ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) { Error4("ioctl(%d, SIOCSIFADDR, {\"%s\", \"%s\"}: %s", sockfd, ifr.ifr_name, ifaddr, strerror(errno)); } ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr = network.netmask.ip4.sin_addr; if (Ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) { Error4("ioctl(%d, SIOCSIFNETMASK, {\"0x%08u\", \"%s\"}, %s", sockfd, ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr, ifaddr, strerror(errno)); } free(ifaddr); } /*--------------------- setting interface flags --------------------------*/ applyopts_single(&xfd->stream, opts, PH_FD); if (Ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { Error3("ioctl(%d, SIOCGIFFLAGS, {\"%s\"}: %s", sockfd, ifr.ifr_name, strerror(errno)); } Debug2("\"%s\": system set flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags); ifr.ifr_flags |= xfd->stream.para.tun.iff_opts[0]; ifr.ifr_flags &= ~xfd->stream.para.tun.iff_opts[1]; Debug2("\"%s\": xio merged flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags); if (Ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { Error4("ioctl(%d, SIOCSIFFLAGS, {\"%s\", %hd}: %s", sockfd, ifr.ifr_name, ifr.ifr_flags, strerror(errno)); } ifr.ifr_flags = 0; if (Ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) { Error3("ioctl(%d, SIOCGIFFLAGS, {\"%s\"}: %s", sockfd, ifr.ifr_name, strerror(errno)); } Debug2("\"%s\": resulting flags: 0x%hx", ifr.ifr_name, ifr.ifr_flags); #if LATER applyopts_named(tundevice, opts, PH_FD); #endif applyopts(xfd->stream.fd, opts, PH_FD); applyopts_cloexec(xfd->stream.fd, opts); applyopts_fchown(xfd->stream.fd, opts); if ((result = _xio_openlate(&xfd->stream, opts)) < 0) return result; return 0; }
/* creates the listening socket, bind, applies options; waits for incoming connection, checks its source address and port. Depending on fork option, it may fork a subprocess. pf specifies the syntax expected for range option. In the case of generic socket it is 0 (expecting raw binary data), and the real pf can be obtained from us->af_family; for other socket types pf == us->af_family Returns 0 if a connection was accepted; with fork option, this is always in a subprocess! Other return values indicate a problem; this can happen in the master process or in a subprocess. This function does not retry. If you need retries, handle this in a loop in the calling function (and always provide the options...) After fork, we set the forever/retry of the child process to 0 applies and consumes the following option: PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY, PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap, OPT_SOURCEPORT, OPT_LOWPORT, cloexec */ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen, struct opt *opts, int pf, int socktype, int proto, int level) { struct sockaddr sa; socklen_t salen; int backlog = 5; /* why? 1 seems to cause problems under some load */ char *rangename; bool dofork = false; int maxchildren = 0; char infobuff[256]; char lisname[256]; union sockaddr_union _peername; union sockaddr_union _sockname; union sockaddr_union *pa = &_peername; /* peer address */ union sockaddr_union *la = &_sockname; /* local address */ socklen_t pas = sizeof(_peername); /* peer address size */ socklen_t las = sizeof(_sockname); /* local address size */ int result; retropt_bool(opts, OPT_FORK, &dofork); if (dofork) { if (!(xioflags & XIO_MAYFORK)) { Error("option fork not allowed here"); return STAT_NORETRY; } xfd->flags |= XIO_DOESFORK; } retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren); if (! dofork && maxchildren) { Error("option max-children not allowed without option fork"); return STAT_NORETRY; } if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; if (dofork) { xiosetchilddied(); /* set SIGCHLD handler */ } if ((xfd->fd = xiosocket(opts, us->sa_family, socktype, proto, level)) < 0) { return STAT_RETRYLATER; } applyopts_cloexec(xfd->fd, opts); applyopts(xfd->fd, opts, PH_PREBIND); applyopts(xfd->fd, opts, PH_BIND); if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) { Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd, sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen, strerror(errno)); Close(xfd->fd); return STAT_RETRYLATER; } #if WITH_UNIX if (us->sa_family == AF_UNIX) { applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD); } #endif /* under some circumstances (e.g., TCP listen on port 0) bind() fills empty fields that we want to know. */ salen = sizeof(sa); if (Getsockname(xfd->fd, us, &uslen) < 0) { Warn4("getsockname(%d, %p, {%d}): %s", xfd->fd, &us, uslen, strerror(errno)); } applyopts(xfd->fd, opts, PH_PASTBIND); #if WITH_UNIX if (us->sa_family == AF_UNIX) { /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/ applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY); applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN); } #endif /* WITH_UNIX */ #if WITH_IP4 /*|| WITH_IP6*/ if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { if (xioparserange(rangename, pf, &xfd->para.socket.range) < 0) { free(rangename); return STAT_NORETRY; } free(rangename); xfd->para.socket.dorange = true; } #endif #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP xio_retropt_tcpwrap(xfd, opts); #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */ #if WITH_TCP || WITH_UDP if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->para.socket.ip.sourceport) >= 0) { xfd->para.socket.ip.dosourceport = true; } retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport); #endif /* WITH_TCP || WITH_UDP */ applyopts(xfd->fd, opts, PH_PRELISTEN); retropt_int(opts, OPT_BACKLOG, &backlog); if (Listen(xfd->fd, backlog) < 0) { Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno)); return STAT_RETRYLATER; } if (xioopts.logopt == 'm') { Info("starting accept loop, switching to syslog"); diag_set('y', xioopts.syslogfac); xioopts.logopt = 'y'; } else { Info("starting accept loop"); } while (true) { /* but we only loop if fork option is set */ char peername[256]; char sockname[256]; int ps; /* peer socket */ pa = &_peername; la = &_sockname; salen = sizeof(struct sockaddr); do { /*? int level = E_ERROR;*/ Notice1("listening on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname))); ps = Accept(xfd->fd, (struct sockaddr *)&sa, &salen); if (ps >= 0) { /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", xfd->fd, &sa, salen, ps);*/ break; /* success, break out of loop */ } if (errno == EINTR) { continue; } if (errno == ECONNABORTED) { Notice4("accept(%d, %p, {"F_socklen"}): %s", xfd->fd, &sa, salen, strerror(errno)); continue; } Msg4(level, "accept(%d, %p, {"F_socklen"}): %s", xfd->fd, &sa, salen, strerror(errno)); Close(xfd->fd); return STAT_RETRYLATER; } while (true); applyopts_cloexec(ps, opts); if (Getpeername(ps, &pa->soa, &pas) < 0) { Warn4("getpeername(%d, %p, {"F_socklen"}): %s", ps, pa, pas, strerror(errno)); pa = NULL; } if (Getsockname(ps, &la->soa, &las) < 0) { Warn4("getsockname(%d, %p, {"F_socklen"}): %s", ps, la, las, strerror(errno)); la = NULL; } Notice2("accepting connection from %s on %s", pa? sockaddr_info(&pa->soa, pas, peername, sizeof(peername)):"NULL", la? sockaddr_info(&la->soa, las, sockname, sizeof(sockname)):"NULL"); if (pa != NULL && la != NULL && xiocheckpeer(xfd, pa, la) < 0) { if (Shutdown(ps, 2) < 0) { Info2("shutdown(%d, 2): %s", ps, strerror(errno)); } Close(ps); continue; } if (pa != NULL) Info1("permitting connection from %s", sockaddr_info((struct sockaddr *)pa, pas, infobuff, sizeof(infobuff))); if (dofork) { pid_t pid; /* mostly int; only used with fork */ sigset_t mask_sigchld; /* we must prevent that the current packet triggers another fork; therefore we wait for a signal from the recent child: USR1 indicates that is has consumed the last packet; CHLD means it has terminated */ /* block SIGCHLD and SIGUSR1 until parent is ready to react */ sigemptyset(&mask_sigchld); sigaddset(&mask_sigchld, SIGCHLD); Sigprocmask(SIG_BLOCK, &mask_sigchld, NULL); if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN)) < 0) { Close(xfd->fd); Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); return STAT_RETRYLATER; } if (pid == 0) { /* child */ pid_t cpid = Getpid(); Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); Info1("just born: child process "F_pid, cpid); xiosetenvulong("PID", cpid, 1); if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } xfd->fd = ps; #if WITH_RETRY /* !? */ xfd->forever = false; xfd->retry = 0; level = E_ERROR; #endif /* WITH_RETRY */ break; } /* server: continue loop with listen */ /* shutdown() closes the socket even for the child process, but close() does what we want */ if (Close(ps) < 0) { Info2("close(%d): %s", ps, strerror(errno)); } /* now we are ready to handle signals */ Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL); while (maxchildren) { if (num_child < maxchildren) break; Notice("maxchildren are active, waiting"); /* UINT_MAX would even be nicer, but Openindiana works only with 31 bits */ while (!Sleep(INT_MAX)) ; /* any signal lets us continue */ } Info("still listening"); } else { if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } xfd->fd = ps; break; } } applyopts(xfd->fd, opts, PH_FD); applyopts(xfd->fd, opts, PH_PASTSOCKET); applyopts(xfd->fd, opts, PH_CONNECTED); if ((result = _xio_openlate(xfd, opts)) < 0) return result; /* set the env vars describing the local and remote sockets */ if (la != NULL) xiosetsockaddrenv("SOCK", la, las, proto); if (pa != NULL) xiosetsockaddrenv("PEER", pa, pas, proto); return 0; }
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); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); 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; }
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); }
/* 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_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; }