From 064af42167bf4fc9aaea2702d80ce08074b889c0 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 8 Jul 2009 13:19:16 -0700 Subject: Import from old repository commit 61ef2b42a9c4ba8e1600f15bb0236765edc2ad45. --- extras/ezio/tty.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 extras/ezio/tty.c (limited to 'extras/ezio/tty.c') diff --git a/extras/ezio/tty.c b/extras/ezio/tty.c new file mode 100644 index 00000000..ce788f28 --- /dev/null +++ b/extras/ezio/tty.c @@ -0,0 +1,404 @@ +/* Copyright (c) 2008, 2009 Nicira Networks, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * In addition, as a special exception, Nicira Networks gives permission + * to link the code of its release of vswitchd with the OpenSSL project's + * "OpenSSL" library (or with modified versions of it that use the same + * license as the "OpenSSL" library), and distribute the linked + * executables. You must obey the GNU General Public License in all + * respects for all of the code used other than "OpenSSL". If you modify + * this file, you may extend this exception to your version of the file, + * but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. + * + */ + +#include +#include "extras/ezio/tty.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fatal-signal.h" +#include "socket-util.h" +#include "util.h" + +#define THIS_MODULE VLM_tty +#include "vlog.h" + +/* Get major() and minor() macros. */ +#if MAJOR_IN_MKDEV +# include +#elif MAJOR_IN_SYSMACROS +# include +#else +# include +# ifndef major +# define major(dev) (((dev) >> 8) & 0xff) +# define minor(dev) ((dev) & 0xff) +# endif +#endif + +static int +fcntl_lock(int fd) +{ + struct flock l; + memset(&l, 0, sizeof l); + l.l_type = F_WRLCK; + l.l_whence = SEEK_SET; + l.l_start = 0; + l.l_len = 0; + return fcntl(fd, F_SETLK, &l) == -1 ? errno : 0; +} + +static int +remove_lockfile(const char *name) +{ + char buffer[BUFSIZ]; + ssize_t n; + pid_t pid; + int fd; + + /* Remove existing lockfile. */ + fd = open(name, O_RDWR); + if (fd < 0) { + if (errno == ENOENT) { + return 0; + } else { + VLOG_ERR("%s: open: %s", name, strerror(errno)); + return errno; + } + } + + /* Read lockfile. */ + n = read(fd, buffer, sizeof buffer - 1); + if (n < 0) { + int error = errno; + VLOG_ERR("%s: read: %s", name, strerror(error)); + close(fd); + return error; + } + buffer[n] = '\0'; + if (n == 4 && memchr(buffer, '\0', n)) { + int32_t x; + memcpy(&x, buffer, sizeof x); + pid = x; + } else if (n >= 0) { + pid = strtol(buffer, NULL, 10); + } + if (pid <= 0) { + close(fd); + VLOG_WARN("%s: format not recognized, treating as locked.", name); + return EACCES; + } + + /* Is lockfile fresh? */ + if (strstr(buffer, "fcntl")) { + int retval = fcntl_lock(fd); + if (retval) { + close(fd); + VLOG_ERR("%s: device is locked (via fcntl): %s", + name, strerror(retval)); + return retval; + } else { + VLOG_WARN("%s: removing stale lockfile (checked via fcntl)", name); + } + } else { + if (!(kill(pid, 0) < 0 && errno == ESRCH)) { + close(fd); + VLOG_ERR("%s: device is locked (without fcntl)", name); + return EACCES; + } else { + VLOG_WARN("%s: removing stale lockfile (without fcntl)", name); + } + } + close(fd); + + /* Remove stale lockfile. */ + if (unlink(name)) { + VLOG_ERR("%s: unlink: %s", name, strerror(errno)); + return errno; + } + return 0; +} + +static int +create_lockfile(const char *name) +{ + const char *username; + char buffer[BUFSIZ]; + struct passwd *pwd; + mode_t old_umask; + uid_t uid; + int fd; + + /* Create file. */ + old_umask = umask(022); + fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (fd < 0) { + int error = errno; + VLOG_ERR("%s: create: %s", name, strerror(error)); + umask(old_umask); + return error; + } + umask(old_umask); + + /* Lock file. */ + if (fcntl_lock(fd)) { + int error = errno; + close(fd); + VLOG_ERR("%s: cannot lock: %s", name, strerror(error)); + return error; + } + + /* Write to file. */ + uid = getuid(); + pwd = getpwuid(uid); + username = pwd ? pwd->pw_name : "unknown"; + snprintf(buffer, sizeof buffer, "%10ld %s %.20s fcntl\n", + (long int) getpid(), program_name, username); + if (write(fd, buffer, strlen(buffer)) != strlen(buffer)) { + int error = errno; + VLOG_ERR("%s: write: %s", name, strerror(error)); + close(fd); + unlink(name); + return error; + } + + /* We intentionally do not close 'fd', to avoid releasing the fcntl lock. + * The asssumption here is that we never unlock a tty. */ + fatal_signal_add_file_to_unlink(name); + + return 0; +} + +static int +do_lock(char *name) +{ + int retval = remove_lockfile(name); + if (!retval) { + retval = create_lockfile(name); + } + free(name); + return retval; +} + +int +tty_lock(const char *dev_name) +{ + struct stat s; + char *name; + int retval; + + /* Check that the lockfile directory exists. */ + if (stat(TTY_LOCK_DIR, &s)) { + VLOG_ERR("%s: stat: %s", TTY_LOCK_DIR, strerror(errno)); + return errno; + } + + /* First lock by device number. */ + if (stat(dev_name, &s)) { + VLOG_ERR("%s: stat: %s", dev_name, strerror(errno)); + return errno; + } + retval = do_lock(xasprintf("%s/LK.%03d.%03d.%03d", TTY_LOCK_DIR, + major(s.st_dev), + major(s.st_rdev), minor(s.st_rdev))); + if (retval) { + return retval; + } + + /* Then lock by device name. */ + if (!strncmp(dev_name, "/dev/", 5)) { + char *cp; + + name = xasprintf("%s/%s", TTY_LOCK_DIR, dev_name + 5); + for (cp = name + strlen(dev_name) + 1; *cp; cp++) { + if (*cp == '/') { + *cp = '_'; + } + } + } else { + char *slash = strrchr(dev_name, '/'); + name = xasprintf ("%s/%s", TTY_LOCK_DIR, slash ? slash + 1 : dev_name); + } + return do_lock(name); +} + +struct saved_termios { + int fd; + struct termios tios; +}; + +static void +restore_termios(void *s_) +{ + struct saved_termios *s = s_; + tcsetattr(s->fd, TCSAFLUSH, &s->tios); +} + +int +tty_set_raw_mode(int fd, speed_t speed) +{ + if (isatty(fd)) { + struct termios tios; + struct saved_termios *s; + + if (tcgetattr(fd, &tios) < 0) { + return errno; + } + + s = xmalloc(sizeof *s); + s->fd = dup(fd); + if (s->fd < 0) { + int error = errno; + VLOG_WARN("dup failed: %s", strerror(error)); + free(s); + return errno; + } + s->tios = tios; + fatal_signal_add_hook(restore_termios, s, true); + + tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP + | INLCR | IGNCR | ICRNL | IXON); + tios.c_oflag &= ~OPOST; + tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + tios.c_cflag &= ~(CSIZE | PARENB); + tios.c_cflag |= CS8; + if (speed != B0) { + cfsetispeed(&tios, speed); + cfsetospeed(&tios, speed); + } + if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { + return errno; + } + } + return set_nonblocking(fd); +} + +int +tty_open_master_pty(void) +{ + int retval; + int fd; + + fd = posix_openpt(O_RDWR | O_NOCTTY); + if (fd < 0) { + int error = errno; + VLOG_WARN("posix_openpt failed: %s", strerror(error)); + close(fd); + return -error; + } + + if (grantpt(fd) < 0) { + int error = errno; + VLOG_WARN("grantpt failed: %s", strerror(error)); + close(fd); + return -error; + } + + if (unlockpt(fd) < 0) { + int error = errno; + VLOG_WARN("unlockpt failed: %s", strerror(error)); + close(fd); + return -error; + } + + retval = set_nonblocking(fd); + if (retval) { + VLOG_WARN("set_nonblocking failed: %s", strerror(retval)); + close(fd); + return retval; + } + + return fd; +} + +int +tty_fork_child(int master_fd, char *argv[]) +{ + int retval = fork(); + if (!retval) { + char *slave_name; + int slave_fd; + int fd; + + /* Running in child process. */ + fatal_signal_fork(); + + /* Open pty slave as controlling terminal. */ + setsid(); + slave_name = ptsname(master_fd); + if (slave_name == NULL) { + ovs_fatal(errno, "ptsname"); + } + slave_fd = open(slave_name, O_RDWR); + if (isastream(slave_fd) + && (ioctl(slave_fd, I_PUSH, "ptem") < 0 + || ioctl(slave_fd, I_PUSH, "ldterm") < 0)) { + ovs_fatal(errno, "STREAMS ioctl"); + } + + /* Make pty slave stdin, stdout. */ + if (dup2(slave_fd, STDIN_FILENO) < 0 + || dup2(slave_fd, STDOUT_FILENO) < 0 + || dup2(slave_fd, STDERR_FILENO) < 0) { + ovs_fatal(errno, "dup2"); + } + + /* Close other file descriptors. */ + for (fd = 3; fd < 20; fd++) { + close(fd); + } + + /* Set terminal type. */ + setenv("TERM", "ezio3", true); + + /* Invoke subprocess. */ + execvp(argv[0], argv); + ovs_fatal(errno, "execvp"); + } else if (retval > 0) { + /* Running in parent process. */ + return 0; + } else { + /* Fork failed. */ + VLOG_WARN("fork failed: %s", strerror(errno)); + return errno; + } +} + +int +tty_set_window_size(int fd UNUSED, int rows UNUSED, int columns UNUSED) +{ +#ifdef TIOCGWINSZ + struct winsize win; + win.ws_row = rows; + win.ws_col = columns; + win.ws_xpixel = 0; + win.ws_ypixel = 0; + if (ioctl(fd, TIOCSWINSZ, &win) == -1) { + return errno; + } +#else +#error +#endif + return 0; +} -- cgit v1.2.3