Merge "init: expand arguments of services when they start"
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 65640ad..d2ca44e 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -23,11 +23,6 @@
 #include <stdlib.h>
 #include <unistd.h>
 
-// We only build the affinity WAR code for Linux.
-#if defined(__linux__)
-#include <sched.h>
-#endif
-
 #include <android-base/errors.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -39,21 +34,14 @@
 #include "adb_utils.h"
 #include "transport.h"
 
-#if defined(_WIN32)
-static BOOL WINAPI ctrlc_handler(DWORD type) {
-    // TODO: Consider trying to kill a starting up adb server (if we're in
-    // launch_server) by calling GenerateConsoleCtrlEvent().
-    exit(STATUS_CONTROL_C_EXIT);
-    return TRUE;
-}
-
 static std::string GetLogFilePath() {
+#if defined(_WIN32)
     const char log_name[] = "adb.log";
     WCHAR temp_path[MAX_PATH];
 
     // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
     DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
-    if ((nchars >= arraysize(temp_path)) || (nchars == 0)) {
+    if (nchars >= arraysize(temp_path) || nchars == 0) {
         // If string truncation or some other error.
         fatal("cannot retrieve temporary file path: %s\n",
               android::base::SystemErrorCodeToString(GetLastError()).c_str());
@@ -65,12 +53,12 @@
     }
 
     return temp_path_utf8 + log_name;
-}
 #else
-static std::string GetLogFilePath() {
-    return std::string("/tmp/adb.log");
-}
+    const char* tmp_dir = getenv("TMPDIR");
+    if (tmp_dir == nullptr) tmp_dir = "/tmp";
+    return android::base::StringPrintf("%s/adb.%u.log", tmp_dir, getuid());
 #endif
+}
 
 static void setup_daemon_logging(void) {
     const std::string log_file_path(GetLogFilePath());
@@ -90,6 +78,15 @@
     LOG(INFO) << adb_version();
 }
 
+#if defined(_WIN32)
+static BOOL WINAPI ctrlc_handler(DWORD type) {
+    // TODO: Consider trying to kill a starting up adb server (if we're in
+    // launch_server) by calling GenerateConsoleCtrlEvent().
+    exit(STATUS_CONTROL_C_EXIT);
+    return TRUE;
+}
+#endif
+
 int adb_server_main(int is_daemon, int server_port, int ack_reply_fd) {
 #if defined(_WIN32)
     // adb start-server starts us up with stdout and stderr hooked up to
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 926dbcf..14c26cb 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -21,13 +21,13 @@
 
 #include <dirent.h>
 #include <errno.h>
-#include <log/log.h>
-#include <selinux/android.h>
+#include <linux/xattr.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/xattr.h>
 #include <unistd.h>
 #include <utime.h>
 
@@ -39,6 +39,8 @@
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <log/log.h>
+#include <selinux/android.h>
 
 static bool should_use_fs_config(const std::string& path) {
     // TODO: use fs_config to configure permissions on /data.
@@ -47,11 +49,27 @@
            android::base::StartsWith(path, "/oem/");
 }
 
+static bool update_capabilities(const char* path, uint64_t capabilities) {
+    if (capabilities == 0) {
+        // Ensure we clean up in case the capabilities weren't 0 in the past.
+        removexattr(path, XATTR_NAME_CAPS);
+        return true;
+    }
+
+    vfs_cap_data cap_data = {};
+    cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
+    cap_data.data[0].permitted = (capabilities & 0xffffffff);
+    cap_data.data[0].inheritable = 0;
+    cap_data.data[1].permitted = (capabilities >> 32);
+    cap_data.data[1].inheritable = 0;
+    return setxattr(path, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) != -1;
+}
+
 static bool secure_mkdirs(const std::string& path) {
     uid_t uid = -1;
     gid_t gid = -1;
     unsigned int mode = 0775;
-    uint64_t cap = 0;
+    uint64_t capabilities = 0;
 
     if (path[0] != '/') return false;
 
@@ -62,18 +80,19 @@
         partial_path += path_component;
 
         if (should_use_fs_config(partial_path)) {
-            fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &cap);
+            fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &capabilities);
         }
         if (adb_mkdir(partial_path.c_str(), mode) == -1) {
             if (errno != EEXIST) {
                 return false;
             }
         } else {
-            if (chown(partial_path.c_str(), uid, gid) == -1) {
-                return false;
-            }
+            if (chown(partial_path.c_str(), uid, gid) == -1) return false;
+
             // Not all filesystems support setting SELinux labels. http://b/23530370.
             selinux_android_restorecon(partial_path.c_str(), 0);
+
+            if (!update_capabilities(partial_path.c_str(), capabilities)) return false;
         }
     }
     return true;
