/* xiosocketpair.c */ /* Copyright Gerhard Rieger */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this is the source of the internal xiosocketpair function */ #include "xiosysincludes.h" #include "sycls.h" #include "compat.h" #include "error.h" #include "xio.h" #if defined(HAVE_DEV_PTMX) # define PTMX "/dev/ptmx" /* Linux */ #elif HAVE_DEV_PTC # define PTMX "/dev/ptc" /* AIX */ #endif #define MAXPTYNAMELEN 64 int xiopty(int useptmx, int *ttyfdp, int *ptyfdp) { int ttyfd, ptyfd = -1; char ptyname[MAXPTYNAMELEN]; struct termios termarg; 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) { char *tn = NULL; /* 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_PTSNAME /* AIX, not Linux */ if ((tn = Ptsname(ptyfd)) == NULL) { Warn2("ptsname(%d): %s", ptyfd, strerror(errno)); } else { Notice1("PTY is %s", tn); } #endif /* HAVE_PTSNAME */ #if 0 if (tn == NULL) { /*! ttyname_r() */ if ((tn = Ttyname(ptyfd)) == NULL) { Warn2("ttyname(%d): %s", ptyfd, strerror(errno)); } } ptyname[0] = '\0'; strncat(ptyname, tn, MAXPTYNAMELEN-1); #endif if ((ttyfd = Open(tn, O_RDWR|O_NOCTTY, 0620)) < 0) { Warn2("open(\"%s\", O_RDWR|O_NOCTTY, 0620): %s", tn, strerror(errno)); } else { /*0 Info2("open(\"%s\", O_RDWR|O_NOCTTY, 0620) -> %d", tn, ttyfd);*/ } #ifdef I_PUSH /* Linux: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> -1 EINVAL */ /* AIX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 1 */ /* SunOS: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */ /* HP-UX: I_PUSH def'd; pty: ioctl(, I_FIND, ...) -> 0 */ if (Ioctl(ttyfd, I_FIND, "ldterm") == 0) { Ioctl(ttyfd, I_PUSH, "ptem"); /* 0 */ Ioctl(ttyfd, I_PUSH, "ldterm"); /* 0 */ Ioctl(ttyfd, I_PUSH, "ttcompat"); /* HP-UX: -1 */ } #endif } } #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 (Tcgetattr(ttyfd, &termarg) < 0) { Error3("tcgetattr(%d, %p): %s", ttyfd, &termarg, strerror(errno)); } #if 0 cfmakeraw(&termarg); #else /*!!! share code with xioopts.c raw,echo=0 */ termarg.c_iflag &= ~(IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF #ifdef IUCLC |IUCLC #endif |IXANY|IMAXBEL); termarg.c_iflag |= (0); termarg.c_oflag &= ~(OPOST); termarg.c_oflag |= (0); termarg.c_cflag &= ~(0); termarg.c_cflag |= (0); termarg.c_lflag &= ~(ECHO|ECHONL|ISIG|ICANON #ifdef XCASE |XCASE #endif ); termarg.c_lflag |= (0); termarg.c_cc[VMIN] = 1; termarg.c_cc[VTIME] = 0; #endif if (Tcsetattr(ttyfd, TCSADRAIN, &termarg) < 0) { Error3("tcsetattr(%d, TCSADRAIN, %p): %s", ttyfd, &termarg, strerror(errno)); } *ttyfdp = ttyfd; *ptyfdp = ptyfd; return 0; } /* generates a socket pair; supports not only PF_UNIX but also PF_INET */ int xiosocketpair2(int pf, int socktype, int protocol, int sv[2]) { int result; switch (pf) { struct sockaddr_in ssin, csin, xsin; /* server, client, compare */ socklen_t cslen, xslen; int sconn, slist, sserv; /* socket FDs */ case PF_UNIX: result = Socketpair(pf, socktype, protocol, sv); if (result < 0) { Error5("socketpair(%d, %d, %d, %p): %s", pf, socktype, protocol, sv, strerror(errno)); return -1; } break; #if LATER case PF_INET: #if 1 /*!!! Linux */ ssin.sin_family = pf; ssin.sin_port = htons(1024+random()%(65536-1024)); ssin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); #endif /* */ if ((s = Socket(pf, socktype, protocol)) < 0) { Error4("socket(%d, %d, %d): %s", pf, socktype, protocol, strerror(errno)); } if (Bind(s, (struct sockaddr *)&ssin, sizeof(ssin)) < 0) { Error6("bind(%d, {%u, 0x%x:%u}, "F_Zu"): %s", s, ssin.sin_family, ssin.sin_addr.s_addr, ssin.sin_port, sizeof(ssin), strerror(errno)); } if (Connect(s, (struct sockaddr *)&ssin, sizeof(ssin)) < 0) { Error6("connect(%d, {%u, 0x%x:%u}, "F_Zu"): %s", s, ssin.sin_family, ssin.sin_addr.s_addr, ssin.sin_port, sizeof(ssin), strerror(errno)); return -1; } break; #endif /* LATER */ case PF_INET: ssin.sin_family = pf; ssin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if ((slist = Socket(pf, socktype, protocol)) < 0) { Error4("socket(%d, %d, %d): %s", pf, socktype, protocol, strerror(errno)); } while (true) { /* find a port we can bind to */ ssin.sin_port = htons(1024+random()%(65536-1024)); if (Bind(slist, (struct sockaddr *)&ssin, sizeof(ssin)) == 0) break; if (errno == EADDRINUSE) { Info6("bind(%d, {%u, 0x%x:%u}, "F_Zu"): %s", slist, ssin.sin_family, ssin.sin_addr.s_addr, ntohs(ssin.sin_port), sizeof(ssin), strerror(errno)); continue; } Error6("bind(%d, {%u, 0x%x:%u}, "F_Zu"): %s", slist, ssin.sin_family, ssin.sin_addr.s_addr, ssin.sin_port, sizeof(ssin), strerror(errno)); Close(slist); return -1; } Listen(slist, 0); if ((sconn = Socket(pf, socktype, protocol)) < 0) { Error4("socket(%d, %d, %d): %s", pf, socktype, protocol, strerror(errno)); Close(slist); return -1; } /* for testing race condition: Sleep(30); */ if (Connect(sconn, (struct sockaddr *)&ssin, sizeof(ssin)) < 0) { Error6("connect(%d, {%u, 0x%x:%u}, "F_Zu"): %s", sconn, ssin.sin_family, ssin.sin_addr.s_addr, ntohs(ssin.sin_port), sizeof(ssin), strerror(errno)); Close(slist); Close(sconn); return -1; } cslen = sizeof(csin); if (Getsockname(sconn, (struct sockaddr *)&csin, &cslen) < 0) { Error4("getsockname(%d, %p, %p): %s", sconn, &csin, &cslen, strerror(errno)); Close(slist); Close(sconn); return -1; } do { xslen = sizeof(xsin); if ((sserv = Accept(slist, (struct sockaddr *)&xsin, &xslen)) < 0) { Error4("accept(%d, %p, {"F_Zu"}): %s", slist, &csin, sizeof(xslen), strerror(errno)); Close(slist); Close(sconn); return -1; } if (!memcmp(&csin, &xsin, cslen)) { break; /* expected connection */ } Warn4("unexpected connection to 0x%lx:%hu from 0x%lx:%hu", ntohl(ssin.sin_addr.s_addr), ntohs(ssin.sin_port), ntohl(xsin.sin_addr.s_addr), ntohs(xsin.sin_port)); } while (true); Close(slist); sv[0] = sconn; sv[1] = sserv; break; default: Error1("xiosocketpair2(): pf=%u not implemented", pf); return -1; } return 0; } /* dual should only be != 0 when both directions are used returns 0 on success */ int xiocommpair(int commtype, bool lefttoright, bool righttoleft, int dual, xiofd_t *left, xiofd_t *right, ...) { va_list ap; int domain = -1, socktype = -1, protocol = -1; int useptmx = 0; /* arrays can be used with pipe(2) and socketpair(2): */ int svlr[2] = {-1, -1}; /* left to right: rfd, wfd */ int svrl[2] = {-1, -1}; /* right to left: rfd, wfd */ /* get related parameters from parameter list */ switch (commtype) { case XIOCOMM_SOCKETPAIR: case XIOCOMM_SOCKETPAIRS: va_start(ap, right); domain = va_arg(ap, int); socktype = va_arg(ap, int); protocol = va_arg(ap, int); va_end(ap); break; case XIOCOMM_PTY: case XIOCOMM_PTYS: va_start(ap, right); useptmx = va_arg(ap, int); va_end(ap); break; default: break; } switch (commtype) { default: /* unspec */ Warn1("internal: undefined communication type %d, defaulting to 0", commtype); commtype = 0; /*PASSTHROUGH*/ case XIOCOMM_SOCKETPAIRS: /* two socketpairs - the default */ if (lefttoright) { if (Socketpair(domain, socktype, protocol, svlr) < 0) { Error5("socketpair(%d, %d, %d, %p): %s", domain, socktype, protocol, svlr, strerror(errno)); } Shutdown(svlr[0], SHUT_WR); } if (righttoleft) { if (Socketpair(domain, socktype, protocol, svrl) < 0) { Error5("socketpair(%d, %d, %d, %p): %s", domain, socktype, protocol, svrl, strerror(errno)); } Shutdown(svrl[0], SHUT_WR); } left->single = right->single = false; left->dtype = right->dtype = XIODATA_STREAM; left->howtoshut = right->howtoshut = XIOSHUT_DOWN; left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE; break; case XIOCOMM_PTYS: /* two ptys in raw mode, EOF in canonical mode */ if (lefttoright) { if (xiopty(useptmx, &svlr[0], &svlr[1]) < 0) return -1; /* pty is write side, interpretes ^D in canonical mode */ } if (righttoleft) { if (xiopty(useptmx, &svrl[0], &svrl[1]) < 0) return -1; } left->single = right->single = false; left->dtype = right->dtype = XIODATA_PTY; left->howtoshut = right->howtoshut = XIOSHUT_PTYEOF; left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE; break; case XIOCOMM_SOCKETPAIR: /* one socketpair */ if (Socketpair(domain, socktype, protocol, svlr) < 0) { Error5("socketpair(%d %d %d, %p): %s", domain, socktype, protocol, svlr, strerror(errno)); return -1; } left->single = right->single = true; left->dtype = right->dtype = XIODATA_STREAM; left->howtoshut = right->howtoshut = XIOSHUT_DOWN; left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE; break; case XIOCOMM_PTY: /* one pty in raw mode, EOF in canonical mode */ if (xiopty(useptmx, &svlr[0], &svlr[1]) < 0) return -1; left->single = right->single = true; left->dtype = right->dtype = XIODATA_PTY; left->howtoshut = XIOSHUT_PTYEOF; right->howtoshut = XIOSHUT_CLOSE; left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE; break; case XIOCOMM_PIPES: /* two pipes */ if (lefttoright) { if (Pipe(svlr) < 0) { Error2("pipe(%p): %s", svlr, strerror(errno)); return -1; } } if (righttoleft) { if (Pipe(svrl) < 0) { Error2("pipe(%p): %s", svrl, strerror(errno)); return -1; } } left->single = right->single = false; left->dtype = right->dtype = XIODATA_STREAM; left->howtoshut = right->howtoshut = XIOSHUT_CLOSE; left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE; break; case XIOCOMM_TCP: case XIOCOMM_TCP4: /* one TCP/IPv4 socket pair */ if (xiosocketpair2(PF_INET, SOCK_STREAM, 0, svlr) < 0) { Error2("socketpair(PF_UNIX, PF_STREAM, 0, %p): %s", svlr, strerror(errno)); return -1; } left->single = right->single = true; left->dtype = right->dtype = XIODATA_STREAM; left->howtoshut = right->howtoshut = XIOSHUT_DOWN; left->howtoclose = right->howtoclose = XIOCLOSE_CLOSE; break; } if (dual && left->single) { /* one pair */ /* dual; we use different FDs for the channels to avoid conflicts (happened in dual exec) */ if ((svrl[1] = Dup(svlr[0])) < 0) { Error2("dup(%d): %s", svrl[0], strerror(errno)); return -1; } if ((svrl[0] = Dup(svlr[1])) < 0) { Error2("dup(%d): %s", svlr[1], strerror(errno)); return -1; } } else if (left->single) { svrl[1] = svlr[0]; svrl[0] = svlr[1]; } /* usually they are not to be passed to exec'd child processes */ if (lefttoright) { Fcntl_l(svlr[0], F_SETFD, 1); Fcntl_l(svlr[1], F_SETFD, 1); } if (righttoleft && (!left->single || dual)) { Fcntl_l(svrl[0], F_SETFD, 1); Fcntl_l(svrl[1], F_SETFD, 1); } left->rfd = svrl[0]; left->wfd = svlr[1]; right->rfd = svlr[0]; right->wfd = svrl[1]; Notice4("xiocommpair() -> [%d:%d], [%d:%d]", left->rfd, left->wfd, right->rfd, right->wfd); return 0; }