/** * Copyright (C) ARM Limited 2014. 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 "Config.h" #include "DynBuf.h" #include "Logging.h" bool getLinuxVersion(int version[3]) { // Check the kernel version struct utsname utsname; if (uname(&utsname) != 0) { logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); 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(__FILE__, __LINE__, "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(__FILE__, __LINE__, "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(__FILE__, __LINE__, "gator: error: parsing stat begin failed"); handleException(); } ++comm; char *const str = strrchr(comm, ')'); if (str == NULL) { logg->logError(__FILE__, __LINE__, "gator: error: parsing stat end failed"); handleException(); } *str = '\0'; if (strncmp(comm, "gator", 5) == 0) { // Assume there is only one gator process return pid; } } closedir(proc); return -1; } int update(const char *const gatorPath) { printf("gator: starting\n"); int version[3]; if (!getLinuxVersion(version)) { logg->logError(__FILE__, __LINE__, "gator: error: getLinuxVersion failed"); handleException(); } if (KERNEL_VERSION(version[0], version[1], version[2]) < KERNEL_VERSION(2, 6, 32)) { logg->logError(__FILE__, __LINE__, "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(__FILE__, __LINE__, "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 (access("/sys/module/gator", F_OK) == 0) { logg->logError(__FILE__, __LINE__, "gator: error: Streamline has detected that the gator kernel module is loaded on your device. Please build an updated version of gator.ko and gatord and install them on your device."); handleException(); } if (geteuid() != 0) { printf("gator: trying sudo\n"); execlp("sudo", "sudo", gatorPath, "-u", NULL); // Streamline will provide the password if needed printf("gator: trying su\n"); char buf[1<<10]; snprintf(buf, sizeof(buf), "%s -u", gatorPath); execlp("su", "su", "-", "-c", buf, NULL); // Streamline will provide the password if needed logg->logError(__FILE__, __LINE__, "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: now root\n"); // setenforce 0 not needed for userspace gator // Kill existing gator DynBuf gatorStatPath; int gator_main = pgrep_gator(&gatorStatPath); if (gator_main > 0) { if (kill(gator_main, SIGTERM) != 0) { logg->logError(__FILE__, __LINE__, "gator: error: kill SIGTERM failed"); handleException(); } for (int i = 0; ; ++i) { if (access(gatorStatPath.getBuf(), F_OK) != 0) { break; } if (i == 5) { if (kill(gator_main, SIGKILL) != 0) { logg->logError(__FILE__, __LINE__, "gator: error: kill SIGKILL failed"); handleException(); } } else if (i >= 10) { logg->logError(__FILE__, __LINE__, "gator: error: unable to kill running gator"); handleException(); } sleep(1); } } printf("gator: no gatord running\n"); 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(__FILE__, __LINE__, "gator: error: rename failed"); handleException(); } } // Fork and start gatord (redirect stdout and stderr) int child = fork(); if (child < 0) { logg->logError(__FILE__, __LINE__, "gator: error: fork failed"); handleException(); } else if (child == 0) { int inFd = open("/dev/null", O_RDONLY | O_CLOEXEC); if (inFd < 0) { logg->logError(__FILE__, __LINE__, "gator: error: open of /dev/null failed"); handleException(); } int outFd = open("gatord.out", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600); if (outFd < 0) { logg->logError(__FILE__, __LINE__, "gator: error: open of gatord.out failed"); handleException(); } int errFd = open("gatord.err", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600); if (errFd < 0) { logg->logError(__FILE__, __LINE__, "gator: error: open of gatord.err failed"); handleException(); } if (dup2(inFd, STDIN_FILENO) < 0) { logg->logError(__FILE__, __LINE__, "gator: error: dup2 for stdin failed"); handleException(); } if (dup2(outFd, STDOUT_FILENO) < 0) { logg->logError(__FILE__, __LINE__, "gator: error: dup2 for stdout failed"); handleException(); } if (dup2(errFd, STDERR_FILENO) < 0) { logg->logError(__FILE__, __LINE__, "gator: error: dup2 for stderr failed"); handleException(); } execlp(newGatorPath, newGatorPath, "-a", NULL); logg->logError(__FILE__, __LINE__, "gator: error: execlp failed"); handleException(); } printf("gator: done\n"); return 0; }