/** * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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//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); }