/*----------------------------------------------------------------------* rtp_file_flush *----------------------------------------------------------------------*/ int rtp_file_flush (RTP_HANDLE fd) { #ifdef RTP_DISPLAY_ERROR int result; char error[32]; /* ----------------------------------- */ /* Clear the error state by setting */ /* to 0. */ /* ----------------------------------- */ errno = 0; #endif if (!flush_fd((int) fd)) { #ifdef RTP_DISPLAY_ERROR result = errno; rtp_term_cputs("rtp_file_flush: error returned "); rtp_term_puts(rtp_itoa(result, error, 10)); #endif return (-1); } return (0); }
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; }
_public_ int sd_network_monitor_flush(sd_network_monitor *m) { assert_return(m, -EINVAL); return flush_fd(MONITOR_TO_FD(m)); }
int main(int argc, char *argv[]) { enum { FD_SOCKET, FD_WALL_TIMER, FD_NOLOGIN_TIMER, FD_SHUTDOWN_TIMER, _FD_MAX }; int r = EXIT_FAILURE, n_fds; int one = 1; struct shutdownd_command c; struct pollfd pollfd[_FD_MAX]; bool exec_shutdown = false, unlink_nologin = false, failed = false; unsigned i; if (getppid() != 1) { log_error("This program should be invoked by init only."); return EXIT_FAILURE; } if (argc > 1) { log_error("This program does not take arguments."); return EXIT_FAILURE; } log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); log_parse_environment(); log_open(); if ((n_fds = sd_listen_fds(true)) < 0) { log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); return EXIT_FAILURE; } if (n_fds != 1) { log_error("Need exactly one file descriptor."); return EXIT_FAILURE; } if (setsockopt(SD_LISTEN_FDS_START, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { log_error("SO_PASSCRED failed: %m"); return EXIT_FAILURE; } zero(c); zero(pollfd); pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START; pollfd[FD_SOCKET].events = POLLIN; for (i = 0; i < _FD_MAX; i++) { if (i == FD_SOCKET) continue; pollfd[i].events = POLLIN; if ((pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) { log_error("timerfd_create(): %m"); failed = true; } } if (failed) goto finish; log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid()); sd_notify(false, "READY=1\n" "STATUS=Processing requests..."); do { int k; usec_t n; if (poll(pollfd, _FD_MAX, -1) < 0) { if (errno == EAGAIN || errno == EINTR) continue; log_error("poll(): %m"); goto finish; } n = now(CLOCK_REALTIME); if (pollfd[FD_SOCKET].revents) { if ((k = read_packet(pollfd[FD_SOCKET].fd, &c)) < 0) goto finish; else if (k > 0 && c.elapse > 0) { struct itimerspec its; char date[FORMAT_TIMESTAMP_MAX]; if (c.warn_wall) { /* Send wall messages every so often */ zero(its); timespec_store(&its.it_value, when_wall(n, c.elapse)); if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { log_error("timerfd_settime(): %m"); goto finish; } /* Warn immediately if less than 15 minutes are left */ if (n < c.elapse && n + 15*USEC_PER_MINUTE >= c.elapse) warn_wall(n, &c); } /* Disallow logins 5 minutes prior to shutdown */ zero(its); timespec_store(&its.it_value, when_nologin(c.elapse)); if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { log_error("timerfd_settime(): %m"); goto finish; } /* Shutdown after the specified time is reached */ zero(its); timespec_store(&its.it_value, c.elapse); if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { log_error("timerfd_settime(): %m"); goto finish; } sd_notifyf(false, "STATUS=Shutting down at %s...", format_timestamp(date, sizeof(date), c.elapse)); } } if (pollfd[FD_WALL_TIMER].revents) { struct itimerspec its; warn_wall(n, &c); flush_fd(pollfd[FD_WALL_TIMER].fd); /* Restart timer */ zero(its); timespec_store(&its.it_value, when_wall(n, c.elapse)); if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { log_error("timerfd_settime(): %m"); goto finish; } } if (pollfd[FD_NOLOGIN_TIMER].revents) { int e; log_info("Creating /run/nologin, blocking further logins..."); if ((e = write_one_line_file("/run/nologin", "System is going down.")) < 0) log_error("Failed to create /run/nologin: %s", strerror(-e)); else unlink_nologin = true; flush_fd(pollfd[FD_NOLOGIN_TIMER].fd); } if (pollfd[FD_SHUTDOWN_TIMER].revents) { exec_shutdown = true; goto finish; } } while (c.elapse > 0); r = EXIT_SUCCESS; log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid()); finish: for (i = 0; i < _FD_MAX; i++) if (pollfd[i].fd >= 0) close_nointr_nofail(pollfd[i].fd); if (unlink_nologin) unlink("/run/nologin"); if (exec_shutdown) { char sw[3]; sw[0] = '-'; sw[1] = c.mode; sw[2] = 0; execl(SYSTEMCTL_BINARY_PATH, "shutdown", sw, "now", (c.warn_wall && c.wall_message[0]) ? c.wall_message : (c.warn_wall ? NULL : "--no-wall"), NULL); log_error("Failed to execute /sbin/shutdown: %m"); } sd_notify(false, "STATUS=Exiting..."); return r; }