Merge "ueventd: only relabel devices if there's a delta"
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/adb/shell_service.cpp b/adb/shell_service.cpp
index e8dad58..b038fda 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -477,8 +477,14 @@
                 // and only fall back on this for unexpected closures.
                 D("protocol FD died, sending SIGHUP to pid %d", pid_);
                 kill(pid_, SIGHUP);
+
+                // We also need to close the pipes connected to the child process
+                // so that if it ignores SIGHUP and continues to write data it
+                // won't fill up the pipe and block.
+                stdinout_sfd_.clear();
+                stderr_sfd_.clear();
             }
-            dead_sfd->reset(-1);
+            dead_sfd->clear();
         }
     }
 }
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index b3e65cb..1b3893f 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -586,6 +586,7 @@
 
         /* mount(2) returned an error, handle the encryptable/formattable case */
         bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
+        bool crypt_footer = false;
         if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
             fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {
             /* top_idx and attempted_idx point at the same partition, but sometimes
@@ -606,8 +607,11 @@
                     ERROR("%s(): %s wouldn't open (%s)\n", __func__,
                           fstab->recs[top_idx].key_loc, strerror(errno));
                 }
+            } else if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
+                !strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
+                crypt_footer = true;
             }
-            if (fs_mgr_do_format(&fstab->recs[top_idx]) == 0) {
+            if (fs_mgr_do_format(&fstab->recs[top_idx], crypt_footer) == 0) {
                 /* Let's replay the mount actions. */
                 i = top_idx - 1;
                 continue;
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
index 6c5b1eb..5011b08 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -32,11 +32,12 @@
 #include "ext4.h"
 #include "make_ext4fs.h"
 #include "fs_mgr_priv.h"
+#include "cryptfs.h"
 
 extern struct fs_info info;     /* magic global from ext4_utils */
 extern void reset_ext4fs_info();
 
-static int format_ext4(char *fs_blkdev, char *fs_mnt_point)
+static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
 {
     uint64_t dev_sz;
     int fd, rc = 0;
@@ -63,6 +64,9 @@
     /* Format the partition using the calculated length */
     reset_ext4fs_info();
     info.len = (off64_t)dev_sz;
+    if (crypt_footer) {
+        info.len -= CRYPT_FOOTER_OFFSET;
+    }
 
     /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
     rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, sehandle, 0, 0, NULL);
@@ -118,7 +122,7 @@
     return rc;
 }
 
-int fs_mgr_do_format(struct fstab_rec *fstab)
+int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
 {
     int rc = -EINVAL;
 
@@ -127,7 +131,7 @@
     if (!strncmp(fstab->fs_type, "f2fs", 4)) {
         rc = format_f2fs(fstab->blk_device);
     } else if (!strncmp(fstab->fs_type, "ext4", 4)) {
-        rc = format_ext4(fstab->blk_device, fstab->mount_point);
+        rc = format_ext4(fstab->blk_device, fstab->mount_point, crypt_footer);
     } else {
         ERROR("File system type '%s' is not supported\n", fstab->fs_type);
     }
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/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index b498618..3c27ede 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -18,6 +18,7 @@
 #define __CORE_FS_MGR_H
 
 #include <stdint.h>
+#include <stdbool.h>
 #include <linux/dm-ioctl.h>
 
 // Magic number at start of verity metadata
@@ -108,7 +109,7 @@
 int fs_mgr_is_nofail(struct fstab_rec *fstab);
 int fs_mgr_swapon_all(struct fstab *fstab);
 
-int fs_mgr_do_format(struct fstab_rec *fstab);
+int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
 
 #ifdef __cplusplus
 }
diff --git a/include/cutils/properties.h b/include/cutils/properties.h
index 24aa224..adf670b 100644
--- a/include/cutils/properties.h
+++ b/include/cutils/properties.h
@@ -109,7 +109,7 @@
     
 int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);    
 
-#if defined(__BIONIC_FORTIFY)
+#if defined(__BIONIC_FORTIFY) && !defined(__clang__)
 
 extern int __property_get_real(const char *, char *, const char *)
     __asm__(__USER_LABEL_PREFIX__ "property_get");