@@ -83,8 +102,7 @@
     syncmsg msg;
     msg.stat.id = ID_STAT;
 
-    struct stat st;
-    memset(&st, 0, sizeof(st));
+    struct stat st = {};
     // TODO: add a way to report that the stat failed!
     lstat(path, &st);
     msg.stat.mode = st.st_mode;
@@ -146,8 +164,8 @@
     return SendSyncFail(fd, android::base::StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
 }
 
-static bool handle_send_file(int s, const char* path, uid_t uid,
-                             gid_t gid, mode_t mode, std::vector<char>& buffer, bool do_unlink) {
+static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
+                             mode_t mode, std::vector<char>& buffer, bool do_unlink) {
     syncmsg msg;
     unsigned int timestamp = 0;
 
@@ -178,8 +196,13 @@
 
         // fchown clears the setuid bit - restore it if present.
         // Ignore the result of calling fchmod. It's not supported
-        // by all filesystems. b/12441485
+        // by all filesystems, so we don't check for success. b/12441485
         fchmod(fd, mode);
+
+        if (!update_capabilities(path, capabilities)) {
+            SendSyncFailErrno(s, "update_capabilities failed");
+            goto fail;
+        }
     }
 
     while (true) {
@@ -338,13 +361,13 @@
 
     uid_t uid = -1;
     gid_t gid = -1;
-    uint64_t cap = 0;
+    uint64_t capabilities = 0;
     if (should_use_fs_config(path)) {
         unsigned int broken_api_hack = mode;
-        fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &cap);
+        fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &capabilities);
         mode = broken_api_hack;
     }
-    return handle_send_file(s, path.c_str(), uid, gid, mode, buffer, do_unlink);
+    return handle_send_file(s, path.c_str(), uid, gid, capabilities, mode, buffer, do_unlink);
 }
 
 static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 72554a8..aa32600 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -91,14 +91,12 @@
     FILE* f = fopen(path, "r");
     if (!f) {
         ERROR("Can't open '%s'\n", path);
-        free(key_data);
         return NULL;
     }
 
     if (!fread(key_data, sizeof(key_data), 1, f)) {
         ERROR("Could not read key!\n");
         fclose(f);
-        free(key_data);
         return NULL;
     }
 
@@ -107,7 +105,6 @@
     RSA* key = NULL;
     if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
         ERROR("Could not parse key!\n");
-        free(key_data);
         return NULL;
     }
 
diff --git a/init/readme.txt b/init/readme.txt
index aa372eb..8130806 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -184,6 +184,9 @@
   Write the child's pid to the given files when it forks. Meant for
   cgroup/cpuset usage.
 
+priority <priority>
+  Scheduling priority of the service process. This value has to be in range
+  -20 to 19. Default priority is 0. Priority is set via setpriority().
 
 Triggers
 --------
diff --git a/init/service.cpp b/init/service.cpp
index fad61c6..6e68878 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -17,7 +17,9 @@
 #include "service.h"
 
 #include <fcntl.h>
+#include <sys/resource.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <termios.h>
@@ -29,6 +31,7 @@
 #include <android-base/stringprintf.h>
 #include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
