diff --git a/init/service.cpp b/init/service.cpp index 574ff52e3..ad42df7d9 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -42,9 +42,11 @@ #if defined(__ANDROID__) #include +#include #include "mount_namespace.h" #include "property_service.h" +#include "selinux.h" #else #include "host_init_stubs.h" #endif @@ -182,7 +184,7 @@ void Service::NotifyStateChange(const std::string& new_state) const { } } -void Service::KillProcessGroup(int signal) { +void Service::KillProcessGroup(int signal, bool report_oneshot) { // If we've already seen a successful result from killProcessGroup*(), then we have removed // the cgroup already and calling these functions a second time will simply result in an error. // This is true regardless of which signal was sent. @@ -190,11 +192,20 @@ void Service::KillProcessGroup(int signal) { if (!process_cgroup_empty_) { LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_ << ") process group..."; + int max_processes = 0; int r; if (signal == SIGTERM) { - r = killProcessGroupOnce(proc_attr_.uid, pid_, signal); + r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes); } else { - r = killProcessGroup(proc_attr_.uid, pid_, signal); + r = killProcessGroup(proc_attr_.uid, pid_, signal, &max_processes); + } + + if (report_oneshot && max_processes > 0) { + LOG(WARNING) + << "Killed " << max_processes + << " additional processes from a oneshot process group for service '" << name_ + << "'. This is new behavior, previously child processes would not be killed in " + "this case."; } if (r == 0) process_cgroup_empty_ = true; @@ -244,7 +255,16 @@ void Service::SetProcessAttributesAndCaps() { void Service::Reap(const siginfo_t& siginfo) { if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) { - KillProcessGroup(SIGKILL); + KillProcessGroup(SIGKILL, false); + } else { + // Legacy behavior from ~2007 until Android R: this else branch did not exist and we did not + // kill the process group in this case. + if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) { + // The new behavior in Android R is to kill these process groups in all cases. The + // 'true' parameter instructions KillProcessGroup() to report a warning message where it + // detects a difference in behavior has occurred. + KillProcessGroup(SIGKILL, true); + } } // Remove any socket resources we may have created. diff --git a/init/service.h b/init/service.h index 272c9f94e..f842b3cd8 100644 --- a/init/service.h +++ b/init/service.h @@ -132,7 +132,7 @@ class Service { private: void NotifyStateChange(const std::string& new_state) const; void StopOrReset(int how); - void KillProcessGroup(int signal); + void KillProcessGroup(int signal, bool report_oneshot = false); void SetProcessAttributesAndCaps(); static unsigned long next_start_order_; diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h index f73ec2d41..0b38b6bce 100644 --- a/libprocessgroup/include/processgroup/processgroup.h +++ b/libprocessgroup/include/processgroup/processgroup.h @@ -47,11 +47,14 @@ void DropTaskProfilesResourceCaching(); // Return 0 and removes the cgroup if there are no longer any processes in it. // Returns -1 in the case of an error occurring or if there are processes still running // even after retrying for up to 200ms. -int killProcessGroup(uid_t uid, int initialPid, int signal); +// If max_processes is not nullptr, it returns the maximum number of processes seen in the cgroup +// during the killing process. Note that this can be 0 if all processes from the process group have +// already been terminated. +int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes = nullptr); // Returns the same as killProcessGroup(), however it does not retry, which means // that it only returns 0 in the case that the cgroup exists and it contains no processes. -int killProcessGroupOnce(uid_t uid, int initialPid, int signal); +int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes = nullptr); int createProcessGroup(uid_t uid, int initialPid, bool memControl = false); diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp index 7b6dde2fc..6272664bb 100644 --- a/libprocessgroup/processgroup.cpp +++ b/libprocessgroup/processgroup.cpp @@ -301,7 +301,8 @@ static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid, return feof(fd.get()) ? processes : -1; } -static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) { +static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries, + int* max_processes) { std::string cpuacct_path; std::string memory_path; @@ -316,9 +317,16 @@ static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + if (max_processes != nullptr) { + *max_processes = 0; + } + int retry = retries; int processes; while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) { + if (max_processes != nullptr && processes > *max_processes) { + *max_processes = processes; + } LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid; if (retry > 0) { std::this_thread::sleep_for(5ms); @@ -359,12 +367,12 @@ static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) } } -int killProcessGroup(uid_t uid, int initialPid, int signal) { - return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/); +int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes) { + return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/, max_processes); } -int killProcessGroupOnce(uid_t uid, int initialPid, int signal) { - return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/); +int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes) { + return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes); } int createProcessGroup(uid_t uid, int initialPid, bool memControl) {