diff --git a/init/Android.mk b/init/Android.mk
index e1a3638..7aa3c3f 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -52,7 +52,7 @@
     service.cpp \
     util.cpp \
 
-LOCAL_STATIC_LIBRARIES := libbase libselinux
+LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup
 LOCAL_MODULE := libinit
 LOCAL_SANITIZE := integer
 LOCAL_CLANG := true
@@ -91,7 +91,6 @@
     libcutils \
     libbase \
     libext4_utils_static \
-    libutils \
     libc \
     libselinux \
     liblog \
@@ -100,7 +99,8 @@
     libc++_static \
     libdl \
     libsparse_static \
-    libz
+    libz \
+    libprocessgroup
 
 # Create symlinks
 LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
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 f5b8b00..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,9 @@
 #include <android-base/stringprintf.h>
 #include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
+#include <system/thread_defs.h>
+
+#include <processgroup/processgroup.h>
 
 #include "action.h"
 #include "init.h"
@@ -63,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");
 }
 
@@ -72,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");
 }
 
@@ -97,7 +103,7 @@
     if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
         NOTICE("Service '%s' (pid %d) killing any children in process group\n",
                name_.c_str(), pid_);
-        kill(-pid_, SIGKILL);
+        killProcessGroup(uid_, pid_, SIGKILL);
     }
 
     // Remove any sockets we may have created.
@@ -195,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);
 
@@ -288,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}},
@@ -468,14 +488,28 @@
                 _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;
-        for (const auto& s : args_) {
-            strs.push_back(const_cast<char*>(s.c_str()));
+        expanded_args.resize(args_.size());
+        strs.push_back(const_cast<char*>(args_[0].c_str()));
+        for (std::size_t i = 1; i < args_.size(); ++i) {
+            if (!expand_props(args_[i], &expanded_args[i])) {
+                ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
+                _exit(127);
+            }
+            strs.push_back(const_cast<char*>(expanded_args[i].c_str()));
         }
         strs.push_back(nullptr);
-        if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
-            ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
+
+        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
+            ERROR("cannot execve('%s'): %s\n", strs[0], strerror(errno));
         }
 
         _exit(127);
@@ -490,6 +524,7 @@
     time_started_ = gettime();
     pid_ = pid;
     flags_ |= SVC_RUNNING;
+    createProcessGroup(uid_, pid_);
 
     if ((flags_ & SVC_EXEC) != 0) {
         INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
@@ -532,7 +567,7 @@
     if (pid_) {
         NOTICE("Sending SIGTERM to service '%s' (pid %d)...\n", name_.c_str(),
                pid_);
-        kill(-pid_, SIGTERM);
+        killProcessGroup(uid_, pid_, SIGTERM);
         NotifyStateChange("stopping");
     }
 }
@@ -583,7 +618,7 @@
 
     if (pid_) {
         NOTICE("Service '%s' is being killed...\n", name_.c_str());
-        kill(-pid_, SIGKILL);
+        killProcessGroup(uid_, pid_, SIGKILL);
         NotifyStateChange("stopping");
     } else {
         NotifyStateChange("stopped");
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/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 01fb50f..1b66a56 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -205,7 +205,7 @@
         android_log_header_t header;
         android_log_event_int_t payload;
     };
-    char buf[sizeof(struct packet) + 8] __aligned(8);
+    alignas(8) char buf[sizeof(struct packet) + 8];
     memset(buf, 0, sizeof(buf));
     struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
     if (((uintptr_t)&buffer->pmsg_header) & 7) {
@@ -281,7 +281,7 @@
         android_log_header_t header;
         android_log_event_int_t payload;
     };
-    char buf[sizeof(struct packet) + 8] __aligned(8);
+    alignas(8) char buf[sizeof(struct packet) + 8];
     memset(buf, 0, sizeof(buf));
     struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
     if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
@@ -357,7 +357,7 @@
         android_log_header_t header;
         android_log_event_int_t payload;
     };
-    char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD] __aligned(8);
+    alignas(8) char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD];
     memset(buf, 0, sizeof(buf));
     struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
     if (((uintptr_t)&buffer->pmsg_header) & 7) {
@@ -430,7 +430,7 @@
         android_log_header_t header;
         android_log_event_int_t payload;
     };
