Merge "init: always kill oneshot services' process groups." am: 8fa4d6c382 am: f45bc91373

Change-Id: Ic26dc11fdf9bc4fae50700250fd06ae01d46d856
diff --git a/init/service.cpp b/init/service.cpp
index 574ff52..ad42df7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -42,9 +42,11 @@
 
 #if defined(__ANDROID__)
 #include <ApexProperties.sysprop.h>
+#include <android/api-level.h>
 
 #include "mount_namespace.h"
 #include "property_service.h"
+#include "selinux.h"
 #else
 #include "host_init_stubs.h"
 #endif
@@ -182,7 +184,7 @@
     }
 }
 
-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 @@
     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::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 272c9f9..f842b3c 100644
--- a/init/service.h
+++ b/init/service.h
@@ -132,7 +132,7 @@
   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 f73ec2d..0b38b6b 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -47,11 +47,14 @@
 // 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 7b6dde2..6272664 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -301,7 +301,8 @@
     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 @@
 
     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 @@
     }
 }
 
-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) {