Merge pull request #12033 from fbuihuu/watched-process-improvments
Watched process improvments
This commit is contained in:
@@ -934,6 +934,20 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pid_is_my_child(pid_t pid) {
|
||||
pid_t ppid;
|
||||
int r;
|
||||
|
||||
if (pid <= 1)
|
||||
return false;
|
||||
|
||||
r = get_process_ppid(pid, &ppid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return ppid == getpid_cached();
|
||||
}
|
||||
|
||||
bool pid_is_unwaited(pid_t pid) {
|
||||
/* Checks whether a PID is still valid at all, including a zombie */
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value);
|
||||
|
||||
bool pid_is_alive(pid_t pid);
|
||||
bool pid_is_unwaited(pid_t pid);
|
||||
int pid_is_my_child(pid_t pid);
|
||||
int pid_from_same_root_fs(pid_t pid);
|
||||
|
||||
bool is_main_thread(void);
|
||||
|
||||
+3
-6
@@ -2269,7 +2269,7 @@ void unit_prune_cgroup(Unit *u) {
|
||||
|
||||
int unit_search_main_pid(Unit *u, pid_t *ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
pid_t pid = 0, npid, mypid;
|
||||
pid_t pid = 0, npid;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
@@ -2282,15 +2282,12 @@ int unit_search_main_pid(Unit *u, pid_t *ret) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
mypid = getpid_cached();
|
||||
while (cg_read_pid(f, &npid) > 0) {
|
||||
pid_t ppid;
|
||||
|
||||
if (npid == pid)
|
||||
continue;
|
||||
|
||||
/* Ignore processes that aren't our kids */
|
||||
if (get_process_ppid(npid, &ppid) >= 0 && ppid != mypid)
|
||||
if (pid_is_my_child(npid) == 0)
|
||||
continue;
|
||||
|
||||
if (pid != 0)
|
||||
@@ -2322,7 +2319,7 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) {
|
||||
pid_t pid;
|
||||
|
||||
while ((r = cg_read_pid(f, &pid)) > 0) {
|
||||
r = unit_watch_pid(u, pid);
|
||||
r = unit_watch_pid(u, pid, false);
|
||||
if (r < 0 && ret >= 0)
|
||||
ret = r;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ static int bus_scope_set_transient_property(
|
||||
return r;
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
r = unit_watch_pid(UNIT(s), pid);
|
||||
r = unit_watch_pid(UNIT(s), pid, false);
|
||||
if (r < 0 && r != -EEXIST)
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -2129,6 +2129,16 @@ void manager_clear_jobs(Manager *m) {
|
||||
job_finish_and_invalidate(j, JOB_CANCELED, false, false);
|
||||
}
|
||||
|
||||
void manager_unwatch_pid(Manager *m, pid_t pid) {
|
||||
assert(m);
|
||||
|
||||
/* First let's drop the unit keyed as "pid". */
|
||||
(void) hashmap_remove(m->watch_pids, PID_TO_PTR(pid));
|
||||
|
||||
/* Then, let's also drop the array keyed by -pid. */
|
||||
free(hashmap_remove(m->watch_pids, PID_TO_PTR(-pid)));
|
||||
}
|
||||
|
||||
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) {
|
||||
Manager *m = userdata;
|
||||
Job *j;
|
||||
|
||||
@@ -437,6 +437,8 @@ int manager_get_dump_string(Manager *m, char **ret);
|
||||
|
||||
void manager_clear_jobs(Manager *m);
|
||||
|
||||
void manager_unwatch_pid(Manager *m, pid_t pid);
|
||||
|
||||
unsigned manager_dispatch_load_queue(Manager *m);
|
||||
|
||||
int manager_default_environment(Manager *m);
|
||||
|
||||
+2
-3
@@ -704,7 +704,7 @@ static int mount_coldplug(Unit *u) {
|
||||
pid_is_unwaited(m->control_pid) &&
|
||||
MOUNT_STATE_WITH_PROCESS(new_state)) {
|
||||
|
||||
r = unit_watch_pid(UNIT(m), m->control_pid);
|
||||
r = unit_watch_pid(UNIT(m), m->control_pid, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -810,9 +810,8 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_watch_pid(UNIT(m), pid);
|
||||
r = unit_watch_pid(UNIT(m), pid, true);
|
||||
if (r < 0)
|
||||
/* FIXME: we need to do something here */
|
||||
return r;
|
||||
|
||||
*_pid = pid;
|
||||
|
||||
+4
-4
@@ -333,11 +333,11 @@ static int scope_start(Unit *u) {
|
||||
(void) unit_reset_cpu_accounting(u);
|
||||
(void) unit_reset_ip_accounting(u);
|
||||
|
||||
unit_export_state_files(UNIT(s));
|
||||
unit_export_state_files(u);
|
||||
|
||||
r = unit_attach_pids_to_cgroup(u, UNIT(s)->pids, NULL);
|
||||
r = unit_attach_pids_to_cgroup(u, u->pids, NULL);
|
||||
if (r < 0) {
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to add PIDs to scope's control group: %m");
|
||||
log_unit_warning_errno(u, r, "Failed to add PIDs to scope's control group: %m");
|
||||
scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
|
||||
return r;
|
||||
}
|
||||
@@ -347,7 +347,7 @@ static int scope_start(Unit *u) {
|
||||
scope_set_state(s, SCOPE_RUNNING);
|
||||
|
||||
/* Start watching the PIDs currently in the scope */
|
||||
(void) unit_enqueue_rewatch_pids(UNIT(s));
|
||||
(void) unit_enqueue_rewatch_pids(u);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+19
-20
@@ -143,8 +143,6 @@ static void service_unwatch_pid_file(Service *s) {
|
||||
}
|
||||
|
||||
static int service_set_main_pid(Service *s, pid_t pid) {
|
||||
pid_t ppid;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (pid <= 1)
|
||||
@@ -163,12 +161,10 @@ static int service_set_main_pid(Service *s, pid_t pid) {
|
||||
|
||||
s->main_pid = pid;
|
||||
s->main_pid_known = true;
|
||||
s->main_pid_alien = pid_is_my_child(pid) == 0;
|
||||
|
||||
if (get_process_ppid(pid, &ppid) >= 0 && ppid != getpid_cached()) {
|
||||
if (s->main_pid_alien)
|
||||
log_unit_warning(UNIT(s), "Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", pid);
|
||||
s->main_pid_alien = true;
|
||||
} else
|
||||
s->main_pid_alien = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -994,7 +990,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_watch_pid(UNIT(s), pid);
|
||||
r = unit_watch_pid(UNIT(s), pid, false);
|
||||
if (r < 0) /* FIXME: we need to do something here */
|
||||
return log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid);
|
||||
|
||||
@@ -1024,7 +1020,7 @@ static void service_search_main_pid(Service *s) {
|
||||
if (service_set_main_pid(s, pid) < 0)
|
||||
return;
|
||||
|
||||
r = unit_watch_pid(UNIT(s), pid);
|
||||
r = unit_watch_pid(UNIT(s), pid, false);
|
||||
if (r < 0)
|
||||
/* FIXME: we need to do something here */
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid);
|
||||
@@ -1158,7 +1154,7 @@ static int service_coldplug(Unit *u) {
|
||||
SERVICE_RUNNING, SERVICE_RELOAD,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
|
||||
r = unit_watch_pid(UNIT(s), s->main_pid);
|
||||
r = unit_watch_pid(UNIT(s), s->main_pid, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -1170,7 +1166,7 @@ static int service_coldplug(Unit *u) {
|
||||
SERVICE_RELOAD,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
|
||||
r = unit_watch_pid(UNIT(s), s->control_pid);
|
||||
r = unit_watch_pid(UNIT(s), s->control_pid, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -1570,8 +1566,8 @@ static int service_spawn(
|
||||
s->exec_fd_event_source = TAKE_PTR(exec_fd_source);
|
||||
s->exec_fd_hot = false;
|
||||
|
||||
r = unit_watch_pid(UNIT(s), pid);
|
||||
if (r < 0) /* FIXME: we need to do something here */
|
||||
r = unit_watch_pid(UNIT(s), pid, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*_pid = pid;
|
||||
@@ -3475,8 +3471,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
if (main_pid_good(s) <= 0)
|
||||
service_enter_stop_post(s, f);
|
||||
|
||||
/* If there is still a service
|
||||
* process around, wait until
|
||||
/* If there is still a service process around, wait until
|
||||
* that one quit, too */
|
||||
break;
|
||||
|
||||
@@ -3498,10 +3493,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
if (notify_dbus)
|
||||
unit_add_to_dbus_queue(u);
|
||||
|
||||
/* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to watch,
|
||||
* under the assumption that we'll sooner or later get a SIGCHLD for them, as the original process we watched
|
||||
* was probably the parent of them, and they are hence now our children. */
|
||||
(void) unit_enqueue_rewatch_pids(u);
|
||||
/* We watch the main/control process otherwise we can't retrieve the unit they
|
||||
* belong to with cgroupv1. But if they are not our direct child, we won't get a
|
||||
* SIGCHLD for them. Therefore we need to look for others to watch so we can
|
||||
* detect when the cgroup becomes empty. Note that the control process is always
|
||||
* our child so it's pointless to watch all other processes. */
|
||||
if (!control_pid_good(s))
|
||||
if (!s->main_pid_known || s->main_pid_alien)
|
||||
(void) unit_enqueue_rewatch_pids(u);
|
||||
}
|
||||
|
||||
static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) {
|
||||
@@ -3709,7 +3708,7 @@ static void service_notify_message(
|
||||
if (r > 0) {
|
||||
service_set_main_pid(s, new_main_pid);
|
||||
|
||||
r = unit_watch_pid(UNIT(s), new_main_pid);
|
||||
r = unit_watch_pid(UNIT(s), new_main_pid, false);
|
||||
if (r < 0)
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", new_main_pid);
|
||||
|
||||
@@ -3927,7 +3926,7 @@ static void service_bus_name_owner_change(
|
||||
log_unit_debug(u, "D-Bus name %s is now owned by process " PID_FMT, name, pid);
|
||||
|
||||
service_set_main_pid(s, pid);
|
||||
unit_watch_pid(UNIT(s), pid);
|
||||
unit_watch_pid(UNIT(s), pid, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -1852,7 +1852,7 @@ static int socket_coldplug(Unit *u) {
|
||||
SOCKET_FINAL_SIGTERM,
|
||||
SOCKET_FINAL_SIGKILL)) {
|
||||
|
||||
r = unit_watch_pid(UNIT(s), s->control_pid);
|
||||
r = unit_watch_pid(UNIT(s), s->control_pid, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1938,9 +1938,8 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_watch_pid(UNIT(s), pid);
|
||||
r = unit_watch_pid(UNIT(s), pid, true);
|
||||
if (r < 0)
|
||||
/* FIXME: we need to do something here */
|
||||
return r;
|
||||
|
||||
*_pid = pid;
|
||||
@@ -2009,7 +2008,7 @@ static int socket_chown(Socket *s, pid_t *_pid) {
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
r = unit_watch_pid(UNIT(s), pid);
|
||||
r = unit_watch_pid(UNIT(s), pid, true);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
|
||||
+2
-3
@@ -550,7 +550,7 @@ static int swap_coldplug(Unit *u) {
|
||||
pid_is_unwaited(s->control_pid) &&
|
||||
SWAP_STATE_WITH_PROCESS(new_state)) {
|
||||
|
||||
r = unit_watch_pid(UNIT(s), s->control_pid);
|
||||
r = unit_watch_pid(UNIT(s), s->control_pid, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -657,9 +657,8 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = unit_watch_pid(UNIT(s), pid);
|
||||
r = unit_watch_pid(UNIT(s), pid, true);
|
||||
if (r < 0)
|
||||
/* FIXME: we need to do something here */
|
||||
goto fail;
|
||||
|
||||
*_pid = pid;
|
||||
|
||||
+7
-1
@@ -2525,7 +2525,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
|
||||
unit_add_to_gc_queue(u);
|
||||
}
|
||||
|
||||
int unit_watch_pid(Unit *u, pid_t pid) {
|
||||
int unit_watch_pid(Unit *u, pid_t pid, bool exclusive) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
@@ -2533,6 +2533,12 @@ int unit_watch_pid(Unit *u, pid_t pid) {
|
||||
|
||||
/* Watch a specific PID */
|
||||
|
||||
/* Caller might be sure that this PID belongs to this unit only. Let's take this
|
||||
* opportunity to remove any stalled references to this PID as they can be created
|
||||
* easily (when watching a process which is not our direct child). */
|
||||
if (exclusive)
|
||||
manager_unwatch_pid(u->manager, pid);
|
||||
|
||||
r = set_ensure_allocated(&u->pids, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
+1
-1
@@ -678,7 +678,7 @@ typedef enum UnitNotifyFlags {
|
||||
|
||||
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags);
|
||||
|
||||
int unit_watch_pid(Unit *u, pid_t pid);
|
||||
int unit_watch_pid(Unit *u, pid_t pid, bool exclusive);
|
||||
void unit_unwatch_pid(Unit *u, pid_t pid);
|
||||
void unit_unwatch_all_pids(Unit *u);
|
||||
|
||||
|
||||
@@ -42,25 +42,25 @@ int main(int argc, char *argv[]) {
|
||||
assert_se(hashmap_isempty(m->watch_pids));
|
||||
assert_se(manager_get_unit_by_pid(m, 4711) == NULL);
|
||||
|
||||
assert_se(unit_watch_pid(a, 4711) >= 0);
|
||||
assert_se(unit_watch_pid(a, 4711, false) >= 0);
|
||||
assert_se(manager_get_unit_by_pid(m, 4711) == a);
|
||||
|
||||
assert_se(unit_watch_pid(a, 4711) >= 0);
|
||||
assert_se(unit_watch_pid(a, 4711, false) >= 0);
|
||||
assert_se(manager_get_unit_by_pid(m, 4711) == a);
|
||||
|
||||
assert_se(unit_watch_pid(b, 4711) >= 0);
|
||||
assert_se(unit_watch_pid(b, 4711, false) >= 0);
|
||||
u = manager_get_unit_by_pid(m, 4711);
|
||||
assert_se(u == a || u == b);
|
||||
|
||||
assert_se(unit_watch_pid(b, 4711) >= 0);
|
||||
assert_se(unit_watch_pid(b, 4711, false) >= 0);
|
||||
u = manager_get_unit_by_pid(m, 4711);
|
||||
assert_se(u == a || u == b);
|
||||
|
||||
assert_se(unit_watch_pid(c, 4711) >= 0);
|
||||
assert_se(unit_watch_pid(c, 4711, false) >= 0);
|
||||
u = manager_get_unit_by_pid(m, 4711);
|
||||
assert_se(u == a || u == b || u == c);
|
||||
|
||||
assert_se(unit_watch_pid(c, 4711) >= 0);
|
||||
assert_se(unit_watch_pid(c, 4711, false) >= 0);
|
||||
u = manager_get_unit_by_pid(m, 4711);
|
||||
assert_se(u == a || u == b || u == c);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user