-    char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD] __aligned(8);
+    alignas(8) char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD];
     memset(buf, 0, sizeof(buf));
     struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
     if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
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;
     }
 
diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk
index ee6ba58..87985d4 100644
--- a/libprocessgroup/Android.mk
+++ b/libprocessgroup/Android.mk
@@ -3,7 +3,16 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := processgroup.cpp
 LOCAL_MODULE := libprocessgroup
-LOCAL_SHARED_LIBRARIES := liblog libutils
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := processgroup.cpp
+LOCAL_MODULE := libprocessgroup
+LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_CFLAGS := -Wall -Werror
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 00a0357..da4bb71 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -22,19 +22,18 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+
+#include <chrono>
 #include <memory>
 
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
 
-#include <utils/SystemClock.h>
-
 #include <processgroup/processgroup.h>
 #include "processgroup_priv.h"
 
@@ -250,25 +249,26 @@
 
 int killProcessGroup(uid_t uid, int initialPid, int signal)
 {
-    int processes;
-    const int sleep_us = 5 * 1000;  // 5ms
-    int64_t startTime = android::uptimeMillis();
-    int retry = 40;
+    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
 
+    int retry = 40;
+    int processes;
     while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
         SLOGV("killed %d processes for processgroup %d\n", processes, initialPid);
         if (retry > 0) {
-            usleep(sleep_us);
+            usleep(5 * 1000); // 5ms
             --retry;
         } else {
-            SLOGE("failed to kill %d processes for processgroup %d\n",
-                    processes, initialPid);
+            SLOGE("failed to kill %d processes for processgroup %d\n", processes, initialPid);
             break;
         }
     }
 
