/
niceSOFT
/
util-linux
Обзор
Документация
Войти
/
niceSOFT
/
util-linux
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
misc-utils/waitpid.c
294 строки
7 KB
Tobias Stoeckmann
Avoid PRI specifiers in translated messages
15 апр 2026, 14:20
15 апр 2026, 14:20
77712a9
Код
Авторство
О чём код?
/* * waitpid(1) - wait for process termination * * Copyright (C) 2022 Thomas Weißschuh <thomas@t-8ch.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://gnu.org/licenses/>. */ #include <sys/epoll.h> #include <sys/timerfd.h> #include <assert.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <stdbool.h> #include <getopt.h> #include "pidfd-utils.h" #include "c.h" #include "nls.h" #include "xalloc.h" #include "strutils.h" #include "exitcodes.h" #include "timeutils.h" #include "optutils.h" #include "pidutils.h" #define EXIT_TIMEOUT_EXPIRED 3 #define TIMEOUT_SOCKET_IDX UINT64_MAX struct process_info { pid_t pid; int pidfd; uint64_t pidfd_ino; }; /* list of processes specified by PIDs on the command line */ static struct process_info *proc_infos; struct waitpid_control { size_t count; bool allow_exited; bool verbose; struct timespec timeout; }; static void parse_pids_or_err(struct process_info *pinfos, size_t n_strings, char * const *strings) { for (size_t i = 0; i < n_strings; i++) { struct process_info *pi = &pinfos[i]; ul_parse_pid_str_or_err(strings[i], &pi->pid, &pi->pidfd_ino, 0); } } static inline int get_pidfd(const struct waitpid_control *ctl, struct process_info *pi) { int fd; if (pi->pidfd_ino) { fd = ul_get_valid_pidfd(pi->pid, pi->pidfd_ino); if (fd < 0 && ctl->verbose) warnx(_("pidfd inode %ju not found for PID %d"), (uintmax_t) pi->pidfd_ino, pi->pid); } else { fd = pidfd_open(pi->pid, 0); } return fd; } static void open_pidfds_or_err(const struct waitpid_control *ctl, struct process_info *pinfos, size_t n_pids) { for (size_t i = 0; i < n_pids; i++) { struct process_info *pi = &pinfos[i]; pi->pidfd = get_pidfd(ctl, pi); if (pi->pidfd < 0) { if (ctl->allow_exited && errno == ESRCH) { if (ctl->verbose) warnx(_("PID %d has exited, skipping"), pi->pid); continue; } err_nosys(EXIT_FAILURE, _("could not open PID %d"), pi->pid); } } } static int open_timeoutfd(const struct waitpid_control *ctl) { int fd; struct itimerspec timer = {}; if (!ctl->timeout.tv_sec && !ctl->timeout.tv_nsec) return -1; timer.it_value = ctl->timeout; fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if (fd == -1) err_nosys(EXIT_FAILURE, _("could not create timerfd")); if (timerfd_settime(fd, 0, &timer, NULL)) err_nosys(EXIT_FAILURE, _("could not set timer")); return fd; } static size_t add_listeners(int epll, size_t n_pids, struct process_info *pinfos, int timeoutfd) { size_t skipped = 0; struct epoll_event evt = { .events = EPOLLIN, }; if (timeoutfd != -1) { evt.data.u64 = TIMEOUT_SOCKET_IDX; if (epoll_ctl(epll, EPOLL_CTL_ADD, timeoutfd, &evt)) err_nosys(EXIT_FAILURE, _("could not add timerfd")); } for (size_t i = 0; i < n_pids; i++) { struct process_info *pi = &pinfos[i]; if (pi->pidfd == -1) { skipped++; continue; } evt.data.u64 = i; if (epoll_ctl(epll, EPOLL_CTL_ADD, pi->pidfd, &evt)) err_nosys(EXIT_FAILURE, _("could not add listener")); } return n_pids - skipped; } static void wait_for_exits(const struct waitpid_control *ctl, int epll, size_t active_pids, struct process_info *pinfos) { while (active_pids) { struct epoll_event evt; int ret, fd; ret = epoll_wait(epll, &evt, 1, -1); if (ret == -1) { if (errno == EINTR) continue; else err_nosys(EXIT_FAILURE, _("failure during wait")); } if (evt.data.u64 == TIMEOUT_SOCKET_IDX) { if (ctl->verbose) printf(_("Timeout expired\n")); exit(EXIT_TIMEOUT_EXPIRED); } struct process_info *pi = &pinfos[evt.data.u64]; if (ctl->verbose) printf(_("PID %d finished\n"), pi->pid); assert((size_t) ret <= active_pids); fd = pi->pidfd; epoll_ctl(epll, EPOLL_CTL_DEL, fd, NULL); close(fd); active_pids -= ret; } } static void __attribute__((__noreturn__)) usage(void) { FILE *out = stdout; char *pid_arg = NULL; #ifdef USE_PIDFD_INO_SUPPORT pid_arg = "PID[:inode]"; #else pid_arg = "PID"; #endif fputs(USAGE_HEADER, out); fprintf(out, _(" %s [options] %s...\n"), program_invocation_short_name, pid_arg); fputs(USAGE_OPTIONS, out); fputs(_(" -v, --verbose be more verbose\n"), out); fputs(_(" -t, --timeout=<timeout> wait at most timeout seconds\n"), out); fputs(_(" -e, --exited allow exited PIDs\n"), out); fputs(_(" -c, --count=<count> number of process exits to wait for\n"), out); fputs(USAGE_SEPARATOR, out); fprintf(out, USAGE_HELP_OPTIONS(25)); fprintf(out, USAGE_MAN_TAIL("waitpid(1)")); exit(EXIT_SUCCESS); } static int parse_options(struct waitpid_control *ctl, int argc, char **argv) { int c; static const struct option longopts[] = { { "verbose", no_argument, NULL, 'v' }, { "timeout", required_argument, NULL, 't' }, { "exited", no_argument, NULL, 'e' }, { "count", required_argument, NULL, 'c' }, { "version", no_argument, NULL, 'V' }, { "help", no_argument, NULL, 'h' }, { 0 } }; static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ { 'c', 'e' }, { 0 } }; int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; while ((c = getopt_long (argc, argv, "vVht:c:e", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); switch (c) { case 'v': ctl->verbose = true; break; case 't': strtotimespec_or_err(optarg, &ctl->timeout, _("invalid timeout")); break; case 'e': ctl->allow_exited = true; break; case 'c': ctl->count = str2num_or_err(optarg, 10, _("invalid count"), 1, INT64_MAX); break; case 'V': print_version(EXIT_SUCCESS); case 'h': usage(); default: errtryhelp(EXIT_FAILURE); } } return optind; } int main(int argc, char **argv) { int pid_idx, epoll, timeoutfd; size_t n_pids, active_pids; struct waitpid_control ctl = { .allow_exited = false, .verbose = false, }; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); pid_idx = parse_options(&ctl, argc, argv); n_pids = argc - pid_idx; if (!n_pids) errx(EXIT_FAILURE, _("no PIDs specified")); if (ctl.count && ctl.count > n_pids) errx(EXIT_FAILURE, _("can't wait for %zu of %zu PIDs"), ctl.count, n_pids); proc_infos = xcalloc(n_pids, sizeof(*proc_infos)); parse_pids_or_err(proc_infos, argc - pid_idx, argv + pid_idx); open_pidfds_or_err(&ctl, proc_infos, n_pids); timeoutfd = open_timeoutfd(&ctl); epoll = epoll_create(n_pids); if (epoll == -1) err_nosys(EXIT_FAILURE, _("could not create epoll")); active_pids = add_listeners(epoll, n_pids, proc_infos, timeoutfd); if (ctl.count) active_pids = min(active_pids, ctl.count); wait_for_exits(&ctl, epoll, active_pids, proc_infos); }