int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { struct termios old_termios, new_termios; char c, line[LINE_MAX]; assert(f); assert(ret); if (tcgetattr(fileno(f), &old_termios) >= 0) { new_termios = old_termios; new_termios.c_lflag &= ~ICANON; new_termios.c_cc[VMIN] = 1; new_termios.c_cc[VTIME] = 0; if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) { size_t k; if (t != USEC_INFINITY) { if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) { tcsetattr(fileno(f), TCSADRAIN, &old_termios); return -ETIMEDOUT; } } k = fread(&c, 1, 1, f); tcsetattr(fileno(f), TCSADRAIN, &old_termios); if (k <= 0) return -EIO; if (need_nl) *need_nl = c != '\n'; *ret = c; return 0; } } if (t != USEC_INFINITY) { if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) return -ETIMEDOUT; } errno = 0; if (!fgets(line, sizeof(line), f)) return errno ? -errno : -EIO; truncate_nl(line); if (strlen(line) != 1) return -EBADMSG; if (need_nl) *need_nl = false; *ret = line[0]; return 0; }
ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { uint8_t *p = buf; ssize_t n = 0; assert(fd >= 0); assert(buf); /* If called with nbytes == 0, let's call read() at least * once, to validate the operation */ if (nbytes > (size_t) SSIZE_MAX) return -EINVAL; do { ssize_t k; k = read(fd, p, nbytes); if (k < 0) { if (errno == EINTR) continue; if (errno == EAGAIN && do_poll) { /* We knowingly ignore any return value here, * and expect that any error/EOF is reported * via read() */ (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY); continue; } return n > 0 ? n : -errno; } if (k == 0) return n; assert((size_t) k <= nbytes); p += k; nbytes -= k; n += k; } while (nbytes > 0); return n; }
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { const uint8_t *p = buf; assert(fd >= 0); assert(buf); if (nbytes > (size_t) SSIZE_MAX) return -EINVAL; do { ssize_t k; k = write(fd, p, nbytes); if (k < 0) { if (errno == EINTR) continue; if (errno == EAGAIN && do_poll) { /* We knowingly ignore any return value here, * and expect that any error/EOF is reported * via write() */ (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY); continue; } return -errno; } if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */ return -EIO; assert((size_t) k <= nbytes); p += k; nbytes -= k; } while (nbytes > 0); return 0; }
int polkit_agent_open(void) { int r; int pipe_fd[2]; char notify_fd[DECIMAL_STR_MAX(int) + 1]; if (agent_pid > 0) return 0; /* We check STDIN here, not STDOUT, since this is about input, * not output */ if (!isatty(STDIN_FILENO)) return 0; if (pipe2(pipe_fd, 0) < 0) return -errno; xsprintf(notify_fd, "%i", pipe_fd[1]); r = fork_agent(&agent_pid, &pipe_fd[1], 1, POLKIT_AGENT_BINARY_PATH, POLKIT_AGENT_BINARY_PATH, "--notify-fd", notify_fd, "--fallback", NULL); /* Close the writing side, because that's the one for the agent */ safe_close(pipe_fd[1]); if (r < 0) log_error_errno(r, "Failed to fork TTY ask password agent: %m"); else /* Wait until the agent closes the fd */ fd_wait_for_event(pipe_fd[0], POLLHUP, USEC_INFINITY); safe_close(pipe_fd[0]); return r; }
int acquire_terminal( const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout) { int fd = -1, notify = -1, r = 0, wd = -1; usec_t ts = 0; assert(name); /* We use inotify to be notified when the tty is closed. We * create the watch before checking if we can actually acquire * it, so that we don't lose any event. * * Note: strictly speaking this actually watches for the * device being closed, it does *not* really watch whether a * tty loses its controlling process. However, unless some * rogue process uses TIOCNOTTY on /dev/tty *after* closing * its tty otherwise this will not become a problem. As long * as the administrator makes sure not configure any service * on the same tty as an untrusted user this should not be a * problem. (Which he probably should not do anyway.) */ if (timeout != USEC_INFINITY) ts = now(CLOCK_MONOTONIC); if (!fail && !force) { notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0)); if (notify < 0) { r = -errno; goto fail; } wd = inotify_add_watch(notify, name, IN_CLOSE); if (wd < 0) { r = -errno; goto fail; } } for (;;) { struct sigaction sa_old, sa_new = { .sa_handler = SIG_IGN, .sa_flags = SA_RESTART, }; if (notify >= 0) { r = flush_fd(notify); if (r < 0) goto fail; } /* We pass here O_NOCTTY only so that we can check the return * value TIOCSCTTY and have a reliable way to figure out if we * successfully became the controlling process of the tty */ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); if (fd < 0) return fd; /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed * if we already own the tty. */ assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0); /* First, try to get the tty */ if (ioctl(fd, TIOCSCTTY, force) < 0) r = -errno; assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0); /* Sometimes it makes sense to ignore TIOCSCTTY * returning EPERM, i.e. when very likely we already * are have this controlling terminal. */ if (r < 0 && r == -EPERM && ignore_tiocstty_eperm) r = 0; if (r < 0 && (force || fail || r != -EPERM)) goto fail; if (r >= 0) break; assert(!fail); assert(!force); assert(notify >= 0); for (;;) { union inotify_event_buffer buffer; struct inotify_event *e; ssize_t l; if (timeout != USEC_INFINITY) { usec_t n; n = now(CLOCK_MONOTONIC); if (ts + timeout < n) { r = -ETIMEDOUT; goto fail; } r = fd_wait_for_event(fd, POLLIN, ts + timeout - n); if (r < 0) goto fail; if (r == 0) { r = -ETIMEDOUT; goto fail; } } l = read(notify, &buffer, sizeof(buffer)); if (l < 0) { if (errno == EINTR || errno == EAGAIN) continue; r = -errno; goto fail; } FOREACH_INOTIFY_EVENT(e, buffer, l) { if (e->wd != wd || !(e->mask & IN_CLOSE)) { r = -EIO; goto fail; } } break; } /* We close the tty fd here since if the old session * ended our handle will be dead. It's important that * we do this after sleeping, so that we don't enter * an endless loop. */ fd = safe_close(fd); } safe_close(notify); r = reset_terminal_fd(fd, true); if (r < 0) log_warning_errno(r, "Failed to reset terminal: %m"); return fd; fail: safe_close(fd); safe_close(notify); return r; }