-    SLOGV("Killed process group uid %d pid %d in %" PRId64 "ms, %d procs remain", uid, initialPid,
-            android::uptimeMillis()-startTime, processes);
+    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
+
+    SLOGV("Killed process group uid %d pid %d in %dms, %d procs remain", uid, initialPid,
+          static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()),
+          processes);
 
     if (processes == 0) {
         return removeProcessGroup(uid, initialPid);
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 52f49cc..b8232cd 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -27,7 +27,6 @@
 
 #include <android-base/file.h>
 #include <android-base/strings.h>
-#include <cutils/properties.h>
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
@@ -281,10 +280,10 @@
     fprintf(stderr, "options include:\n"
                     "  -s              Set default filter to silent. Equivalent to filterspec '*:S'\n"
                     "  -f <file>, --file=<file>               Log to file. Default is stdout\n"
-                    "  -r <kbytes>, --rotate-kbytes=<kbytes>  Rotate log every kbytes. Requires -f\n"
-                    "                  option. Permits property expansion.\n"
-                    "  -n <count>, --rotate-count=<count>     Sets max number of rotated logs to\n"
-                    "                  <count>, default 4. Permits property expansion.\n"
+                    "  -r <kbytes>, --rotate-kbytes=<kbytes>\n"
+                    "                  Rotate log every kbytes. Requires -f option\n"
+                    "  -n <count>, --rotate-count=<count>\n"
+                    "                  Sets max number of rotated logs to <count>, default 4\n"
                     "  -v <format>, --format=<format>\n"
                     "                  Sets the log print format, where <format> is:\n"
                     "                    brief color epoch long monotonic printable process raw\n"
@@ -318,7 +317,6 @@
                     "                  'system', 'radio', 'events', 'crash', 'default' or 'all'.\n"
                     "                  Multiple -b parameters or comma separated list of buffers are\n"
                     "                  allowed. Buffers interleaved. Default -b main,system,crash.\n"
-                    "                  Permits property expansion.\n"
                     "  -B, --binary    Output the log in binary.\n"
                     "  -S, --statistics                       Output statistics.\n"
                     "  -p, --prune     Print prune white and ~black list. Service is specified as\n"
@@ -336,11 +334,7 @@
                     "                  comes first. Improves efficiency of polling by providing\n"
                     "                  an about-to-wrap wakeup.\n");
 
-    fprintf(stderr,"\nProperty expansion where available, may need to be single quoted to prevent\n"
-                   "shell expansion:\n"
-                   "  ${key}          - Expand string with property value associated with key\n"
-                   "  ${key:-default} - Expand, if property key value clear, use default\n"
-                   "\nfilterspecs are a series of \n"
+    fprintf(stderr,"\nfilterspecs are a series of \n"
                    "  <tag>[:priority]\n\n"
                    "where <tag> is a log component tag (or * for all) and priority is:\n"
                    "  V    Verbose (default for <tag>)\n"
@@ -538,49 +532,6 @@
     return retval;
 }
 
-// Expand multiple flat property references ${<tag>:-default} or ${tag}.
-//
-// ToDo: Do we permit nesting?
-//   ${persist.logcat.something:-${ro.logcat.something:-maybesomething}}
-// For now this will result in a syntax error for caller and is acceptable.
-//
-std::string expand(const char *str)
-{
-  std::string retval(str);
-
-  // Caller has no use for ${, } or :- as literals so no use for escape
-  // character. Result expectations are a number or a string, with validity
-  // checking for both in caller. Recursive expansion or other syntax errors
-  // will result in content caller can not obviously tolerate, error must
-  // report substring if applicable, expanded and original content (if
-  // different) so that it will be clear to user what they did wrong.
-  for (size_t pos; (pos = retval.find("${")) != std::string::npos; ) {
-    size_t epos = retval.find("}", pos + 2);
-    if (epos == std::string::npos) {
-      break; // Caller will error out, showing this unexpanded.
-    }
-    size_t def = retval.find(":-", pos + 2);
-    if (def >= epos) {
-      def = std::string::npos;
-    }
-    std::string default_value("");
-    std::string key;
-    if (def == std::string::npos) {
-      key = retval.substr(pos + 2, epos - (pos + 2));
-    } else {
-      key = retval.substr(pos + 2, def - (pos + 2));
-      default_value = retval.substr(def + 2, epos - (def + 2));
-    }
-    char value[PROPERTY_VALUE_MAX];
-    property_get(key.c_str(), value, default_value.c_str());
-    // Caller will error out, syntactically empty content at this point
-    // will not be tolerated as expected.
-    retval.replace(pos, epos - pos + 1, value);
-  }
-
-  return retval;
-}
-
 } /* namespace android */
 
 
@@ -812,35 +763,23 @@
 
             case 'b': {
                 unsigned idMask = 0;
-                std::string expanded = expand(optarg);
-                std::istringstream copy(expanded);
-                std::string token;
-                // wish for strtok and ",:; \t\n\r\f" for hidden flexibility
-                while (std::getline(copy, token, ',')) { // settle for ","
-                    if (token.compare("default") == 0) {
+                while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
+                    if (strcmp(optarg, "default") == 0) {
                         idMask |= (1 << LOG_ID_MAIN) |
                                   (1 << LOG_ID_SYSTEM) |
                                   (1 << LOG_ID_CRASH);
-                    } else if (token.compare("all") == 0) {
+                    } else if (strcmp(optarg, "all") == 0) {
                         idMask = (unsigned)-1;
                     } else {
-                        log_id_t log_id = android_name_to_log_id(token.c_str());
+                        log_id_t log_id = android_name_to_log_id(optarg);
                         const char *name = android_log_id_to_name(log_id);
 
-                        if (token.compare(name) != 0) {
-                            bool strDifferent = expanded.compare(token);
-                            if (expanded.compare(optarg)) {
-                                expanded += " expanded from ";
-                                expanded += optarg;
-                            }
-                            if (strDifferent) {
-                                expanded = token + " within " + expanded;
-                            }
-                            logcat_panic(true, "unknown buffer -b %s\n",
-                                         expanded.c_str());
+                        if (strcmp(name, optarg) != 0) {
+                            logcat_panic(true, "unknown buffer %s\n", optarg);
                         }
                         idMask |= (1 << log_id);
                     }
+                    optarg = NULL;
                 }
 
                 for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
@@ -895,36 +834,22 @@
                 g_outputFileName = optarg;
             break;
 
-            case 'r': {
-                std::string expanded = expand(optarg);
-                if (!getSizeTArg(expanded.c_str(), &g_logRotateSizeKBytes, 1)) {
-                    if (expanded.compare(optarg)) {
-                        expanded += " expanded from ";
-                        expanded += optarg;
-                    }
-                    logcat_panic(true, "Invalid parameter -r %s\n",
-                                 expanded.c_str());
+            case 'r':
+                if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
+                    logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
                 }
-            }
             break;
 
-            case 'n': {
-                std::string expanded = expand(optarg);
-                if (!getSizeTArg(expanded.c_str(), &g_maxRotatedLogs, 1)) {
-                    if (expanded.compare(optarg)) {
-                        expanded += " expanded from ";
-                        expanded += optarg;
-                    }
-                    logcat_panic(true, "Invalid parameter -n %s\n",
-                                 expanded.c_str());
+            case 'n':
+                if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
+                    logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
                 }
-            }
             break;
 
             case 'v':
                 err = setLogFormat (optarg);
                 if (err < 0) {
-                    logcat_panic(true, "Invalid parameter -v %s\n", optarg);
+                    logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
                 }
                 hasSetLogFormat |= err;
             break;
diff --git a/logcat/logpersist b/logcat/logpersist
index e448456..bd465c8 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -28,7 +28,7 @@
     -b|--buffer) buffer="${2}" ; shift ;;
     -h|--help|*)
       LEAD_SPACE_="`echo ${progname%.*} | tr '[ -~]' ' '`"
-      echo "${progname%.*}.cat             - dump current ${service%d} logs"
+      echo "${progname%.*}.cat             - dump current ${service} logs"
       echo "${progname%.*}.start [--size=<size_in_kb>] [--buffer=<buffers>] [--clear]"
       echo "${LEAD_SPACE_}                 - start ${service} service"
       echo "${progname%.*}.stop [--clear]  - stop ${service} service"
@@ -113,8 +113,8 @@
   getprop ${property}
   # 20ms done, to permit process feedback check
   sleep 1
-  # also generate an error return code if not found running, bonus
-  ps -t | grep "${data##*/}.*${service%d}"
+  # also generate an error return code if not found running
+  pgrep -u ${data##*/} ${service%d}
   ;;
 *.stop)
   if [ -n "${size}${buffer}" ]; then
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 3bf8a0b..a28664e 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -56,6 +56,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog libcutils
+LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 920d504..d46e9ff 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -25,7 +25,6 @@
 
 #include <memory>
 
-#include <cutils/properties.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
 #include <log/logger.h>
@@ -426,14 +425,6 @@
       "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
 }
 
-// duplicate test for get_size, but use test.logcat.buffer property
-TEST(logcat, property_expand) {
-    property_set("test.logcat.buffer", "radio,events");
-    EXPECT_EQ(4, get_groups(
-      "logcat -v brief -b 'system,${test.logcat.buffer:-bogo},main' -g 2>/dev/null"));
-    property_set("test.logcat.buffer", "");
-}
-
 TEST(logcat, bad_buffer) {
     ASSERT_EQ(0, get_groups(
       "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 39dd227..61b7fd8 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -48,7 +48,7 @@
         + LOGGER_ENTRY_MAX_PAYLOAD];
     struct iovec iov = { buffer, sizeof(buffer) };
 
-    char control[CMSG_SPACE(sizeof(struct ucred))] __aligned(4);
+    alignas(4) char control[CMSG_SPACE(sizeof(struct ucred))];
     struct msghdr hdr = {
         NULL,
         0,
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index ba04364..feeffda 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -32,7 +32,6 @@
 
 OUR_TOOLS := \
     getevent \
-    ioctl \
     log \
     nandread \
     newfs_msdos \
diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c
deleted file mode 100644
index 093e467..0000000
--- a/toolbox/ioctl.c
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 2008, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name of Google, Inc. nor the names of its contributors
- *    may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <errno.h>
-#include <error.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-
-static void usage() {
-    fprintf(stderr, "%s [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>\n"
-            "  -l <length>   Length of io buffer\n"
-            "  -a <argsize>  Size of each argument (1-8)\n"
-            "  -r            Open device in read only mode\n"
-            "  -d            Direct argument (no iobuffer)\n"
-            "  -h            Print help\n", getprogname());
-    exit(1);
-}
-
-static int xstrtoi(const char* s, const char* what) {
-    char* endp;
-    errno = 0;
-    long result = strtol(s, &endp, 0);
-    if (errno != 0 || *endp != '\0') {
-        error(1, errno, "couldn't parse %s '%s'", what, s);
-    }
-    if (result > INT_MAX || result < INT_MIN) {
-        error(1, errno, "%s '%s' out of range", what, s);
-    }
-    return result;
-}
-
-int ioctl_main(int argc, char* argv[]) {
-    int read_only = 0;
-    int length = -1;
-    int arg_size = 4;
-    int direct_arg = 0;
-
-    void *ioctl_args = NULL;
-    uint8_t *ioctl_argp;
-    uint8_t *ioctl_argp_save = NULL;
-    int rem;
-
-    int c;
-    while ((c = getopt(argc, argv, "rdl:a:h")) != -1) {
-        switch (c) {
-        case 'r':
-            read_only = 1;
-            break;
-        case 'd':
-            direct_arg = 1;
-            break;
-        case 'l':
-            length = xstrtoi(optarg, "length");
-            break;
-        case 'a':
-            arg_size = xstrtoi(optarg, "argument size");
-            break;
-        case 'h':
-            usage();
-            break;
-        default:
-            error(1, 0, "invalid option -%c", optopt);
-        }
-    }
-
-    if (optind + 2 > argc) {
-        usage();
-    }
-
-    const char* device = argv[optind];
-    int fd;
-    if (strcmp(device, "-") == 0) {
-        fd = STDIN_FILENO;
-    } else {
-        fd = open(device, read_only ? O_RDONLY : (O_RDWR | O_SYNC));
-        if (fd == -1) {
-            error(1, errno, "cannot open %s", argv[optind]);
-        }
-    }
-    optind++;
-
-    // IOCTL(2) wants second parameter as a signed int.
-    // Let's let the user specify either negative numbers or large positive
-    // numbers, for the case where ioctl number is larger than INT_MAX.
-    errno = 0;
-    char* endp;
-    int ioctl_nr = UINT_MAX & strtoll(argv[optind], &endp, 0);
-    if (errno != 0 || *endp != '\0') {
-        error(1, errno, "couldn't parse ioctl number '%s'", argv[optind]);
-    }
-    optind++;
-
-    if(direct_arg) {
-        arg_size = 4;
-        length = 4;
-    }
-
-    if(length < 0) {
-        length = (argc - optind) * arg_size;
-    }
-    if(length) {
-        ioctl_args = calloc(1, length);
-
-        ioctl_argp_save = ioctl_argp = ioctl_args;
-        rem = length;
-        while (optind < argc) {
-            uint64_t tmp = strtoull(argv[optind], NULL, 0);
-            if (rem < arg_size) {
-                error(1, 0, "too many arguments");
-            }
-            memcpy(ioctl_argp, &tmp, arg_size);
-            ioctl_argp += arg_size;
-            rem -= arg_size;
-            optind++;
-        }
-    }
-    printf("sending ioctl 0x%x", ioctl_nr);
-    rem = length;
-    while(rem--) {
-        printf(" 0x%02x", *ioctl_argp_save++);
-    }
-    printf(" to %s\n", device);
-
-    int res;
-    if(direct_arg)
-        res = ioctl(fd, ioctl_nr, *(uint32_t*)ioctl_args);
-    else if(length)
-        res = ioctl(fd, ioctl_nr, ioctl_args);
-    else
-        res = ioctl(fd, ioctl_nr, 0);
-    if (res < 0) {
-        free(ioctl_args);
-        error(1, errno, "ioctl 0x%x failed (returned %d)", ioctl_nr, res);
-    }
-
-    if (length) {
-        printf("return buf:");
-        ioctl_argp = ioctl_args;
-        rem = length;
-        while(rem--) {
-            printf(" %02x", *ioctl_argp++);
-        }
-        printf("\n");
-    }
-    free(ioctl_args);
-    close(fd);
-    return 0;
-}