1 /* Copyright (c) 2008, 2009 Nicira Networks, Inc.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 #include "extras/ezio/tty.h"
25 #include <sys/ioctl.h>
28 #include "fatal-signal.h"
29 #include "socket-util.h"
32 #define THIS_MODULE VLM_tty
35 /* Get major() and minor() macros. */
37 # include <sys/mkdev.h>
38 #elif MAJOR_IN_SYSMACROS
39 # include <sys/sysmacros.h>
41 # include <sys/types.h>
43 # define major(dev) (((dev) >> 8) & 0xff)
44 # define minor(dev) ((dev) & 0xff)
52 memset(&l, 0, sizeof l);
54 l.l_whence = SEEK_SET;
57 return fcntl(fd, F_SETLK, &l) == -1 ? errno : 0;
61 remove_lockfile(const char *name)
68 /* Remove existing lockfile. */
69 fd = open(name, O_RDWR);
71 if (errno == ENOENT) {
74 VLOG_ERR("%s: open: %s", name, strerror(errno));
80 n = read(fd, buffer, sizeof buffer - 1);
83 VLOG_ERR("%s: read: %s", name, strerror(error));
88 if (n == 4 && memchr(buffer, '\0', n)) {
90 memcpy(&x, buffer, sizeof x);
93 pid = strtol(buffer, NULL, 10);
97 VLOG_WARN("%s: format not recognized, treating as locked.", name);
101 /* Is lockfile fresh? */
102 if (strstr(buffer, "fcntl")) {
103 int retval = fcntl_lock(fd);
106 VLOG_ERR("%s: device is locked (via fcntl): %s",
107 name, strerror(retval));
110 VLOG_WARN("%s: removing stale lockfile (checked via fcntl)", name);
113 if (!(kill(pid, 0) < 0 && errno == ESRCH)) {
115 VLOG_ERR("%s: device is locked (without fcntl)", name);
118 VLOG_WARN("%s: removing stale lockfile (without fcntl)", name);
123 /* Remove stale lockfile. */
125 VLOG_ERR("%s: unlink: %s", name, strerror(errno));
132 create_lockfile(const char *name)
134 const char *username;
142 old_umask = umask(022);
143 fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
146 VLOG_ERR("%s: create: %s", name, strerror(error));
153 if (fcntl_lock(fd)) {
156 VLOG_ERR("%s: cannot lock: %s", name, strerror(error));
163 username = pwd ? pwd->pw_name : "unknown";
164 snprintf(buffer, sizeof buffer, "%10ld %s %.20s fcntl\n",
165 (long int) getpid(), program_name, username);
166 if (write(fd, buffer, strlen(buffer)) != strlen(buffer)) {
168 VLOG_ERR("%s: write: %s", name, strerror(error));
174 /* We intentionally do not close 'fd', to avoid releasing the fcntl lock.
175 * The asssumption here is that we never unlock a tty. */
176 fatal_signal_add_file_to_unlink(name);
184 int retval = remove_lockfile(name);
186 retval = create_lockfile(name);
193 tty_lock(const char *dev_name)
199 /* Check that the lockfile directory exists. */
200 if (stat(TTY_LOCK_DIR, &s)) {
201 VLOG_ERR("%s: stat: %s", TTY_LOCK_DIR, strerror(errno));
205 /* First lock by device number. */
206 if (stat(dev_name, &s)) {
207 VLOG_ERR("%s: stat: %s", dev_name, strerror(errno));
210 retval = do_lock(xasprintf("%s/LK.%03d.%03d.%03d", TTY_LOCK_DIR,
212 major(s.st_rdev), minor(s.st_rdev)));
217 /* Then lock by device name. */
218 if (!strncmp(dev_name, "/dev/", 5)) {
221 name = xasprintf("%s/%s", TTY_LOCK_DIR, dev_name + 5);
222 for (cp = name + strlen(dev_name) + 1; *cp; cp++) {
228 char *slash = strrchr(dev_name, '/');
229 name = xasprintf ("%s/%s", TTY_LOCK_DIR, slash ? slash + 1 : dev_name);
231 return do_lock(name);
234 struct saved_termios {
240 restore_termios(void *s_)
242 struct saved_termios *s = s_;
243 tcsetattr(s->fd, TCSAFLUSH, &s->tios);
247 tty_set_raw_mode(int fd, speed_t speed)
251 struct saved_termios *s;
253 if (tcgetattr(fd, &tios) < 0) {
257 s = xmalloc(sizeof *s);
261 VLOG_WARN("dup failed: %s", strerror(error));
266 fatal_signal_add_hook(restore_termios, s, true);
268 tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
269 | INLCR | IGNCR | ICRNL | IXON);
270 tios.c_oflag &= ~OPOST;
271 tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
272 tios.c_cflag &= ~(CSIZE | PARENB);
275 cfsetispeed(&tios, speed);
276 cfsetospeed(&tios, speed);
278 if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
282 return set_nonblocking(fd);
286 tty_open_master_pty(void)
291 fd = posix_openpt(O_RDWR | O_NOCTTY);
294 VLOG_WARN("posix_openpt failed: %s", strerror(error));
299 if (grantpt(fd) < 0) {
301 VLOG_WARN("grantpt failed: %s", strerror(error));
306 if (unlockpt(fd) < 0) {
308 VLOG_WARN("unlockpt failed: %s", strerror(error));
313 retval = set_nonblocking(fd);
315 VLOG_WARN("set_nonblocking failed: %s", strerror(retval));
324 tty_fork_child(int master_fd, char *argv[])
332 /* Running in child process. */
335 /* Open pty slave as controlling terminal. */
337 slave_name = ptsname(master_fd);
338 if (slave_name == NULL) {
339 ovs_fatal(errno, "ptsname");
341 slave_fd = open(slave_name, O_RDWR);
342 if (isastream(slave_fd)
343 && (ioctl(slave_fd, I_PUSH, "ptem") < 0
344 || ioctl(slave_fd, I_PUSH, "ldterm") < 0)) {
345 ovs_fatal(errno, "STREAMS ioctl");
348 /* Make pty slave stdin, stdout. */
349 if (dup2(slave_fd, STDIN_FILENO) < 0
350 || dup2(slave_fd, STDOUT_FILENO) < 0
351 || dup2(slave_fd, STDERR_FILENO) < 0) {
352 ovs_fatal(errno, "dup2");
355 /* Close other file descriptors. */
356 for (fd = 3; fd < 20; fd++) {
360 /* Set terminal type. */
361 setenv("TERM", "ezio3", true);
363 /* Invoke subprocess. */
364 execvp(argv[0], argv);
365 ovs_fatal(errno, "execvp");
366 } else if (retval > 0) {
367 /* Running in parent process. */
371 VLOG_WARN("fork failed: %s", strerror(errno));
377 tty_set_window_size(int fd UNUSED, int rows UNUSED, int columns UNUSED)
382 win.ws_col = columns;
385 if (ioctl(fd, TIOCSWINSZ, &win) == -1) {