325 lines
8.1 KiB
C++
325 lines
8.1 KiB
C++
/**
|
|
* 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);
|
|
}
|