/* * gnome-login-support.c: * Replacement for systems that lack login_tty, open_pty and forkpty * * Author: * Miguel de Icaza (miguel@gnu.org) * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gnome_login_support.h" /* * HAVE_OPENPTY => HAVE_FORKPTY */ #ifndef HAVE_LOGIN_TTY int g_login_tty (int fd) { pid_t pid = getpid (); /* Create the session */ setsid (); #ifdef TIOCSCTTY if (ioctl (fd, TIOCSCTTY, 0) == -1) return -1; #else /* !TIOCSTTY */ /* Hackery to set controlling tty on SVR4 - on SVR4 the first terminal we open after sesid() becomes our controlling terminal, thus we must find the name of, open, and re-close the tty since we already have it open at this point. */ { char *ctty; int ct_fdes; ctty = ttyname(fd); ct_fdes = open(ctty, O_RDWR); close(ct_fdes); } #endif /* !TIOCSTTY */ #if defined (_POSIX_VERSION) || defined (__svr4__) tcsetpgrp (0, pid); #elif defined (TIOCSPGRP) ioctl (0, TIOCSPGRP, &pid); #endif dup2 (fd, 0); dup2 (fd, 1); dup2 (fd, 2); if (fd > 2) close (fd); return 0; } #endif #ifndef HAVE_OPENPTY static int pty_open_master_bsd (char *pty_name, int *used_bsd) { int pty_master; char *ptr1, *ptr2; *used_bsd = 1; strcpy (pty_name, "/dev/ptyXX"); for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1; ++ptr1) { pty_name [8] = *ptr1; for (ptr2 = "0123456789abcdef"; *ptr2; ++ptr2) { pty_name [9] = *ptr2; /* Try to open master */ if ((pty_master = open (pty_name, O_RDWR | O_NOCTTY)) == -1) { if (errno == ENOENT) /* Different from EIO */ return -1; /* Out of pty devices */ else continue; /* Try next pty device */ } pty_name [5] = 't'; /* Change "pty" to "tty" */ if (access (pty_name, (R_OK | W_OK))){ close (pty_master); pty_name [5] = 'p'; continue; } return pty_master; } } return -1; /* Ran out of pty devices */ } static int pty_open_slave_bsd (const char *pty_name) { int pty_slave; struct group *group_info = getgrnam ("tty"); if (group_info != NULL) { /* The following two calls will only succeed if we are root */ chown (pty_name, getuid (), group_info->gr_gid); chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); } else { chown (pty_name, getuid (), -1); chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); } #ifdef HAVE_REVOKE revoke (pty_name); #endif if ((pty_slave = open (pty_name, O_RDWR | O_NOCTTY)) == -1){ return -1; } return pty_slave; } /* SystemVish pty opening */ #if defined (HAVE_GRANTPT) #ifdef HAVE_STROPTS_H # include #endif static int pty_open_slave (const char *pty_name) { int pty_slave = open (pty_name, O_RDWR); if (pty_slave == -1) return -1; #ifdef HAVE_STROPTS_H #if !defined(__osf__) if (!ioctl (pty_slave, I_FIND, "ptem")) if (ioctl (pty_slave, I_PUSH, "ptem") == -1){ close (pty_slave); return -1; } if (!ioctl (pty_slave, I_FIND, "ldterm")) if (ioctl (pty_slave, I_PUSH, "ldterm") == -1){ close (pty_slave); return -1; } #if !defined(sgi) && !defined(__sgi) if (!ioctl (pty_slave, I_FIND, "ttcompat")) if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1) { perror ("ioctl (pty_slave, I_PUSH, \"ttcompat\")"); close (pty_slave); return -1; } #endif /* sgi || __sgi */ #endif /* __osf__ */ #endif /* HAVE_STROPTS_H */ return pty_slave; } static int pty_open_master (char *pty_name, int *used_bsd) { int pty_master; char *slave_name; strcpy (pty_name, "/dev/ptc"); pty_master = open (pty_name, O_RDWR | O_NOCTTY); if ((pty_master == -1) && (errno == ENOENT)) { strcpy (pty_name, "/dev/ptc"); /* AIX */ pty_master = open (pty_name, O_RDWR | O_NOCTTY); } /* * Try BSD open, this is needed for Linux which * might have Unix98 devices but no kernel support * for those. */ if (pty_master == -1) { *used_bsd = 1; return pty_open_master_bsd (pty_name, used_bsd); } *used_bsd = 0; if (grantpt (pty_master) == -1 || unlockpt (pty_master) == -1) { close (pty_master); return -1; } if ((slave_name = ptsname (pty_master)) == NULL){ close (pty_master); return -1; } strcpy (pty_name, slave_name); return pty_master; } #else # define pty_open_master pty_open_master_bsd # define pty_open_slave pty_open_slave_bsd #endif int g_openpty (int *master_fd, int *slave_fd, char *name, struct termios *termp, struct winsize *winp) { int pty_master, pty_slave, used_bsd = 0; struct group *group_info; char line [256]; pty_master = pty_open_master (line, &used_bsd); fcntl (pty_master, F_SETFD, FD_CLOEXEC); if (pty_master == -1) return -1; group_info = getgrnam ("tty"); if (group_info != NULL){ chown (line, getuid (), group_info->gr_gid); chmod (line, S_IRUSR | S_IWUSR | S_IWGRP); } else { chown (line, getuid (), -1); chmod (line, S_IRUSR | S_IWUSR | S_IWGRP); } #ifdef HAVE_REVOKE revoke (line); #endif /* Open slave side */ if (used_bsd) pty_slave = pty_open_slave_bsd (line); else pty_slave = pty_open_slave (line); if (pty_slave == -1){ close (pty_master); errno = ENOENT; return -1; } fcntl (pty_slave, F_SETFD, FD_CLOEXEC); *master_fd = pty_master; *slave_fd = pty_slave; if (termp) tcsetattr (pty_slave, TCSAFLUSH, termp); if (winp) ioctl (pty_slave, TIOCSWINSZ, winp); if (name) strcpy (name, line); return 0; } pid_t g_forkpty (int *master_fd, char *name, struct termios *termp, struct winsize *winp) { int master, slave; pid_t pid; if (g_openpty (&master, &slave, name, termp, winp) == -1) return -1; pid = fork (); if (pid == -1) return -1; /* Child */ if (pid == 0){ close (master); g_login_tty (slave); } else { *master_fd = master; close (slave); } return pid; } #endif /* HAVE_OPENPTY */ int n_read (int fd, void *buf, int count) { int n = 0, i; char *buffer; buffer = (char*) buf; while (n < count) { i = read (fd, buffer + n, count - n); switch (i) { case -1: switch (errno) { case EINTR: case EAGAIN: #ifdef ERESTART case ERESTART: #endif /* suppress these errors */ break; default: return -1; break; } break; case 0: return n; break; default: n += i; break; } } return n; } int n_write (int fd, const void *buf, int count) { int n = 0, i; const char *buffer; buffer = (char*) buf; while (n < count) { i = write (fd, buffer + n, count - n); switch (i) { case -1: switch (errno) { case EINTR: case EAGAIN: #ifdef ERESTART case ERESTART: #endif /* suppress these errors */ break; default: return -1; break; } break; case 0: return n; break; default: n += i; break; } } return n; }