1 /* Copyright (c) 2008, 2009 Nicira Networks, Inc.
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * In addition, as a special exception, Nicira Networks gives permission
17 * to link the code of its release of vswitchd with the OpenSSL project's
18 * "OpenSSL" library (or with modified versions of it that use the same
19 * license as the "OpenSSL" library), and distribute the linked
20 * executables. You must obey the GNU General Public License in all
21 * respects for all of the code used other than "OpenSSL". If you modify
22 * this file, you may extend this exception to your version of the file,
23 * but you are not obligated to do so. If you do not wish to do so,
24 * delete this exception statement from your version.
29 #include "extras/ezio/tty.h"
37 #include <sys/ioctl.h>
40 #include "fatal-signal.h"
41 #include "socket-util.h"
44 #define THIS_MODULE VLM_tty
47 /* Get major() and minor() macros. */
49 # include <sys/mkdev.h>
50 #elif MAJOR_IN_SYSMACROS
51 # include <sys/sysmacros.h>
53 # include <sys/types.h>
55 # define major(dev) (((dev) >> 8) & 0xff)
56 # define minor(dev) ((dev) & 0xff)
64 memset(&l, 0, sizeof l);
66 l.l_whence = SEEK_SET;
69 return fcntl(fd, F_SETLK, &l) == -1 ? errno : 0;
73 remove_lockfile(const char *name)
80 /* Remove existing lockfile. */
81 fd = open(name, O_RDWR);
83 if (errno == ENOENT) {
86 VLOG_ERR("%s: open: %s", name, strerror(errno));
92 n = read(fd, buffer, sizeof buffer - 1);
95 VLOG_ERR("%s: read: %s", name, strerror(error));
100 if (n == 4 && memchr(buffer, '\0', n)) {
102 memcpy(&x, buffer, sizeof x);
105 pid = strtol(buffer, NULL, 10);
109 VLOG_WARN("%s: format not recognized, treating as locked.", name);
113 /* Is lockfile fresh? */
114 if (strstr(buffer, "fcntl")) {
115 int retval = fcntl_lock(fd);
118 VLOG_ERR("%s: device is locked (via fcntl): %s",
119 name, strerror(retval));
122 VLOG_WARN("%s: removing stale lockfile (checked via fcntl)", name);
125 if (!(kill(pid, 0) < 0 && errno == ESRCH)) {
127 VLOG_ERR("%s: device is locked (without fcntl)", name);
130 VLOG_WARN("%s: removing stale lockfile (without fcntl)", name);
135 /* Remove stale lockfile. */
137 VLOG_ERR("%s: unlink: %s", name, strerror(errno));
144 create_lockfile(const char *name)
146 const char *username;
154 old_umask = umask(022);
155 fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
158 VLOG_ERR("%s: create: %s", name, strerror(error));
165 if (fcntl_lock(fd)) {
168 VLOG_ERR("%s: cannot lock: %s", name, strerror(error));
175 username = pwd ? pwd->pw_name : "unknown";
176 snprintf(buffer, sizeof buffer, "%10ld %s %.20s fcntl\n",
177 (long int) getpid(), program_name, username);
178 if (write(fd, buffer, strlen(buffer)) != strlen(buffer)) {
180 VLOG_ERR("%s: write: %s", name, strerror(error));
186 /* We intentionally do not close 'fd', to avoid releasing the fcntl lock.
187 * The asssumption here is that we never unlock a tty. */
188 fatal_signal_add_file_to_unlink(name);
196 int retval = remove_lockfile(name);
198 retval = create_lockfile(name);
205 tty_lock(const char *dev_name)
211 /* Check that the lockfile directory exists. */
212 if (stat(TTY_LOCK_DIR, &s)) {
213 VLOG_ERR("%s: stat: %s", TTY_LOCK_DIR, strerror(errno));
217 /* First lock by device number. */
218 if (stat(dev_name, &s)) {
219 VLOG_ERR("%s: stat: %s", dev_name, strerror(errno));
222 retval = do_lock(xasprintf("%s/LK.%03d.%03d.%03d", TTY_LOCK_DIR,
224 major(s.st_rdev), minor(s.st_rdev)));
229 /* Then lock by device name. */
230 if (!strncmp(dev_name, "/dev/", 5)) {
233 name = xasprintf("%s/%s", TTY_LOCK_DIR, dev_name + 5);
234 for (cp = name + strlen(dev_name) + 1; *cp; cp++) {
240 char *slash = strrchr(dev_name, '/');
241 name = xasprintf ("%s/%s", TTY_LOCK_DIR, slash ? slash + 1 : dev_name);
243 return do_lock(name);
246 struct saved_termios {
252 restore_termios(void *s_)
254 struct saved_termios *s = s_;
255 tcsetattr(s->fd, TCSAFLUSH, &s->tios);
259 tty_set_raw_mode(int fd, speed_t speed)
263 struct saved_termios *s;
265 if (tcgetattr(fd, &tios) < 0) {
269 s = xmalloc(sizeof *s);
273 VLOG_WARN("dup failed: %s", strerror(error));
278 fatal_signal_add_hook(restore_termios, s, true);
280 tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
281 | INLCR | IGNCR | ICRNL | IXON);
282 tios.c_oflag &= ~OPOST;
283 tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
284 tios.c_cflag &= ~(CSIZE | PARENB);
287 cfsetispeed(&tios, speed);
288 cfsetospeed(&tios, speed);
290 if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
294 return set_nonblocking(fd);
298 tty_open_master_pty(void)
303 fd = posix_openpt(O_RDWR | O_NOCTTY);
306 VLOG_WARN("posix_openpt failed: %s", strerror(error));
311 if (grantpt(fd) < 0) {
313 VLOG_WARN("grantpt failed: %s", strerror(error));
318 if (unlockpt(fd) < 0) {
320 VLOG_WARN("unlockpt failed: %s", strerror(error));
325 retval = set_nonblocking(fd);
327 VLOG_WARN("set_nonblocking failed: %s", strerror(retval));
336 tty_fork_child(int master_fd, char *argv[])
344 /* Running in child process. */
347 /* Open pty slave as controlling terminal. */
349 slave_name = ptsname(master_fd);
350 if (slave_name == NULL) {
351 ovs_fatal(errno, "ptsname");
353 slave_fd = open(slave_name, O_RDWR);
354 if (isastream(slave_fd)
355 && (ioctl(slave_fd, I_PUSH, "ptem") < 0
356 || ioctl(slave_fd, I_PUSH, "ldterm") < 0)) {
357 ovs_fatal(errno, "STREAMS ioctl");
360 /* Make pty slave stdin, stdout. */
361 if (dup2(slave_fd, STDIN_FILENO) < 0
362 || dup2(slave_fd, STDOUT_FILENO) < 0
363 || dup2(slave_fd, STDERR_FILENO) < 0) {
364 ovs_fatal(errno, "dup2");
367 /* Close other file descriptors. */
368 for (fd = 3; fd < 20; fd++) {
372 /* Set terminal type. */
373 setenv("TERM", "ezio3", true);
375 /* Invoke subprocess. */
376 execvp(argv[0], argv);
377 ovs_fatal(errno, "execvp");
378 } else if (retval > 0) {
379 /* Running in parent process. */
383 VLOG_WARN("fork failed: %s", strerror(errno));
389 tty_set_window_size(int fd UNUSED, int rows UNUSED, int columns UNUSED)
394 win.ws_col = columns;
397 if (ioctl(fd, TIOCSWINSZ, &win) == -1) {