Magisk/native/jni/core/daemon.cpp
topjohnwu 80cd85b061 Try to use broadcast for su logging and notify
In commit 8d4c407, native Magisk always launches an activity for
communicating with Magisk Manager. While this works extremely well,
since it also workaround stupid OEMs that blocks broadcasts, it has a
problem: launching an activity will claim the focus of the device,
which could be super annoying in some circumstances.

This commit adds a new feature to run a broadcast test on boot complete.
If Magisk Manager successfully receives the broadcast, it will toggle
a setting in magiskd so all future su loggings and notifies will always
use broadcasts instead of launching activities.

Fix #1412
2019-05-13 02:01:10 -07:00

199 lines
4.3 KiB
C++

/* daemon.c - Magisk Daemon
*
* Start the daemon and wait for requests
* Connect the daemon and send requests through sockets
*/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <magisk.h>
#include <utils.h>
#include <daemon.h>
#include <selinux.h>
#include <db.h>
#include <resetprop.h>
#include <flags.h>
int SDK_INT = -1;
bool RECOVERY_MODE = false;
static struct stat SERVER_STAT;
static void verify_client(int client, pid_t pid) {
// Verify caller is the same as server
char path[32];
sprintf(path, "/proc/%d/exe", pid);
struct stat st{};
stat(path, &st);
if (st.st_dev != SERVER_STAT.st_dev || st.st_ino != SERVER_STAT.st_ino) {
close(client);
pthread_exit(nullptr);
}
}
static void *request_handler(void *args) {
int client = *((int *) args);
delete (int *) args;
struct ucred credential;
get_client_cred(client, &credential);
if (credential.uid != 0)
verify_client(client, credential.pid);
int req = read_int(client);
switch (req) {
case MAGISKHIDE:
case POST_FS_DATA:
case LATE_START:
case BOOT_COMPLETE:
case SQLITE_CMD:
case BROADCAST_ACK:
if (credential.uid != 0) {
write_int(client, ROOT_REQUIRED);
close(client);
return nullptr;
}
default:
break;
}
switch (req) {
case MAGISKHIDE:
magiskhide_handler(client);
break;
case SUPERUSER:
su_daemon_handler(client, &credential);
break;
case CHECK_VERSION:
write_string(client, MAGISK_VERSION ":MAGISK");
close(client);
break;
case CHECK_VERSION_CODE:
write_int(client, MAGISK_VER_CODE);
close(client);
break;
case POST_FS_DATA:
post_fs_data(client);
break;
case LATE_START:
late_start(client);
break;
case BOOT_COMPLETE:
boot_complete(client);
break;
case SQLITE_CMD:
exec_sql(client);
break;
case BROADCAST_ACK:
LOGD("* Use broadcasts for su logging and notify\n");
CONNECT_BROADCAST = true;
close(client);
default:
close(client);
break;
}
return nullptr;
}
static void main_daemon() {
android_logging();
setsid();
setcon("u:r:" SEPOL_PROC_DOMAIN ":s0");
restore_rootcon();
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
xdup2(fd, STDOUT_FILENO);
xdup2(fd, STDERR_FILENO);
close(fd);
fd = xopen("/dev/zero", O_RDWR | O_CLOEXEC);
xdup2(fd, STDIN_FILENO);
close(fd);
LOGI(SHOW_VER(Magisk) " daemon started\n");
// Get server stat
stat("/proc/self/exe", &SERVER_STAT);
// Get API level
parse_prop_file("/system/build.prop", [](auto key, auto val) -> bool {
if (key == "ro.build.version.sdk") {
LOGI("* Device API level: %s\n", val.data());
SDK_INT = parse_int(val);
return false;
}
return true;
});
// Load config status
parse_prop_file(MAGISKTMP "/config", [](auto key, auto val) -> bool {
if (key == "RECOVERYMODE" && val == "true")
RECOVERY_MODE = true;
return true;
});
struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (xbind(fd, (struct sockaddr*) &sun, len))
exit(1);
xlisten(fd, 10);
// Change process name
set_nice_name("magiskd");
// Block all signals
sigset_t block_set;
sigfillset(&block_set);
pthread_sigmask(SIG_SETMASK, &block_set, nullptr);
// Loop forever to listen for requests
for (;;) {
int *client = new int;
*client = xaccept4(fd, nullptr, nullptr, SOCK_CLOEXEC);
new_daemon_thread(request_handler, client);
}
}
int switch_mnt_ns(int pid) {
char mnt[32];
snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid);
if (access(mnt, R_OK) == -1) return 1; // Maybe process died..
int fd, ret;
fd = xopen(mnt, O_RDONLY);
if (fd < 0) return 1;
// Switch to its namespace
ret = xsetns(fd, 0);
close(fd);
return ret;
}
int connect_daemon(bool create) {
struct sockaddr_un sun;
socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET);
int fd = xsocket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (connect(fd, (struct sockaddr*) &sun, len)) {
if (!create || getuid() != UID_ROOT || getgid() != UID_ROOT) {
LOGE("No daemon is currently running!\n");
exit(1);
}
LOGD("client: launching new main daemon process\n");
if (fork_dont_care() == 0) {
close(fd);
main_daemon();
}
while (connect(fd, (struct sockaddr*) &sun, len))
usleep(10000);
}
return fd;
}