+#include <system/thread_defs.h>
 
 #include <processgroup/processgroup.h>
 
@@ -65,7 +68,7 @@
                  const std::vector<std::string>& args)
     : name_(name), classname_(classname), flags_(0), pid_(0), time_started_(0),
       time_crashed_(0), nr_crashed_(0), uid_(0), gid_(0), seclabel_(""),
-      ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+      ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), priority_(0), args_(args) {
     onrestart_.InitSingleTrigger("onrestart");
 }
 
@@ -74,7 +77,8 @@
                  const std::string& seclabel,  const std::vector<std::string>& args)
     : name_(name), classname_(classname), flags_(flags), pid_(0), time_started_(0),
       time_crashed_(0), nr_crashed_(0), uid_(uid), gid_(gid), supp_gids_(supp_gids),
-      seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+      seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), priority_(0),
+      args_(args) {
     onrestart_.InitSingleTrigger("onrestart");
 }
 
@@ -197,6 +201,19 @@
     return true;
 }
 
+bool Service::HandlePriority(const std::vector<std::string>& args, std::string* err) {
+    priority_ = std::stoi(args[1]);
+
+    if (priority_ < ANDROID_PRIORITY_HIGHEST || priority_ > ANDROID_PRIORITY_LOWEST) {
+        priority_ = 0;
+        *err = StringPrintf("process priority value must be range %d - %d",
+                ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
+        return false;
+    }
+
+    return true;
+}
+
 bool Service::HandleIoprio(const std::vector<std::string>& args, std::string* err) {
     ioprio_pri_ = std::stoul(args[2], 0, 8);
 
@@ -290,6 +307,7 @@
         {"disabled",    {0,     0,    &Service::HandleDisabled}},
         {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
         {"ioprio",      {2,     2,    &Service::HandleIoprio}},
+        {"priority",    {1,     1,    &Service::HandlePriority}},
         {"keycodes",    {1,     kMax, &Service::HandleKeycodes}},
         {"oneshot",     {0,     0,    &Service::HandleOneshot}},
         {"onrestart",   {1,     kMax, &Service::HandleOnrestart}},
@@ -470,6 +488,12 @@
                 _exit(127);
             }
         }
+        if (priority_ != 0) {
+            if (setpriority(PRIO_PROCESS, 0, priority_) != 0) {
+                ERROR("setpriority failed: %s\n", strerror(errno));
+                _exit(127);
+            }
+        }
 
         std::vector<std::string> expanded_args;
         std::vector<char*> strs;
diff --git a/init/service.h b/init/service.h
index d6ce664..dc14f9a 100644
--- a/init/service.h
+++ b/init/service.h
@@ -93,6 +93,7 @@
     pid_t pid() const { return pid_; }
     uid_t uid() const { return uid_; }
     gid_t gid() const { return gid_; }
+    int priority() const { return priority_; }
     const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
     const std::string& seclabel() const { return seclabel_; }
     const std::vector<int>& keycodes() const { return keycodes_; }
@@ -116,6 +117,7 @@
     bool HandleCritical(const std::vector<std::string>& args, std::string* err);
     bool HandleDisabled(const std::vector<std::string>& args, std::string* err);
     bool HandleGroup(const std::vector<std::string>& args, std::string* err);
+    bool HandlePriority(const std::vector<std::string>& args, std::string* err);
     bool HandleIoprio(const std::vector<std::string>& args, std::string* err);
     bool HandleKeycodes(const std::vector<std::string>& args, std::string* err);
     bool HandleOneshot(const std::vector<std::string>& args, std::string* err);
@@ -155,6 +157,7 @@
 
     IoSchedClass ioprio_class_;
     int ioprio_pri_;
+    int priority_;
 
     std::vector<std::string> args_;
 };
