udev: several trivial cleanups (#37059)
This commit is contained in:
commit
4dca06cba9
|
@ -110,7 +110,7 @@ _udevadm() {
|
|||
)
|
||||
|
||||
local verbs=(info trigger settle control monitor test-builtin test verify cat wait lock)
|
||||
local builtins=(blkid btrfs factory_reset hwdb input_id keyboard kmod net_driver net_id net_setup_link path_id uaccess usb_id)
|
||||
local builtins=(blkid btrfs dissect_image factory_reset hwdb input_id keyboard kmod net_driver net_id net_setup_link path_id uaccess usb_id)
|
||||
|
||||
for ((i=0; i < COMP_CWORD; i++)); do
|
||||
if __contains_word "${COMP_WORDS[i]}" "${verbs[@]}"; then
|
||||
|
|
|
@ -103,7 +103,7 @@ _udevadm_test-builtin(){
|
|||
'(- *)'{-h,--help}'[Print help]' \
|
||||
'(- *)'{-V,--version}'[Print version of the program]' \
|
||||
'--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
|
||||
'*::builtins:(blkid btrfs factory_reset hwdb input_id keyboard kmod net_driver net_id net_setup_link path_id uaccess usb_id)'
|
||||
'*::builtins:(blkid btrfs dissect_image factory_reset hwdb input_id keyboard kmod net_driver net_id net_setup_link path_id uaccess usb_id)'
|
||||
elif (( CURRENT == 3 )); then
|
||||
_arguments \
|
||||
'--action=[The action string.]:actions:(add change remove move online offline bind unbind)' \
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#define WORKER_NUM_MAX UINT64_C(2048)
|
||||
|
||||
static int default_log_level = LOG_INFO;
|
||||
static bool arg_debug = false;
|
||||
bool arg_daemonize = false;
|
||||
|
||||
|
@ -279,7 +280,7 @@ static void manager_merge_config_log_level(Manager *manager) {
|
|||
if (manager->config.trace)
|
||||
manager->config.log_level = LOG_DEBUG;
|
||||
else
|
||||
MERGE_NON_NEGATIVE(log_level, log_get_max_level());
|
||||
MERGE_NON_NEGATIVE(log_level, default_log_level);
|
||||
}
|
||||
|
||||
static void manager_merge_config(Manager *manager) {
|
||||
|
@ -441,6 +442,8 @@ int manager_load(Manager *manager, int argc, char *argv[]) {
|
|||
|
||||
assert(manager);
|
||||
|
||||
default_log_level = log_get_max_level();
|
||||
|
||||
manager_parse_udev_config(&manager->config_by_udev_conf);
|
||||
|
||||
r = parse_argv(argc, argv, &manager->config_by_command);
|
||||
|
|
|
@ -160,47 +160,23 @@ Manager* manager_free(Manager *manager) {
|
|||
return mfree(manager);
|
||||
}
|
||||
|
||||
static int on_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata);
|
||||
Manager* manager_new(void) {
|
||||
Manager *manager;
|
||||
|
||||
static int worker_new(Worker **ret, Manager *manager, sd_device_monitor *worker_monitor, PidRef *pidref) {
|
||||
_cleanup_(worker_freep) Worker *worker = NULL;
|
||||
int r;
|
||||
manager = new(Manager, 1);
|
||||
if (!manager)
|
||||
return NULL;
|
||||
|
||||
assert(ret);
|
||||
assert(manager);
|
||||
assert(worker_monitor);
|
||||
assert(pidref);
|
||||
|
||||
/* This takes and invalidates pidref even on some error cases. */
|
||||
|
||||
worker = new(Worker, 1);
|
||||
if (!worker)
|
||||
return -ENOMEM;
|
||||
|
||||
*worker = (Worker) {
|
||||
.pidref = TAKE_PIDREF(*pidref),
|
||||
*manager = (Manager) {
|
||||
.inotify_fd = -EBADF,
|
||||
.config_by_udev_conf = UDEV_CONFIG_INIT,
|
||||
.config_by_command = UDEV_CONFIG_INIT,
|
||||
.config_by_kernel = UDEV_CONFIG_INIT,
|
||||
.config_by_control = UDEV_CONFIG_INIT,
|
||||
.config = UDEV_CONFIG_INIT,
|
||||
};
|
||||
|
||||
r = device_monitor_get_address(worker_monitor, &worker->address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = event_add_child_pidref(manager->event, &worker->child_event_source, &worker->pidref, WEXITED, on_sigchld, worker);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_priority(worker->child_event_source, EVENT_PRIORITY_WORKER_SIGCHLD);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = hashmap_ensure_put(&manager->workers, &worker_hash_op, &worker->pidref, worker);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
worker->manager = manager;
|
||||
|
||||
*ret = TAKE_PTR(worker);
|
||||
return 0;
|
||||
return manager;
|
||||
}
|
||||
|
||||
void manager_kill_workers(Manager *manager, int signo) {
|
||||
|
@ -335,6 +311,94 @@ void manager_reload(Manager *manager, bool force) {
|
|||
notify_ready(manager);
|
||||
}
|
||||
|
||||
static int on_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
|
||||
_cleanup_(worker_freep) Worker *worker = ASSERT_PTR(userdata);
|
||||
sd_device *dev = worker->event ? ASSERT_PTR(worker->event->dev) : NULL;
|
||||
int r;
|
||||
|
||||
assert(si);
|
||||
|
||||
switch (si->si_code) {
|
||||
case CLD_EXITED:
|
||||
if (si->si_status == 0) {
|
||||
log_device_debug(dev, "Worker ["PID_FMT"] exited.", si->si_pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_device_warning(dev, "Worker ["PID_FMT"] exited with return code %i.",
|
||||
si->si_pid, si->si_status);
|
||||
if (!dev)
|
||||
return 0;
|
||||
|
||||
(void) device_add_exit_status(dev, si->si_status);
|
||||
break;
|
||||
|
||||
case CLD_KILLED:
|
||||
case CLD_DUMPED:
|
||||
log_device_warning(dev, "Worker ["PID_FMT"] terminated by signal %i (%s).",
|
||||
si->si_pid, si->si_status, signal_to_string(si->si_status));
|
||||
if (!dev)
|
||||
return 0;
|
||||
|
||||
(void) device_add_signal(dev, si->si_status);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
/* delete state from disk */
|
||||
device_delete_db(dev);
|
||||
device_tag_index(dev, NULL, false);
|
||||
|
||||
r = device_monitor_send(worker->manager->monitor, NULL, dev);
|
||||
if (r < 0)
|
||||
log_device_warning_errno(dev, r, "Failed to broadcast event to libudev listeners, ignoring: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int worker_new(Worker **ret, Manager *manager, sd_device_monitor *worker_monitor, PidRef *pidref) {
|
||||
_cleanup_(worker_freep) Worker *worker = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(manager);
|
||||
assert(worker_monitor);
|
||||
assert(pidref);
|
||||
|
||||
/* This takes and invalidates pidref even on some error cases. */
|
||||
|
||||
worker = new(Worker, 1);
|
||||
if (!worker)
|
||||
return -ENOMEM;
|
||||
|
||||
*worker = (Worker) {
|
||||
.pidref = TAKE_PIDREF(*pidref),
|
||||
};
|
||||
|
||||
r = device_monitor_get_address(worker_monitor, &worker->address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = event_add_child_pidref(manager->event, &worker->child_event_source, &worker->pidref, WEXITED, on_sigchld, worker);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_priority(worker->child_event_source, EVENT_PRIORITY_WORKER_SIGCHLD);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = hashmap_ensure_put(&manager->workers, &worker_hash_op, &worker->pidref, worker);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
worker->manager = manager;
|
||||
|
||||
*ret = TAKE_PTR(worker);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
Event *event = ASSERT_PTR(userdata);
|
||||
|
||||
|
@ -806,6 +870,60 @@ static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int manager_init_device_monitor(Manager *manager, int fd) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
/* This takes passed file descriptor on success. */
|
||||
|
||||
if (fd >= 0) {
|
||||
if (manager->monitor)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EALREADY), "Received multiple netlink socket (%i), ignoring.", fd);
|
||||
|
||||
r = sd_is_socket(fd, AF_NETLINK, SOCK_RAW, /* listening = */ -1);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to verify socket type of %i, ignoring: %m", fd);
|
||||
if (r == 0)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Received invalid netlink socket (%i), ignoring.", fd);
|
||||
} else {
|
||||
if (manager->monitor)
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = device_monitor_new_full(&manager->monitor, MONITOR_GROUP_KERNEL, fd);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to initialize device monitor: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_start_device_monitor(Manager *manager) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
r = manager_init_device_monitor(manager, -EBADF);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sd_device_monitor_set_description(manager->monitor, "manager");
|
||||
|
||||
r = sd_device_monitor_attach_event(manager->monitor, manager->event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach event to device monitor: %m");
|
||||
|
||||
r = sd_device_monitor_start(manager->monitor, on_uevent, manager);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to start device monitor: %m");
|
||||
|
||||
r = sd_event_source_set_priority(sd_device_monitor_get_event_source(manager->monitor), EVENT_PRIORITY_DEVICE_MONITOR);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set priority to device monitor: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_worker_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
@ -882,6 +1000,24 @@ static int on_worker_notify(sd_event_source *s, int fd, uint32_t revents, void *
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int manager_start_worker_notify(Manager *manager) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
assert(manager->event);
|
||||
|
||||
r = notify_socket_prepare(
|
||||
manager->event,
|
||||
EVENT_PRIORITY_WORKER_NOTIFY,
|
||||
on_worker_notify,
|
||||
manager,
|
||||
&manager->worker_notify_socket_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to prepare worker notification socket: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
|
||||
|
@ -898,53 +1034,6 @@ static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int on_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
|
||||
_cleanup_(worker_freep) Worker *worker = ASSERT_PTR(userdata);
|
||||
sd_device *dev = worker->event ? ASSERT_PTR(worker->event->dev) : NULL;
|
||||
int r;
|
||||
|
||||
assert(si);
|
||||
|
||||
switch (si->si_code) {
|
||||
case CLD_EXITED:
|
||||
if (si->si_status == 0) {
|
||||
log_device_debug(dev, "Worker ["PID_FMT"] exited.", si->si_pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_device_warning(dev, "Worker ["PID_FMT"] exited with return code %i.",
|
||||
si->si_pid, si->si_status);
|
||||
if (!dev)
|
||||
return 0;
|
||||
|
||||
(void) device_add_exit_status(dev, si->si_status);
|
||||
break;
|
||||
|
||||
case CLD_KILLED:
|
||||
case CLD_DUMPED:
|
||||
log_device_warning(dev, "Worker ["PID_FMT"] terminated by signal %i (%s).",
|
||||
si->si_pid, si->si_status, signal_to_string(si->si_status));
|
||||
if (!dev)
|
||||
return 0;
|
||||
|
||||
(void) device_add_signal(dev, si->si_status);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
/* delete state from disk */
|
||||
device_delete_db(dev);
|
||||
device_tag_index(dev, NULL, false);
|
||||
|
||||
r = device_monitor_send(worker->manager->monitor, NULL, dev);
|
||||
if (r < 0)
|
||||
log_device_warning_errno(dev, r, "Failed to broadcast event to libudev listeners, ignoring: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_unlink_queue_file(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
|
@ -1009,149 +1098,6 @@ static int on_post(sd_event_source *s, void *userdata) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
Manager* manager_new(void) {
|
||||
Manager *manager;
|
||||
|
||||
manager = new(Manager, 1);
|
||||
if (!manager)
|
||||
return NULL;
|
||||
|
||||
*manager = (Manager) {
|
||||
.inotify_fd = -EBADF,
|
||||
.config_by_udev_conf = UDEV_CONFIG_INIT,
|
||||
.config_by_command = UDEV_CONFIG_INIT,
|
||||
.config_by_kernel = UDEV_CONFIG_INIT,
|
||||
.config_by_control = UDEV_CONFIG_INIT,
|
||||
.config = UDEV_CONFIG_INIT,
|
||||
};
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
static int manager_init_device_monitor(Manager *manager, int fd) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
/* This takes passed file descriptor on success. */
|
||||
|
||||
if (fd >= 0) {
|
||||
if (manager->monitor)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EALREADY), "Received multiple netlink socket (%i), ignoring.", fd);
|
||||
|
||||
r = sd_is_socket(fd, AF_NETLINK, SOCK_RAW, /* listening = */ -1);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to verify socket type of %i, ignoring: %m", fd);
|
||||
if (r == 0)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Received invalid netlink socket (%i), ignoring.", fd);
|
||||
} else {
|
||||
if (manager->monitor)
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = device_monitor_new_full(&manager->monitor, MONITOR_GROUP_KERNEL, fd);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to initialize device monitor: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_listen_fds(Manager *manager) {
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
int n = sd_listen_fds_with_names(/* unset_environment = */ true, &names);
|
||||
if (n < 0)
|
||||
return n;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
int fd = SD_LISTEN_FDS_START + i;
|
||||
|
||||
if (streq(names[i], "varlink"))
|
||||
r = 0; /* The fd will be handled by sd_varlink_server_listen_auto(). */
|
||||
else if (streq(names[i], "systemd-udevd-control.socket"))
|
||||
r = manager_init_ctrl(manager, fd);
|
||||
else if (streq(names[i], "systemd-udevd-kernel.socket"))
|
||||
r = manager_init_device_monitor(manager, fd);
|
||||
else if (streq(names[i], "inotify"))
|
||||
r = manager_init_inotify(manager, fd);
|
||||
else
|
||||
r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received unexpected fd (%s), ignoring.", names[i]);
|
||||
if (r < 0)
|
||||
close_and_notify_warn(fd, names[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_init(Manager *manager) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
r = manager_listen_fds(manager);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to listen on fds: %m");
|
||||
|
||||
_cleanup_free_ char *cgroup = NULL;
|
||||
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to get cgroup, ignoring: %m");
|
||||
else if (endswith(cgroup, "/udev")) { /* If we are in a subcgroup /udev/ we assume it was delegated to us */
|
||||
log_debug("Running in delegated subcgroup '%s'.", cgroup);
|
||||
manager->cgroup = TAKE_PTR(cgroup);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_start_device_monitor(Manager *manager) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
r = manager_init_device_monitor(manager, -EBADF);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sd_device_monitor_set_description(manager->monitor, "manager");
|
||||
|
||||
r = sd_device_monitor_attach_event(manager->monitor, manager->event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach event to device monitor: %m");
|
||||
|
||||
r = sd_device_monitor_start(manager->monitor, on_uevent, manager);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to start device monitor: %m");
|
||||
|
||||
r = sd_event_source_set_priority(sd_device_monitor_get_event_source(manager->monitor), EVENT_PRIORITY_DEVICE_MONITOR);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set priority to device monitor: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_start_worker_notify(Manager *manager) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
assert(manager->event);
|
||||
|
||||
r = notify_socket_prepare(
|
||||
manager->event,
|
||||
EVENT_PRIORITY_WORKER_NOTIFY,
|
||||
on_worker_notify,
|
||||
manager,
|
||||
&manager->worker_notify_socket_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to prepare worker notification socket: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_setup_event(Manager *manager) {
|
||||
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
||||
int r;
|
||||
|
@ -1200,15 +1146,59 @@ static int manager_setup_event(Manager *manager) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int manager_listen_fds(Manager *manager) {
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
int n = sd_listen_fds_with_names(/* unset_environment = */ true, &names);
|
||||
if (n < 0)
|
||||
return n;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
int fd = SD_LISTEN_FDS_START + i;
|
||||
|
||||
if (streq(names[i], "varlink"))
|
||||
r = 0; /* The fd will be handled by sd_varlink_server_listen_auto(). */
|
||||
else if (streq(names[i], "systemd-udevd-control.socket"))
|
||||
r = manager_init_ctrl(manager, fd);
|
||||
else if (streq(names[i], "systemd-udevd-kernel.socket"))
|
||||
r = manager_init_device_monitor(manager, fd);
|
||||
else if (streq(names[i], "inotify"))
|
||||
r = manager_init_inotify(manager, fd);
|
||||
else
|
||||
r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received unexpected fd (%s), ignoring.", names[i]);
|
||||
if (r < 0)
|
||||
close_and_notify_warn(fd, names[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_main(Manager *manager) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
_cleanup_free_ char *cgroup = NULL;
|
||||
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to get cgroup, ignoring: %m");
|
||||
else if (endswith(cgroup, "/udev")) { /* If we are in a subcgroup /udev/ we assume it was delegated to us */
|
||||
log_debug("Running in delegated subcgroup '%s'.", cgroup);
|
||||
manager->cgroup = TAKE_PTR(cgroup);
|
||||
}
|
||||
|
||||
r = manager_setup_event(manager);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_listen_fds(manager);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_start_ctrl(manager);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
|
@ -68,7 +68,6 @@ Manager* manager_new(void);
|
|||
Manager* manager_free(Manager *manager);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
|
||||
int manager_init(Manager *manager);
|
||||
int manager_main(Manager *manager);
|
||||
void manager_reload(Manager *manager, bool force);
|
||||
void manager_exit(Manager *manager);
|
||||
|
|
|
@ -51,10 +51,6 @@ int run_udevd(int argc, char *argv[]) {
|
|||
if (r < 0 && r != -EEXIST)
|
||||
return log_error_errno(r, "Failed to create /run/udev: %m");
|
||||
|
||||
r = manager_init(manager);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_daemonize) {
|
||||
pid_t pid;
|
||||
|
||||
|
|
Loading…
Reference in New Issue