summaryrefslogtreecommitdiff
path: root/tools/gator/daemon/Setup.cpp
diff options
context:
space:
mode:
authorAndrey Konovalov <andrey.konovalov@linaro.org>2015-06-15 13:59:33 +0300
committerAndrey Konovalov <andrey.konovalov@linaro.org>2015-06-15 13:59:33 +0300
commit1e5828d50fa0d0e53169eaab814fc95de9970a45 (patch)
tree1d03cba4be1a3692425cf4cac5d6a86f31ad089d /tools/gator/daemon/Setup.cpp
parent472866a4d27f627ba2f965b31a80723b3670974d (diff)
parent83c5aac308433cbf3762e68eb5053490bd958a83 (diff)
Merge branch 'tracking-gator' into merge-linux-linaro-core-tracking
Diffstat (limited to 'tools/gator/daemon/Setup.cpp')
-rw-r--r--tools/gator/daemon/Setup.cpp324
1 files changed, 324 insertions, 0 deletions
diff --git a/tools/gator/daemon/Setup.cpp b/tools/gator/daemon/Setup.cpp
new file mode 100644
index 00000000000..7dd83ceafcc
--- /dev/null
+++ b/tools/gator/daemon/Setup.cpp
@@ -0,0 +1,324 @@
+/**
+ * Copyright (C) ARM Limited 2014-2015. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "Setup.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "Config.h"
+#include "DynBuf.h"
+#include "Logging.h"
+#include "SessionData.h"
+
+#define GATOR_MSG "gator: "
+#define GATOR_ERROR "gator: error: "
+#define GATOR_CONFIRM "gator: confirm: "
+
+bool getLinuxVersion(int version[3]) {
+ // Check the kernel version
+ struct utsname utsname;
+ if (uname(&utsname) != 0) {
+ logg->logMessage("uname failed");
+ return false;
+ }
+
+ version[0] = 0;
+ version[1] = 0;
+ version[2] = 0;
+
+ int part = 0;
+ char *ch = utsname.release;
+ while (*ch >= '0' && *ch <= '9' && part < 3) {
+ version[part] = 10*version[part] + *ch - '0';
+
+ ++ch;
+ if (*ch == '.') {
+ ++part;
+ ++ch;
+ }
+ }
+
+ return true;
+}
+
+static int pgrep_gator(DynBuf *const printb) {
+ DynBuf b;
+
+ DIR *proc = opendir("/proc");
+ if (proc == NULL) {
+ logg->logError(GATOR_ERROR "opendir failed");
+ handleException();
+ }
+
+ int self = getpid();
+
+ struct dirent *dirent;
+ while ((dirent = readdir(proc)) != NULL) {
+ char *endptr;
+ const int pid = strtol(dirent->d_name, &endptr, 10);
+ if (*endptr != '\0' || (pid == self)) {
+ // Ignore proc items that are not integers like ., cpuinfo, etc...
+ continue;
+ }
+
+ if (!printb->printf("/proc/%i/stat", pid)) {
+ logg->logError(GATOR_ERROR "DynBuf::printf failed");
+ handleException();
+ }
+
+ if (!b.read(printb->getBuf())) {
+ // This is not a fatal error - the thread just doesn't exist any more
+ continue;
+ }
+
+ char *comm = strchr(b.getBuf(), '(');
+ if (comm == NULL) {
+ logg->logError(GATOR_ERROR "parsing stat comm begin failed");
+ handleException();
+ }
+ ++comm;
+ char *const str = strrchr(comm, ')');
+ if (str == NULL) {
+ logg->logError(GATOR_ERROR "parsing stat comm end failed");
+ handleException();
+ }
+ *str = '\0';
+
+ if (strncmp(comm, "gator", 5) != 0) {
+ continue;
+ }
+
+ char state;
+ const int count = sscanf(str + 2, " %c ", &state);
+ if (count != 1) {
+ logg->logError(GATOR_ERROR "parsing stat state failed");
+ handleException();
+ }
+
+ if (state == 'Z') {
+ // This gator is a zombie, ignore
+ continue;
+ }
+
+ // Assume there is only one gator process
+ return pid;
+ }
+
+ closedir(proc);
+
+ return -1;
+}
+
+static bool confirm(const char *const message) {
+ char buf[1<<10];
+
+ printf(GATOR_CONFIRM "%s\n", message);
+ while (fgets(buf, sizeof(buf), stdin) != NULL) {
+ if (strcmp(buf, "y\n") == 0) {
+ return true;
+ }
+ if (strcmp(buf, "n\n") == 0) {
+ return false;
+ }
+ // Ignore unrecognized input
+ }
+
+ return false;
+}
+
+void update(const char *const gatorPath) {
+ printf(GATOR_MSG "starting\n");
+
+ int version[3];
+ if (!getLinuxVersion(version)) {
+ logg->logError(GATOR_ERROR "getLinuxVersion failed");
+ handleException();
+ }
+
+ if (KERNEL_VERSION(version[0], version[1], version[2]) < KERNEL_VERSION(2, 6, 32)) {
+ logg->logError(GATOR_ERROR "Streamline can't automatically setup gator as this kernel version is not supported. Please upgrade the kernel on your device.");
+ handleException();
+ }
+
+ if (KERNEL_VERSION(version[0], version[1], version[2]) < KERNEL_VERSION(3, 4, 0)) {
+ logg->logError(GATOR_ERROR "Streamline can't automatically setup gator as gator.ko is required for this version of Linux. Please build gator.ko and gatord and install them on your device.");
+ handleException();
+ }
+
+ if (geteuid() != 0) {
+ printf(GATOR_MSG "trying sudo\n");
+ execlp("sudo", "sudo", gatorPath, "-u", NULL);
+ // Streamline will provide the password if needed
+
+ printf(GATOR_MSG "trying su\n");
+ char buf[1<<10];
+ /*
+ * Different versions of su handle additional -c command line options differently and expect the
+ * arguments in different ways. Try both ways wrapped in a shell.
+ *
+ * Then invoke another shell after su as it avoids odd failures on some Android systems
+ */
+ snprintf(buf, sizeof(buf), "su -c \"sh -c '%s -u'\" || su -c sh -c '%s -u'", gatorPath, gatorPath);
+ execlp("sh", "sh", "-c", buf, NULL);
+ // Streamline will provide the password if needed
+
+ logg->logError(GATOR_ERROR "Streamline was unable to sudo to root on your device. Please double check passwords, ensure sudo or su work with this user or try a different username.");
+ handleException();
+ }
+ printf(GATOR_MSG "now root\n");
+
+ if (access("/sys/module/gator", F_OK) == 0) {
+ if (!confirm("Streamline has detected that the gator kernel module is loaded on your device. Click yes to switch to user space gator, click no to abort the install.")) {
+ printf("gator: cancel\n");
+ exit(-1);
+ }
+ }
+
+ // setenforce 0 not needed for userspace gator
+
+ // Kill existing gator
+ DynBuf printb;
+ int gator_main = pgrep_gator(&printb);
+ if (gator_main > 0) {
+ if (kill(gator_main, SIGTERM) != 0) {
+ logg->logError(GATOR_ERROR "kill SIGTERM failed");
+ handleException();
+ }
+ if (!printb.printf("/proc/%i/exe", gator_main)) {
+ logg->logError(GATOR_ERROR "DynBuf::printf failed");
+ handleException();
+ }
+ for (int i = 0; ; ++i) {
+ // /proc/<pid>/exe exists but will not be accessible for zombies
+ if (access(printb.getBuf(), F_OK) != 0) {
+ break;
+ }
+ if (i == 5) {
+ if (kill(gator_main, SIGKILL) != 0) {
+ logg->logError(GATOR_ERROR "kill SIGKILL failed");
+ handleException();
+ }
+ } else if (i >= 10) {
+ logg->logError(GATOR_ERROR "unable to kill running gator");
+ handleException();
+ }
+ sleep(1);
+ }
+ }
+ printf(GATOR_MSG "no gatord running\n");
+
+ umount("/dev/gator");
+ syscall(__NR_delete_module, "gator", O_NONBLOCK);
+
+ rename("gatord", "gatord.old");
+ rename("gator.ko", "gator.ko.old");
+
+ // Rename gatord.YYYYMMDDHHMMSSMMMM to gatord
+ char *newGatorPath = strdup(gatorPath);
+ char *dot = strrchr(newGatorPath, '.');
+ if (dot != NULL) {
+ *dot = '\0';
+ if (rename(gatorPath, newGatorPath) != 0) {
+ logg->logError(GATOR_ERROR "rename failed");
+ handleException();
+ }
+ }
+
+ char buf[128];
+ int pipefd[2];
+ if (pipe_cloexec(pipefd) != 0) {
+ logg->logError(GATOR_ERROR "pipe failed");
+ handleException();
+ }
+
+ // Fork and start gatord (redirect stdin, stdout and stderr so shell can close)
+ int child = fork();
+ if (child < 0) {
+ logg->logError(GATOR_ERROR "fork failed");
+ handleException();
+ } else if (child == 0) {
+ int inFd;
+ int outFd;
+ int errFd;
+ int result = -1;
+
+ buf[0] = '\0';
+ close(pipefd[0]);
+
+ inFd = open("/dev/null", O_RDONLY | O_CLOEXEC);
+ if (inFd < 0) {
+ snprintf(buf, sizeof(buf), GATOR_ERROR "open of /dev/null failed");
+ goto fail_exit;
+ }
+ outFd = open("gatord.out", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
+ if (outFd < 0) {
+ snprintf(buf, sizeof(buf), GATOR_ERROR "open of gatord.out failed");
+ goto fail_exit;
+ }
+ errFd = open("gatord.err", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
+ if (errFd < 0) {
+ snprintf(buf, sizeof(buf), GATOR_ERROR "open of gatord.err failed");
+ goto fail_exit;
+ }
+ if (dup2(inFd, STDIN_FILENO) < 0) {
+ snprintf(buf, sizeof(buf), GATOR_ERROR "dup2 for stdin failed");
+ goto fail_exit;
+ }
+ fflush(stdout);
+ if (dup2(outFd, STDOUT_FILENO) < 0) {
+ snprintf(buf, sizeof(buf), GATOR_ERROR "dup2 for stdout failed");
+ goto fail_exit;
+ }
+ fflush(stderr);
+ if (dup2(errFd, STDERR_FILENO) < 0) {
+ snprintf(buf, sizeof(buf), GATOR_ERROR "dup2 for stderr failed");
+ goto fail_exit;
+ }
+
+ snprintf(buf, sizeof(buf), GATOR_MSG "done");
+ result = 0;
+
+ fail_exit:
+ if (buf[0] != '\0') {
+ const ssize_t bytes = write(pipefd[1], buf, sizeof(buf));
+ // Can't do anything if this fails
+ (void)bytes;
+ }
+ close(pipefd[1]);
+
+ if (result == 0) {
+ // Continue to execute gator normally
+ return;
+ }
+ exit(-1);
+ }
+
+ close(pipefd[1]);
+ const ssize_t bytes = read(pipefd[0], buf, sizeof(buf));
+ if (bytes > 0) {
+ logg->logError("%s", buf);
+ handleException();
+ }
+ close(pipefd[0]);
+
+ // Exit so parent shell can move on
+ exit(0);
+}