diff --git a/init/util.cpp b/init/util.cpp
index 750e040..69f6566 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -489,6 +489,7 @@
      * - will accept $$ as a literal $.
      * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
      *   bad things will happen
+     * - ${x.y:-default} will return default value if property empty.
      */
     while (*src_ptr) {
         const char* c;
@@ -511,6 +512,7 @@
         }
 
         std::string prop_name;
+        std::string def_val;
         if (*c == '{') {
             c++;
             const char* end = strchr(c, '}');
@@ -521,6 +523,11 @@
             }
             prop_name = std::string(c, end);
             c = end + 1;
+            size_t def = prop_name.find(":-");
+            if (def < prop_name.size()) {
+                def_val = prop_name.substr(def + 2);
+                prop_name = prop_name.substr(0, def);
+            }
         } else {
             prop_name = c;
             ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
@@ -535,9 +542,12 @@
 
         std::string prop_val = property_get(prop_name.c_str());
         if (prop_val.empty()) {
-            ERROR("property '%s' doesn't exist while expanding '%s'\n",
-                  prop_name.c_str(), src.c_str());
-            return false;
+            if (def_val.empty()) {
+                ERROR("property '%s' doesn't exist while expanding '%s'\n",
+                      prop_name.c_str(), src.c_str());
+                return false;
+            }
+            prop_val = def_val;
         }
 
         dst->append(prop_val);
diff --git a/libnetutils/dhcptool.c b/libnetutils/dhcptool.c
index 352ac5e..a2d3869 100644
--- a/libnetutils/dhcptool.c
+++ b/libnetutils/dhcptool.c
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <err.h>
 #include <errno.h>
 #include <error.h>
 #include <stdbool.h>
@@ -29,12 +30,14 @@
 
   char* interface = argv[1];
   if (ifc_init()) {
-    error(EXIT_FAILURE, errno, "dhcptool %s: ifc_init failed", interface);
+    err(errno, "dhcptool %s: ifc_init failed", interface);
+    ifc_close();
+    return EXIT_FAILURE;
   }
 
   int rc = do_dhcp(interface);
   if (rc) {
-    error(0, errno, "dhcptool %s: do_dhcp failed", interface);
+    err(errno, "dhcptool %s: do_dhcp failed", interface);
   }
 
   ifc_close();
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index f9f62f8..eae32ce 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -19,6 +19,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <pthread.h>
 
 #include <sys/socket.h>
 #include <sys/select.h>
@@ -57,6 +58,8 @@
 
 static int ifc_ctl_sock = -1;
 static int ifc_ctl_sock6 = -1;
+static pthread_mutex_t ifc_sock_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t ifc_sock6_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 void printerr(char *fmt, ...);
 
 #define DBG 0
@@ -122,6 +125,8 @@
 int ifc_init(void)
 {
     int ret;
+
+    pthread_mutex_lock(&ifc_sock_mutex);
     if (ifc_ctl_sock == -1) {
         ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
         if (ifc_ctl_sock < 0) {
@@ -136,6 +141,7 @@
 
 int ifc_init6(void)
 {
+    pthread_mutex_lock(&ifc_sock6_mutex);
     if (ifc_ctl_sock6 == -1) {
         ifc_ctl_sock6 = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
         if (ifc_ctl_sock6 < 0) {
@@ -152,6 +158,7 @@
         (void)close(ifc_ctl_sock);
         ifc_ctl_sock = -1;
     }
+    pthread_mutex_unlock(&ifc_sock_mutex);
 }
 
 void ifc_close6(void)
@@ -160,6 +167,7 @@
         (void)close(ifc_ctl_sock6);
         ifc_ctl_sock6 = -1;
     }
+    pthread_mutex_unlock(&ifc_sock6_mutex);
 }
 
 static void ifc_init_ifr(const char *name, struct ifreq *ifr)
@@ -553,6 +561,7 @@
     ifc_init();
 
     if (ifc_ctl_sock < 0) {
+        ifc_close();
         return -errno;
     }