return !!(r & POLLHUP);
}
-int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
+int ppoll_usec_full(struct pollfd *fds, size_t nfds, usec_t timeout, const sigset_t *ss) {
int r;
assert(fds || nfds == 0);
if (nfds == 0)
return 0;
- r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), NULL);
+ r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), ss);
if (r < 0)
return -errno;
if (r == 0)
int pipe_eof(int fd);
-int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) _nonnull_if_nonzero_(1, 2);
+int ppoll_usec_full(struct pollfd *fds, size_t nfds, usec_t timeout, const sigset_t *ss) _nonnull_if_nonzero_(1, 2);
+_nonnull_if_nonzero_(1, 2) static inline int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
+ return ppoll_usec_full(fds, nfds, timeout, NULL);
+}
+
int fd_wait_for_event(int fd, int event, usec_t timeout);
ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
int r, wd = -1;
assert(name);
- assert(IN_SET(flags & ~ACQUIRE_TERMINAL_PERMISSIVE, ACQUIRE_TERMINAL_TRY, ACQUIRE_TERMINAL_FORCE, ACQUIRE_TERMINAL_WAIT));
+
+ AcquireTerminalFlags mode = flags & _ACQUIRE_TERMINAL_MODE_MASK;
+ assert(IN_SET(mode, ACQUIRE_TERMINAL_TRY, ACQUIRE_TERMINAL_FORCE, ACQUIRE_TERMINAL_WAIT));
+ assert(mode == ACQUIRE_TERMINAL_WAIT || !FLAGS_SET(flags, ACQUIRE_TERMINAL_WATCH_SIGTERM));
/* 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.
* not configure any service on the same tty as an untrusted user this should not be a problem. (Which they
* probably should not do anyway.) */
- if ((flags & ~ACQUIRE_TERMINAL_PERMISSIVE) == ACQUIRE_TERMINAL_WAIT) {
- notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
+ if (mode == ACQUIRE_TERMINAL_WAIT) {
+ notify = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
if (notify < 0)
return -errno;
ts = now(CLOCK_MONOTONIC);
}
+ /* If we are called with ACQUIRE_TERMINAL_WATCH_SIGTERM we'll unblock SIGTERM during ppoll() temporarily */
+ sigset_t poll_ss;
+ assert_se(sigprocmask(SIG_SETMASK, /* newset= */ NULL, &poll_ss) >= 0);
+ if (flags & ACQUIRE_TERMINAL_WATCH_SIGTERM) {
+ assert_se(sigismember(&poll_ss, SIGTERM) > 0);
+ assert_se(sigdelset(&poll_ss, SIGTERM) >= 0);
+ }
+
for (;;) {
if (notify >= 0) {
r = flush_fd(notify);
assert_se(sigaction(SIGHUP, &sigaction_ignore, &sa_old) >= 0);
/* First, try to get the tty */
- r = RET_NERRNO(ioctl(fd, TIOCSCTTY, (flags & ~ACQUIRE_TERMINAL_PERMISSIVE) == ACQUIRE_TERMINAL_FORCE));
+ r = RET_NERRNO(ioctl(fd, TIOCSCTTY, mode == ACQUIRE_TERMINAL_FORCE));
/* Reset signal handler to old value */
assert_se(sigaction(SIGHUP, &sa_old, NULL) >= 0);
* already are the owner of the TTY. */
break;
- if (flags != ACQUIRE_TERMINAL_WAIT) /* If we are in TRY or FORCE mode, then propagate EPERM as EPERM */
+ if (mode != ACQUIRE_TERMINAL_WAIT) /* If we are in TRY or FORCE mode, then propagate EPERM as EPERM */
return r;
assert(notify >= 0);
assert(wd >= 0);
for (;;) {
- union inotify_event_buffer buffer;
- ssize_t l;
-
- if (timeout != USEC_INFINITY) {
- usec_t n;
-
+ usec_t left;
+ if (timeout == USEC_INFINITY)
+ left = USEC_INFINITY;
+ else {
assert(ts != USEC_INFINITY);
- n = usec_sub_unsigned(now(CLOCK_MONOTONIC), ts);
+ usec_t n = usec_sub_unsigned(now(CLOCK_MONOTONIC), ts);
if (n >= timeout)
return -ETIMEDOUT;
- r = fd_wait_for_event(notify, POLLIN, usec_sub_unsigned(timeout, n));
- if (r < 0)
- return r;
- if (r == 0)
- return -ETIMEDOUT;
+ left = timeout - n;
}
+ r = ppoll_usec_full(
+ &(struct pollfd) {
+ .fd = notify,
+ .events = POLLIN,
+ },
+ /* n_pollfds = */ 1,
+ left,
+ &poll_ss);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ETIMEDOUT;
+
+ union inotify_event_buffer buffer;
+ ssize_t l;
l = read(notify, &buffer, sizeof(buffer));
if (l < 0) {
if (ERRNO_IS_TRANSIENT(errno))
/* If we can't become the controlling process of the TTY right-away, then wait until we can. */
ACQUIRE_TERMINAL_WAIT = 2,
+ /* The combined mask of the above */
+ _ACQUIRE_TERMINAL_MODE_MASK = ACQUIRE_TERMINAL_TRY | ACQUIRE_TERMINAL_FORCE | ACQUIRE_TERMINAL_WAIT,
+
/* Pick one of the above, and then OR this flag in, in order to request permissive behaviour, if we can't become controlling process then don't mind */
ACQUIRE_TERMINAL_PERMISSIVE = 1 << 2,
+
+ /* Check for pending SIGTERM while waiting for inotify (SIGTERM must be blocked by caller) */
+ ACQUIRE_TERMINAL_WATCH_SIGTERM = 1 << 3,
} AcquireTerminalFlags;
int acquire_terminal(const char *name, AcquireTerminalFlags flags, usec_t timeout);