| /* source: xio-pty.c */ |
| /* Copyright Gerhard Rieger and contributors (see file CHANGES) */ |
| /* Published under the GNU General Public License V.2, see file COPYING */ |
| |
| /* this file contains the source for creating pty addresses */ |
| |
| #include "xiosysincludes.h" |
| #include "xioopen.h" |
| |
| #include "xio-named.h" |
| #include "xio-termios.h" |
| |
| |
| #if WITH_PTY |
| |
| /* here define the preferred polling intervall, in seconds */ |
| #define PTY_INTERVALL 1,0 /* for struct timespec */ |
| |
| #define MAXPTYNAMELEN 64 |
| |
| static int xioopen_pty(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc); |
| |
| const struct addrdesc xioaddr_pty = { "PTY", 3, xioopen_pty, GROUP_NAMED|GROUP_FD|GROUP_TERMIOS|GROUP_PTY, 0, 0, 0 HELP("") }; |
| |
| const struct optdesc opt_symbolic_link = { "symbolic-link", "link", OPT_SYMBOLIC_LINK, GROUP_PTY, PH_LATE, TYPE_FILENAME, OFUNC_SPEC, 0, 0 }; |
| #if HAVE_POLL |
| const struct optdesc opt_pty_wait_slave = { "pty-wait-slave", "wait-slave", OPT_PTY_WAIT_SLAVE, GROUP_PTY, PH_EARLY, TYPE_BOOL, OFUNC_SPEC, 0, 0 }; |
| const struct optdesc opt_pty_intervall = { "pty-interval", NULL, OPT_PTY_INTERVALL, GROUP_PTY, PH_EARLY, TYPE_TIMESPEC, OFUNC_SPEC, 0, 0 }; |
| #endif /* HAVE_POLL */ |
| |
| static int xioopen_pty( |
| int argc, |
| const char *argv[], |
| struct opt *opts, |
| int xioflags, |
| xiofile_t *xfd, |
| const struct addrdesc *addrdesc) |
| { |
| /* we expect the form: filename */ |
| struct single *sfd = &xfd->stream; |
| int ptyfd = -1; /* master */ |
| int ttyfd = -1; /* slave */ |
| #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) { |
| xio_syntax(argv[0], 0, argc-1, addrdesc->syntax); |
| return STAT_NORETRY; |
| } |
| |
| if (sfd->howtoend == END_UNSPEC) |
| sfd->howtoend = END_CLOSE; |
| |
| if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1; |
| applyopts(sfd, -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(sfd, opts, PH_INIT) < 0) return -1; |
| applyopts2(sfd, -1, opts, PH_INIT, PH_EARLY); |
| |
| applyopts(sfd, -1, opts, PH_PREBIGEN); |
| |
| #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 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 || ptyfd < 0) { |
| 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) { |
| /* 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 (!retropt_string(opts, OPT_SYMBOLIC_LINK, &linkname)) { |
| xio_unlink(linkname, E_ERROR); |
| if (Symlink(ptyname, linkname) < 0) { |
| Error3("symlink(\"%s\", \"%s\"): %s", |
| ptyname, linkname, strerror(errno)); |
| } |
| if (opt_unlink_close) { |
| if ((sfd->unlink_close = strdup(linkname)) == NULL) { |
| Error1("strdup(\"%s\"): out of memory", linkname); |
| } |
| sfd->opt_unlink_close = true; |
| } |
| } |
| |
| applyopts_named(ptyname, opts, PH_PASTOPEN); |
| applyopts_named(ptyname, opts, PH_FD); |
| |
| applyopts_cloexec(ptyfd, opts);/*!*/ |
| sfd->dtype = XIODATA_PTY; |
| |
| applyopts(sfd, ttyfd, 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)); |
| } |
| } |
| |
| } |
| |
| sfd->fd = ptyfd; |
| applyopts(sfd, -1, opts, PH_LATE); |
| if (applyopts_single(sfd, 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; |
| } |
| #endif /* WITH_PTY */ |