Fully migrate Magisk to C++
This commit is contained in:
parent
4351de503f
commit
cda57dd4b4
@ -32,14 +32,14 @@ LOCAL_C_INCLUDES := \
|
||||
$(LIBUTILS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
misc/applets.c \
|
||||
misc/img.c \
|
||||
daemon/magisk.c \
|
||||
daemon/daemon.c \
|
||||
daemon/log_daemon.c \
|
||||
daemon/bootstages.c \
|
||||
daemon/socket.c \
|
||||
daemon/db.c \
|
||||
misc/applets.cpp \
|
||||
misc/img.cpp \
|
||||
daemon/magisk.cpp \
|
||||
daemon/daemon.cpp \
|
||||
daemon/log_daemon.cpp \
|
||||
daemon/bootstages.cpp \
|
||||
daemon/socket.cpp \
|
||||
daemon/db.cpp \
|
||||
magiskhide/magiskhide.cpp \
|
||||
magiskhide/proc_monitor.cpp \
|
||||
magiskhide/hide_utils.cpp \
|
||||
@ -47,10 +47,10 @@ LOCAL_SRC_FILES := \
|
||||
resetprop/resetprop.cpp \
|
||||
resetprop/system_property_api.cpp \
|
||||
resetprop/system_property_set.cpp \
|
||||
su/su.c \
|
||||
su/connect.c \
|
||||
su/pts.c \
|
||||
su/su_daemon.c
|
||||
su/su.cpp \
|
||||
su/connect.cpp \
|
||||
su/pts.cpp \
|
||||
su/su_daemon.cpp
|
||||
|
||||
LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
@ -24,13 +24,13 @@
|
||||
#include "flags.h"
|
||||
|
||||
static char buf[PATH_MAX], buf2[PATH_MAX];
|
||||
static struct vector module_list;
|
||||
static Array<char *> module_list;
|
||||
|
||||
extern char **environ;
|
||||
static int bind_mount(const char *from, const char *to);
|
||||
|
||||
/******************
|
||||
* Node structure *
|
||||
******************/
|
||||
/***************
|
||||
* Magic Mount *
|
||||
***************/
|
||||
|
||||
// Precedence: MODULE > SKEL > INTER > DUMMY
|
||||
#define IS_DUMMY 0x01 /* mount from mirror */
|
||||
@ -42,73 +42,236 @@ extern char **environ;
|
||||
#define IS_LNK(n) (n->type == DT_LNK)
|
||||
#define IS_REG(n) (n->type == DT_REG)
|
||||
|
||||
struct node_entry {
|
||||
class node_entry {
|
||||
public:
|
||||
const char *module; /* Only used when status & IS_MODULE */
|
||||
char *name;
|
||||
uint8_t type;
|
||||
uint8_t status;
|
||||
struct node_entry *parent;
|
||||
struct vector *children;
|
||||
node_entry *parent;
|
||||
Array<node_entry *> children;
|
||||
|
||||
node_entry() = default;
|
||||
node_entry(const char *, const char *, uint8_t type = 0, uint8_t status = 0);
|
||||
node_entry(const char *, uint8_t type = 0, uint8_t status = 0);
|
||||
~node_entry();
|
||||
void create_module_tree(const char *module);
|
||||
void magic_mount();
|
||||
|
||||
private:
|
||||
char *get_path();
|
||||
node_entry *insert(node_entry *);
|
||||
void clone_skeleton();
|
||||
int get_path(char *path);
|
||||
};
|
||||
|
||||
static void concat_path(struct node_entry *node) {
|
||||
if (node->parent)
|
||||
concat_path(node->parent);
|
||||
size_t len = strlen(buf);
|
||||
buf[len] = '/';
|
||||
strcpy(buf + len + 1, node->name);
|
||||
node_entry::node_entry(const char *module, const char *name, uint8_t type, uint8_t status)
|
||||
: node_entry(name, type, status) {
|
||||
this->module = module;
|
||||
}
|
||||
|
||||
static char *get_full_path(struct node_entry *node) {
|
||||
buf[0] = '\0';
|
||||
concat_path(node);
|
||||
node_entry::node_entry(const char *name, uint8_t type, uint8_t status)
|
||||
: type(type), status(status), parent(nullptr) {
|
||||
this->name = strdup(name);
|
||||
}
|
||||
|
||||
node_entry::~node_entry() {
|
||||
free(name);
|
||||
for (auto &node : children)
|
||||
delete node;
|
||||
}
|
||||
|
||||
char *node_entry::get_path() {
|
||||
get_path(buf);
|
||||
return strdup(buf);
|
||||
}
|
||||
|
||||
// Free the node
|
||||
static void destroy_node(struct node_entry *node) {
|
||||
free(node->name);
|
||||
vec_destroy(node->children);
|
||||
free(node->children);
|
||||
free(node);
|
||||
int node_entry::get_path(char *path) {
|
||||
int len = 0;
|
||||
if (parent)
|
||||
len = parent->get_path(path);
|
||||
len += sprintf(path + len, "/%s", name);
|
||||
return len;
|
||||
}
|
||||
|
||||
// Free the node and all children recursively
|
||||
static void destroy_subtree(struct node_entry *node) {
|
||||
// Never free parent, since it shall be freed by themselves
|
||||
struct node_entry *e;
|
||||
vec_for_each(node->children, e) {
|
||||
destroy_subtree(e);
|
||||
}
|
||||
destroy_node(node);
|
||||
}
|
||||
|
||||
// Return the child
|
||||
static struct node_entry *insert_child(struct node_entry *p, struct node_entry *c) {
|
||||
c->parent = p;
|
||||
if (p->children == NULL) {
|
||||
p->children = xmalloc(sizeof(struct vector));
|
||||
vec_init(p->children);
|
||||
}
|
||||
struct node_entry *e;
|
||||
vec_for_each(p->children, e) {
|
||||
if (strcmp(e->name, c->name) == 0) {
|
||||
// Exist duplicate
|
||||
if (c->status > e->status) {
|
||||
// Precedence is higher, replace with new node
|
||||
destroy_subtree(e);
|
||||
vec_cur(p->children) = c;
|
||||
return c;
|
||||
node_entry *node_entry::insert(node_entry *node) {
|
||||
node->parent = this;
|
||||
for (auto &child : children) {
|
||||
if (strcmp(child->name, node->name) == 0) {
|
||||
if (node->status > child->status) {
|
||||
// The new node has higher precedence
|
||||
delete child;
|
||||
child = node;
|
||||
return node;
|
||||
} else {
|
||||
// Free the new entry, return old
|
||||
destroy_node(c);
|
||||
return e;
|
||||
delete node;
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
// New entry, push back
|
||||
vec_push_back(p->children, c);
|
||||
return c;
|
||||
children.push_back(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
void node_entry::create_module_tree(const char *module) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
char *full_path = get_path();
|
||||
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, module, full_path);
|
||||
|
||||
if (!(dir = xopendir(buf)))
|
||||
goto cleanup;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
// Create new node
|
||||
node_entry *node = new node_entry(module, entry->d_name, entry->d_type);
|
||||
snprintf(buf, PATH_MAX, "%s/%s", full_path, node->name);
|
||||
|
||||
/*
|
||||
* Clone the parent in the following condition:
|
||||
* 1. File in module is a symlink
|
||||
* 2. Target file do not exist
|
||||
* 3. Target file is a symlink (exclude /system/vendor)
|
||||
*/
|
||||
bool clone = false;
|
||||
if (IS_LNK(node) || access(buf, F_OK) == -1) {
|
||||
clone = true;
|
||||
} else if (parent != nullptr || strcmp(node->name, "vendor") != 0) {
|
||||
struct stat s;
|
||||
xstat(buf, &s);
|
||||
if (S_ISLNK(s.st_mode))
|
||||
clone = true;
|
||||
}
|
||||
|
||||
if (clone) {
|
||||
// Mark self as a skeleton
|
||||
status |= IS_SKEL; /* This will not overwrite if parent is module */
|
||||
node->status = IS_MODULE;
|
||||
} else if (IS_DIR(node)) {
|
||||
// Check if marked as replace
|
||||
snprintf(buf2, PATH_MAX, "%s/%s%s/.replace", MOUNTPOINT, module, buf);
|
||||
if (access(buf2, F_OK) == 0) {
|
||||
// Replace everything, mark as leaf
|
||||
node->status = IS_MODULE;
|
||||
} else {
|
||||
// This will be an intermediate node
|
||||
node->status = IS_INTER;
|
||||
}
|
||||
} else if (IS_REG(node)) {
|
||||
// This is a file, mark as leaf
|
||||
node->status = IS_MODULE;
|
||||
}
|
||||
node = insert(node);
|
||||
if (node->status & (IS_SKEL | IS_INTER)) {
|
||||
// Intermediate folder, travel deeper
|
||||
node->create_module_tree(module);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
cleanup:
|
||||
free(full_path);
|
||||
}
|
||||
|
||||
void node_entry::clone_skeleton() {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
struct node_entry *dummy;
|
||||
|
||||
// Clone the structure
|
||||
char *full_path = get_path();
|
||||
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path);
|
||||
if (!(dir = xopendir(buf)))
|
||||
goto cleanup;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
// Create dummy node
|
||||
dummy = new node_entry(entry->d_name, entry->d_type, IS_DUMMY);
|
||||
insert(dummy);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
if (status & IS_SKEL) {
|
||||
struct stat s;
|
||||
char *con;
|
||||
xstat(full_path, &s);
|
||||
getfilecon(full_path, &con);
|
||||
LOGI("mnt_tmpfs : %s\n", full_path);
|
||||
xmount("tmpfs", full_path, "tmpfs", 0, nullptr);
|
||||
chmod(full_path, s.st_mode & 0777);
|
||||
chown(full_path, s.st_uid, s.st_gid);
|
||||
setfilecon(full_path, con);
|
||||
free(con);
|
||||
}
|
||||
|
||||
for (auto &child : children) {
|
||||
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
|
||||
|
||||
// Create the dummy file/directory
|
||||
if (IS_DIR(child))
|
||||
xmkdir(buf, 0755);
|
||||
else if (IS_REG(child))
|
||||
close(creat(buf, 0644));
|
||||
// Links will be handled later
|
||||
|
||||
if (child->parent->parent == nullptr && strcmp(child->name, "vendor") == 0) {
|
||||
if (IS_LNK(child)) {
|
||||
cp_afc(MIRRDIR "/system/vendor", "/system/vendor");
|
||||
LOGI("creat_link: %s <- %s\n", "/system/vendor", MIRRDIR "/system/vendor");
|
||||
}
|
||||
// Skip
|
||||
continue;
|
||||
} else if (child->status & IS_MODULE) {
|
||||
// Mount from module file to dummy file
|
||||
snprintf(buf2, PATH_MAX, "%s/%s%s/%s", MOUNTPOINT, child->module, full_path, child->name);
|
||||
} else if (child->status & (IS_SKEL | IS_INTER)) {
|
||||
// It's an intermediate folder, recursive clone
|
||||
child->clone_skeleton();
|
||||
continue;
|
||||
} else if (child->status & IS_DUMMY) {
|
||||
// Mount from mirror to dummy file
|
||||
snprintf(buf2, PATH_MAX, "%s%s/%s", MIRRDIR, full_path, child->name);
|
||||
}
|
||||
|
||||
if (IS_LNK(child)) {
|
||||
// Copy symlinks directly
|
||||
cp_afc(buf2, buf);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("creat_link: %s <- %s\n",buf, buf2);
|
||||
#else
|
||||
LOGI("creat_link: %s\n", buf);
|
||||
#endif
|
||||
} else {
|
||||
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
|
||||
bind_mount(buf2, buf);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(full_path);
|
||||
}
|
||||
|
||||
void node_entry::magic_mount() {
|
||||
if (status & IS_MODULE) {
|
||||
// Mount module item
|
||||
char *real_path = get_path();
|
||||
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, module, real_path);
|
||||
bind_mount(buf, real_path);
|
||||
free(real_path);
|
||||
} else if (status & IS_SKEL) {
|
||||
// The node is labeled to be cloned with skeleton, lets do it
|
||||
clone_skeleton();
|
||||
} else if (status & IS_INTER) {
|
||||
// It's an intermediate node, travel deeper
|
||||
for (auto &child : children)
|
||||
child->magic_mount();
|
||||
}
|
||||
// The only thing goes here should be vendor placeholder
|
||||
// There should be no dummies, so don't need to handle it here
|
||||
}
|
||||
|
||||
/***********
|
||||
@ -144,11 +307,12 @@ static void exec_common_script(const char* stage) {
|
||||
if (access(buf2, X_OK) == -1)
|
||||
continue;
|
||||
LOGI("%s.d: exec [%s]\n", stage, entry->d_name);
|
||||
int pid = exec_command(0, NULL,
|
||||
int pid = exec_command(
|
||||
0, nullptr,
|
||||
strcmp(stage, "post-fs-data") ? set_path : set_mirror_path,
|
||||
"sh", buf2, NULL);
|
||||
"sh", buf2, nullptr);
|
||||
if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
waitpid(pid, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,206 +320,19 @@ static void exec_common_script(const char* stage) {
|
||||
}
|
||||
|
||||
static void exec_module_script(const char* stage) {
|
||||
char *module;
|
||||
vec_for_each(&module_list, module) {
|
||||
for (auto &module : module_list) {
|
||||
snprintf(buf2, PATH_MAX, "%s/%s/%s.sh", MOUNTPOINT, module, stage);
|
||||
snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, module);
|
||||
if (access(buf2, F_OK) == -1 || access(buf, F_OK) == 0)
|
||||
continue;
|
||||
LOGI("%s: exec [%s.sh]\n", module, stage);
|
||||
int pid = exec_command(0, NULL,
|
||||
int pid = exec_command(
|
||||
0, nullptr,
|
||||
strcmp(stage, "post-fs-data") ? set_path : set_mirror_path,
|
||||
"sh", buf2, NULL);
|
||||
"sh", buf2, nullptr);
|
||||
if (pid != -1)
|
||||
waitpid(pid, NULL, 0);
|
||||
waitpid(pid, nullptr, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***************
|
||||
* Magic Mount *
|
||||
***************/
|
||||
|
||||
static int bind_mount(const char *from, const char *to) {
|
||||
int ret = xmount(from, to, NULL, MS_BIND, NULL);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("bind_mount: %s <- %s\n", to, from);
|
||||
#else
|
||||
LOGI("bind_mount: %s\n", to);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void construct_tree(const char *module, struct node_entry *parent) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
struct node_entry *node;
|
||||
|
||||
char *parent_path = get_full_path(parent);
|
||||
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, module, parent_path);
|
||||
|
||||
if (!(dir = xopendir(buf)))
|
||||
goto cleanup;
|
||||
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
// Create new node
|
||||
node = xcalloc(sizeof(*node), 1);
|
||||
node->module = module;
|
||||
node->name = strdup(entry->d_name);
|
||||
node->type = entry->d_type;
|
||||
snprintf(buf, PATH_MAX, "%s/%s", parent_path, node->name);
|
||||
|
||||
/*
|
||||
* Clone the parent in the following condition:
|
||||
* 1. File in module is a symlink
|
||||
* 2. Target file do not exist
|
||||
* 3. Target file is a symlink, but not /system/vendor
|
||||
*/
|
||||
int clone = 0;
|
||||
if (IS_LNK(node) || access(buf, F_OK) == -1) {
|
||||
clone = 1;
|
||||
} else if (parent->parent != NULL || strcmp(node->name, "vendor") != 0) {
|
||||
struct stat s;
|
||||
xstat(buf, &s);
|
||||
if (S_ISLNK(s.st_mode))
|
||||
clone = 1;
|
||||
}
|
||||
|
||||
if (clone) {
|
||||
// Mark the parent folder as a skeleton
|
||||
parent->status |= IS_SKEL; /* This will not overwrite if parent is module */
|
||||
node->status = IS_MODULE;
|
||||
} else if (IS_DIR(node)) {
|
||||
// Check if marked as replace
|
||||
snprintf(buf2, PATH_MAX, "%s/%s%s/.replace", MOUNTPOINT, module, buf);
|
||||
if (access(buf2, F_OK) == 0) {
|
||||
// Replace everything, mark as leaf
|
||||
node->status = IS_MODULE;
|
||||
} else {
|
||||
// This will be an intermediate node
|
||||
node->status = IS_INTER;
|
||||
}
|
||||
} else if (IS_REG(node)) {
|
||||
// This is a leaf, mark as target
|
||||
node->status = IS_MODULE;
|
||||
}
|
||||
node = insert_child(parent, node);
|
||||
if (node->status & (IS_SKEL | IS_INTER)) {
|
||||
// Intermediate folder, travel deeper
|
||||
construct_tree(module, node);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
cleanup:
|
||||
free(parent_path);
|
||||
}
|
||||
|
||||
static void clone_skeleton(struct node_entry *node) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
struct node_entry *dummy, *child;
|
||||
|
||||
// Clone the structure
|
||||
char *full_path = get_full_path(node);
|
||||
snprintf(buf, PATH_MAX, "%s%s", MIRRDIR, full_path);
|
||||
if (!(dir = xopendir(buf)))
|
||||
goto cleanup;
|
||||
while ((entry = xreaddir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
// Create dummy node
|
||||
dummy = xcalloc(sizeof(*dummy), 1);
|
||||
dummy->name = strdup(entry->d_name);
|
||||
dummy->type = entry->d_type;
|
||||
dummy->status = IS_DUMMY;
|
||||
insert_child(node, dummy);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
if (node->status & IS_SKEL) {
|
||||
struct stat s;
|
||||
char *con;
|
||||
xstat(full_path, &s);
|
||||
getfilecon(full_path, &con);
|
||||
LOGI("mnt_tmpfs : %s\n", full_path);
|
||||
xmount("tmpfs", full_path, "tmpfs", 0, NULL);
|
||||
chmod(full_path, s.st_mode & 0777);
|
||||
chown(full_path, s.st_uid, s.st_gid);
|
||||
setfilecon(full_path, con);
|
||||
free(con);
|
||||
}
|
||||
|
||||
vec_for_each(node->children, child) {
|
||||
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
|
||||
|
||||
// Create the dummy file/directory
|
||||
if (IS_DIR(child))
|
||||
xmkdir(buf, 0755);
|
||||
else if (IS_REG(child))
|
||||
close(creat(buf, 0644));
|
||||
// Links will be handled later
|
||||
|
||||
if (child->parent->parent == NULL && strcmp(child->name, "vendor") == 0) {
|
||||
if (IS_LNK(child)) {
|
||||
cp_afc(MIRRDIR "/system/vendor", "/system/vendor");
|
||||
LOGI("creat_link: %s <- %s\n", "/system/vendor", MIRRDIR "/system/vendor");
|
||||
}
|
||||
// Skip
|
||||
continue;
|
||||
} else if (child->status & IS_MODULE) {
|
||||
// Mount from module file to dummy file
|
||||
snprintf(buf2, PATH_MAX, "%s/%s%s/%s", MOUNTPOINT, child->module, full_path, child->name);
|
||||
} else if (child->status & (IS_SKEL | IS_INTER)) {
|
||||
// It's an intermediate folder, recursive clone
|
||||
clone_skeleton(child);
|
||||
continue;
|
||||
} else if (child->status & IS_DUMMY) {
|
||||
// Mount from mirror to dummy file
|
||||
snprintf(buf2, PATH_MAX, "%s%s/%s", MIRRDIR, full_path, child->name);
|
||||
}
|
||||
|
||||
if (IS_LNK(child)) {
|
||||
// Copy symlinks directly
|
||||
cp_afc(buf2, buf);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("creat_link: %s <- %s\n",buf, buf2);
|
||||
#else
|
||||
LOGI("creat_link: %s\n", buf);
|
||||
#endif
|
||||
} else {
|
||||
snprintf(buf, PATH_MAX, "%s/%s", full_path, child->name);
|
||||
bind_mount(buf2, buf);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
free(full_path);
|
||||
}
|
||||
|
||||
static void magic_mount(struct node_entry *node) {
|
||||
char *real_path;
|
||||
struct node_entry *child;
|
||||
|
||||
if (node->status & IS_MODULE) {
|
||||
// The real deal, mount module item
|
||||
real_path = get_full_path(node);
|
||||
snprintf(buf, PATH_MAX, "%s/%s%s", MOUNTPOINT, node->module, real_path);
|
||||
bind_mount(buf, real_path);
|
||||
free(real_path);
|
||||
} else if (node->status & IS_SKEL) {
|
||||
// The node is labeled to be cloned with skeleton, lets do it
|
||||
clone_skeleton(node);
|
||||
} else if (node->status & IS_INTER) {
|
||||
// It's an intermediate node, travel deeper
|
||||
vec_for_each(node->children, child)
|
||||
magic_mount(child);
|
||||
}
|
||||
// The only thing goes here should be vendor placeholder
|
||||
// There should be no dummies, so don't need to handle it here
|
||||
}
|
||||
|
||||
/****************
|
||||
@ -399,8 +376,18 @@ static void simple_mount(const char *path) {
|
||||
* Miscellaneous *
|
||||
*****************/
|
||||
|
||||
#define alt_img ((char *[]) \
|
||||
{ "/cache/magisk.img", "/data/magisk_merge.img", "/data/adb/magisk_merge.img", NULL })
|
||||
static int bind_mount(const char *from, const char *to) {
|
||||
int ret = xmount(from, to, nullptr, MS_BIND, nullptr);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("bind_mount: %s <- %s\n", to, from);
|
||||
#else
|
||||
LOGI("bind_mount: %s\n", to);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define alt_img ((const char *[]) \
|
||||
{ "/cache/magisk.img", "/data/magisk_merge.img", "/data/adb/magisk_merge.img", nullptr })
|
||||
|
||||
static int prepare_img() {
|
||||
// Merge images
|
||||
@ -419,7 +406,7 @@ static int prepare_img() {
|
||||
LOGI("* Mounting " MAINIMG "\n");
|
||||
// Mounting magisk image
|
||||
char *magiskloop = mount_image(MAINIMG, MOUNTPOINT);
|
||||
if (magiskloop == NULL)
|
||||
if (magiskloop == nullptr)
|
||||
return 1;
|
||||
|
||||
xmkdir(COREDIR, 0755);
|
||||
@ -447,7 +434,7 @@ static int prepare_img() {
|
||||
snprintf(buf, PATH_MAX, "%s/%s/disable", MOUNTPOINT, entry->d_name);
|
||||
if (access(buf, F_OK) == 0)
|
||||
continue;
|
||||
vec_push_back(&module_list, strdup(entry->d_name));
|
||||
module_list.push_back(strdup(entry->d_name));
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
@ -459,19 +446,19 @@ static int prepare_img() {
|
||||
}
|
||||
|
||||
static void install_apk(const char *apk) {
|
||||
setfilecon(apk, "u:object_r:"SEPOL_FILE_DOMAIN":s0");
|
||||
setfilecon(apk, "u:object_r:" SEPOL_FILE_DOMAIN ":s0");
|
||||
while (1) {
|
||||
sleep(5);
|
||||
LOGD("apk_install: attempting to install APK");
|
||||
int apk_res = -1, pid;
|
||||
pid = exec_command(1, &apk_res, NULL, "/system/bin/pm", "install", "-r", apk, NULL);
|
||||
pid = exec_command(1, &apk_res, nullptr, "/system/bin/pm", "install", "-r", apk, nullptr);
|
||||
if (pid != -1) {
|
||||
int err = 0;
|
||||
while (fdgets(buf, PATH_MAX, apk_res) > 0) {
|
||||
LOGD("apk_install: %s", buf);
|
||||
err |= strstr(buf, "Error:") != NULL;
|
||||
err |= strstr(buf, "Error:") != nullptr;
|
||||
}
|
||||
waitpid(pid, NULL, 0);
|
||||
waitpid(pid, nullptr, 0);
|
||||
close(apk_res);
|
||||
// Keep trying until pm is started
|
||||
if (err)
|
||||
@ -482,38 +469,35 @@ static void install_apk(const char *apk) {
|
||||
unlink(apk);
|
||||
}
|
||||
|
||||
static int check_data() {
|
||||
struct vector v;
|
||||
vec_init(&v);
|
||||
file_to_vector("/proc/mounts", &v);
|
||||
char *line;
|
||||
int mnt = 0;
|
||||
vec_for_each(&v, line) {
|
||||
if (strstr(line, " /data ") && strstr(line, "tmpfs") == NULL) {
|
||||
mnt = 1;
|
||||
break;
|
||||
static bool check_data() {
|
||||
bool mnt = false;
|
||||
bool data = false;
|
||||
Array<char *> mounts;
|
||||
file_to_array("/proc/mounts", mounts);
|
||||
for (auto &line : mounts) {
|
||||
if (strstr(line, " /data ") && strstr(line, "tmpfs") == nullptr)
|
||||
mnt = true;
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
vec_deep_destroy(&v);
|
||||
int data = 0;
|
||||
mounts.clear();
|
||||
if (mnt) {
|
||||
char *crypto = getprop("ro.crypto.state");
|
||||
if (crypto != NULL) {
|
||||
if (crypto != nullptr) {
|
||||
if (strcmp(crypto, "unencrypted") == 0) {
|
||||
// Unencrypted, we can directly access data
|
||||
data = 1;
|
||||
data = true;
|
||||
} else {
|
||||
// Encrypted, check whether vold is started
|
||||
char *vold = getprop("init.svc.vold");
|
||||
if (vold != NULL) {
|
||||
if (vold != nullptr) {
|
||||
free(vold);
|
||||
data = 1;
|
||||
data = true;
|
||||
}
|
||||
}
|
||||
free(crypto);
|
||||
} else {
|
||||
// ro.crypto.state is not set, assume it's unencrypted
|
||||
data = 1;
|
||||
data = true;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
@ -521,18 +505,18 @@ static int check_data() {
|
||||
|
||||
extern int launch_magiskhide();
|
||||
|
||||
static void *start_magisk_hide(void *args) {
|
||||
static void *start_magisk_hide(void *) {
|
||||
launch_magiskhide();
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void auto_start_magiskhide() {
|
||||
if (!start_log_daemon())
|
||||
return;
|
||||
char *hide_prop = getprop2(MAGISKHIDE_PROP, 1);
|
||||
if (hide_prop == NULL || strcmp(hide_prop, "0") != 0) {
|
||||
char *hide_prop = getprop(MAGISKHIDE_PROP, 1);
|
||||
if (hide_prop == nullptr || strcmp(hide_prop, "0") != 0) {
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, start_magisk_hide, NULL);
|
||||
xpthread_create(&thread, nullptr, start_magisk_hide, nullptr);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
free(hide_prop);
|
||||
@ -565,7 +549,7 @@ void unlock_blocks() {
|
||||
|
||||
static void unblock_boot_process() {
|
||||
close(xopen(UNBLOCKFILE, O_RDONLY | O_CREAT, 0));
|
||||
pthread_exit(NULL);
|
||||
pthread_exit(nullptr);
|
||||
}
|
||||
|
||||
static const char wrapper[] =
|
||||
@ -621,7 +605,7 @@ void startup() {
|
||||
void *magisk, *init;
|
||||
size_t magisk_size, init_size;
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT, NULL);
|
||||
xmount(nullptr, "/", nullptr, MS_REMOUNT, nullptr);
|
||||
|
||||
// Remove some traits of Magisk
|
||||
unlink(MAGISKRC);
|
||||
@ -645,7 +629,7 @@ void startup() {
|
||||
close(sbin);
|
||||
|
||||
// Mount the /sbin tmpfs overlay
|
||||
xmount("tmpfs", "/sbin", "tmpfs", 0, NULL);
|
||||
xmount("tmpfs", "/sbin", "tmpfs", 0, nullptr);
|
||||
chmod("/sbin", 0755);
|
||||
setfilecon("/sbin", "u:object_r:rootfs:s0");
|
||||
sbin = xopen("/sbin", O_RDONLY | O_CLOEXEC);
|
||||
@ -665,15 +649,15 @@ void startup() {
|
||||
fd = creat("/sbin/magisk", 0755);
|
||||
xwrite(fd, wrapper, sizeof(wrapper) - 1);
|
||||
close(fd);
|
||||
setfilecon("/sbin/magisk.bin", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
|
||||
setfilecon("/sbin/magisk", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
|
||||
setfilecon("/sbin/magisk.bin", "u:object_r:" SEPOL_FILE_DOMAIN ":s0");
|
||||
setfilecon("/sbin/magisk", "u:object_r:" SEPOL_FILE_DOMAIN ":s0");
|
||||
|
||||
// Setup magiskinit symlinks
|
||||
fd = creat("/sbin/magiskinit", 0755);
|
||||
xwrite(fd, init, init_size);
|
||||
close(fd);
|
||||
free(init);
|
||||
setfilecon("/sbin/magiskinit", "u:object_r:"SEPOL_FILE_DOMAIN":s0");
|
||||
setfilecon("/sbin/magiskinit", "u:object_r:" SEPOL_FILE_DOMAIN ":s0");
|
||||
for (int i = 0; init_applet[i]; ++i) {
|
||||
snprintf(buf, PATH_MAX, "/sbin/%s", init_applet[i]);
|
||||
xsymlink("/sbin/magiskinit", buf);
|
||||
@ -691,10 +675,10 @@ void startup() {
|
||||
close(root);
|
||||
|
||||
// Alternative binaries paths
|
||||
char *alt_bin[] = { "/cache/data_bin", "/data/magisk",
|
||||
const char *alt_bin[] = { "/cache/data_bin", "/data/magisk",
|
||||
"/data/data/com.topjohnwu.magisk/install",
|
||||
"/data/user_de/0/com.topjohnwu.magisk/install", NULL };
|
||||
char *bin_path = NULL;
|
||||
"/data/user_de/0/com.topjohnwu.magisk/install", nullptr };
|
||||
const char *bin_path = nullptr;
|
||||
for (int i = 0; alt_bin[i]; ++i) {
|
||||
struct stat st;
|
||||
if (lstat(alt_bin[i], &st) != -1 && !S_ISLNK(st.st_mode)) {
|
||||
@ -723,19 +707,17 @@ void startup() {
|
||||
xmkdir(BLOCKDIR, 0755);
|
||||
|
||||
LOGI("* Mounting mirrors");
|
||||
struct vector mounts;
|
||||
vec_init(&mounts);
|
||||
file_to_vector("/proc/mounts", &mounts);
|
||||
char *line;
|
||||
Array<char *> mounts;
|
||||
file_to_array("/proc/mounts", mounts);
|
||||
int skip_initramfs = 0;
|
||||
// Check whether skip_initramfs device
|
||||
vec_for_each(&mounts, line) {
|
||||
for (auto &line : mounts) {
|
||||
if (strstr(line, " /system_root ")) {
|
||||
bind_mount("/system_root/system", MIRRDIR "/system");
|
||||
skip_initramfs = 1;
|
||||
} else if (!skip_initramfs && strstr(line, " /system ")) {
|
||||
sscanf(line, "%s %*s %s", buf, buf2);
|
||||
xmount(buf, MIRRDIR "/system", buf2, MS_RDONLY, NULL);
|
||||
xmount(buf, MIRRDIR "/system", buf2, MS_RDONLY, nullptr);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s <- %s\n", MIRRDIR "/system", buf);
|
||||
#else
|
||||
@ -745,7 +727,7 @@ void startup() {
|
||||
seperate_vendor = 1;
|
||||
sscanf(line, "%s %*s %s", buf, buf2);
|
||||
xmkdir(MIRRDIR "/vendor", 0755);
|
||||
xmount(buf, MIRRDIR "/vendor", buf2, MS_RDONLY, NULL);
|
||||
xmount(buf, MIRRDIR "/vendor", buf2, MS_RDONLY, nullptr);
|
||||
#ifdef MAGISK_DEBUG
|
||||
LOGI("mount: %s <- %s\n", MIRRDIR "/vendor", buf);
|
||||
#else
|
||||
@ -754,7 +736,7 @@ void startup() {
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
vec_destroy(&mounts);
|
||||
mounts.clear();
|
||||
if (!seperate_vendor) {
|
||||
xsymlink(MIRRDIR "/system/vendor", MIRRDIR "/vendor");
|
||||
#ifdef MAGISK_DEBUG
|
||||
@ -767,12 +749,23 @@ void startup() {
|
||||
bind_mount(DATABIN, MIRRDIR "/bin");
|
||||
if (access(MIRRDIR "/bin/busybox", X_OK) == 0) {
|
||||
LOGI("* Setting up internal busybox");
|
||||
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, NULL);
|
||||
exec_command_sync(MIRRDIR "/bin/busybox", "--install", "-s", BBPATH, nullptr);
|
||||
xsymlink(MIRRDIR "/bin/busybox", BBPATH "/busybox");
|
||||
}
|
||||
|
||||
// Start post-fs-data mode
|
||||
execl("/sbin/magisk.bin", "magisk", "--post-fs-data", NULL);
|
||||
execl("/sbin/magisk.bin", "magisk", "--post-fs-data", nullptr);
|
||||
}
|
||||
|
||||
static void core_only() {
|
||||
// Systemless hosts
|
||||
if (access(HOSTSFILE, F_OK) == 0) {
|
||||
LOGI("* Enabling systemless hosts file support");
|
||||
bind_mount(HOSTSFILE, "/system/etc/hosts");
|
||||
}
|
||||
|
||||
auto_start_magiskhide();
|
||||
unblock_boot_process();
|
||||
}
|
||||
|
||||
void post_fs_data(int client) {
|
||||
@ -783,20 +776,20 @@ void post_fs_data(int client) {
|
||||
// If post-fs-data mode is started, it means startup succeeded
|
||||
setup_done = 1;
|
||||
|
||||
xmount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL);
|
||||
xmount(nullptr, "/", nullptr, MS_REMOUNT | MS_RDONLY, nullptr);
|
||||
|
||||
// Start log_daemon
|
||||
start_log_daemon();
|
||||
|
||||
LOGI("** post-fs-data mode running\n");
|
||||
|
||||
// Allocate buffer
|
||||
vec_init(&module_list);
|
||||
|
||||
// Merge, trim, mount magisk.img, which will also travel through the modules
|
||||
// After this, it will create the module list
|
||||
if (prepare_img())
|
||||
goto core_only; // Mounting fails, we can only do core only stuffs
|
||||
if (prepare_img()) {
|
||||
// Mounting fails, we can only do core only stuffs
|
||||
core_only();
|
||||
return;
|
||||
}
|
||||
|
||||
restorecon();
|
||||
chmod(SECURE_DIR, 0700);
|
||||
@ -806,30 +799,31 @@ void post_fs_data(int client) {
|
||||
exec_common_script("post-fs-data");
|
||||
|
||||
// Core only mode
|
||||
if (access(DISABLEFILE, F_OK) == 0)
|
||||
goto core_only;
|
||||
if (access(DISABLEFILE, F_OK) == 0) {
|
||||
core_only();
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute module scripts
|
||||
LOGI("* Running module post-fs-data scripts\n");
|
||||
exec_module_script("post-fs-data");
|
||||
|
||||
char *module;
|
||||
struct node_entry *sys_root, *ven_root = NULL, *child;
|
||||
|
||||
// Create the system root entry
|
||||
sys_root = xcalloc(sizeof(*sys_root), 1);
|
||||
sys_root->name = strdup("system");
|
||||
node_entry *sys_root = new node_entry("system");
|
||||
sys_root->status = IS_INTER;
|
||||
|
||||
int has_modules = 0;
|
||||
// Vendor root entry
|
||||
node_entry *ven_root = nullptr;
|
||||
|
||||
bool has_modules = false;
|
||||
|
||||
LOGI("* Loading modules\n");
|
||||
vec_for_each(&module_list, module) {
|
||||
for (auto &module : module_list) {
|
||||
// Read props
|
||||
snprintf(buf, PATH_MAX, "%s/%s/system.prop", MOUNTPOINT, module);
|
||||
if (access(buf, F_OK) == 0) {
|
||||
LOGI("%s: loading [system.prop]\n", module);
|
||||
read_prop_file(buf, 0);
|
||||
load_prop_file(buf, 0);
|
||||
}
|
||||
// Check whether enable auto_mount
|
||||
snprintf(buf, PATH_MAX, "%s/%s/auto_mount", MOUNTPOINT, module);
|
||||
@ -841,7 +835,7 @@ void post_fs_data(int client) {
|
||||
continue;
|
||||
|
||||
// Construct structure
|
||||
has_modules = 1;
|
||||
has_modules = true;
|
||||
LOGI("%s: constructing magic mount structure\n", module);
|
||||
// If /system/vendor exists in module, create a link outside
|
||||
snprintf(buf, PATH_MAX, "%s/%s/system/vendor", MOUNTPOINT, module);
|
||||
@ -850,44 +844,31 @@ void post_fs_data(int client) {
|
||||
unlink(buf2);
|
||||
xsymlink(buf, buf2);
|
||||
}
|
||||
construct_tree(module, sys_root);
|
||||
sys_root->create_module_tree(module);
|
||||
}
|
||||
|
||||
if (has_modules) {
|
||||
// Extract the vendor node out of system tree and swap with placeholder
|
||||
vec_for_each(sys_root->children, child) {
|
||||
for (auto &child : sys_root->children) {
|
||||
if (strcmp(child->name, "vendor") == 0) {
|
||||
ven_root = child;
|
||||
child = xcalloc(sizeof(*child), 1);
|
||||
child->type = seperate_vendor ? DT_LNK : DT_DIR;
|
||||
child = new node_entry("vendor", seperate_vendor ? DT_LNK : DT_DIR);
|
||||
child->parent = ven_root->parent;
|
||||
child->name = strdup("vendor");
|
||||
child->status = 0;
|
||||
// Swap!
|
||||
vec_cur(sys_root->children) = child;
|
||||
ven_root->parent = NULL;
|
||||
ven_root->parent = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Magic!!
|
||||
magic_mount(sys_root);
|
||||
if (ven_root) magic_mount(ven_root);
|
||||
sys_root->magic_mount();
|
||||
if (ven_root) ven_root->magic_mount();
|
||||
}
|
||||
|
||||
// Cleanup memory
|
||||
destroy_subtree(sys_root);
|
||||
if (ven_root) destroy_subtree(ven_root);
|
||||
delete sys_root;
|
||||
if (ven_root) delete ven_root;
|
||||
|
||||
core_only:
|
||||
// Systemless hosts
|
||||
if (access(HOSTSFILE, F_OK) == 0) {
|
||||
LOGI("* Enabling systemless hosts file support");
|
||||
bind_mount(HOSTSFILE, "/system/etc/hosts");
|
||||
}
|
||||
|
||||
auto_start_magiskhide();
|
||||
unblock_boot_process();
|
||||
core_only();
|
||||
}
|
||||
|
||||
void late_start(int client) {
|
||||
@ -903,7 +884,7 @@ void late_start(int client) {
|
||||
|
||||
if (!setup_done) {
|
||||
// The setup failed for some reason, reboot and try again
|
||||
exec_command_sync("/system/bin/reboot", NULL);
|
||||
exec_command_sync("/system/bin/reboot", nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -932,9 +913,9 @@ core_only:
|
||||
struct db_strings str;
|
||||
memset(&str, 0, sizeof(str));
|
||||
get_db_strings(db, SU_MANAGER, &str);
|
||||
if (validate_manager(str.s[SU_MANAGER], 0, NULL)) {
|
||||
if (validate_manager(str.s[SU_MANAGER], 0, nullptr)) {
|
||||
// There is no manager installed, install the stub
|
||||
exec_command_sync("/sbin/magiskinit", "-x", "manager", "/data/magisk.apk", NULL);
|
||||
exec_command_sync("/sbin/magiskinit", "-x", "manager", "/data/magisk.apk", nullptr);
|
||||
install_apk("/data/magisk.apk");
|
||||
}
|
||||
sqlite3_close_v2(db);
|
||||
@ -942,7 +923,9 @@ core_only:
|
||||
}
|
||||
|
||||
// All boot stage done, cleanup
|
||||
vec_deep_destroy(&module_list);
|
||||
for (auto &module : module_list)
|
||||
free(module);
|
||||
module_list.clear();
|
||||
}
|
||||
|
||||
void boot_complete(int client) {
|
@ -31,7 +31,7 @@ static void get_client_cred(int fd, struct ucred *cred) {
|
||||
|
||||
static void *request_handler(void *args) {
|
||||
int client = *((int *) args);
|
||||
free(args);
|
||||
delete (int *) args;
|
||||
int req = read_int(client);
|
||||
|
||||
struct ucred credential;
|
||||
@ -88,7 +88,7 @@ static void *request_handler(void *args) {
|
||||
static void main_daemon() {
|
||||
android_logging();
|
||||
setsid();
|
||||
setcon("u:r:"SEPOL_PROC_DOMAIN":s0");
|
||||
setcon("u:r:" SEPOL_PROC_DOMAIN ":s0");
|
||||
int fd = xopen("/dev/null", O_RDWR | O_CLOEXEC);
|
||||
xdup2(fd, STDOUT_FILENO);
|
||||
xdup2(fd, STDERR_FILENO);
|
||||
@ -123,7 +123,7 @@ static void main_daemon() {
|
||||
|
||||
// Loop forever to listen for requests
|
||||
while(1) {
|
||||
int *client = xmalloc(sizeof(int));
|
||||
int *client = new int;
|
||||
*client = xaccept4(fd, NULL, NULL, SOCK_CLOEXEC);
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, request_handler, client);
|
@ -10,8 +10,8 @@
|
||||
|
||||
#define DB_VERSION 7
|
||||
|
||||
static int ver_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
*((int *) v) = atoi(data[0]);
|
||||
static int ver_cb(void *ver, int, char **data, char **) {
|
||||
*((int *) ver) = atoi(data[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ static int ver_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
if (err) { \
|
||||
LOGE("sqlite3_exec: %s\n", err); \
|
||||
sqlite3_free(err); \
|
||||
return NULL; \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
static sqlite3 *open_and_init_db() {
|
||||
@ -27,7 +27,7 @@ static sqlite3 *open_and_init_db() {
|
||||
int ret = sqlite3_open(MAGISKDB, &db);
|
||||
if (ret) {
|
||||
LOGE("sqlite3 open failure: %s\n", sqlite3_errstr(ret));
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
int ver, upgrade = 0;
|
||||
char *err;
|
||||
@ -36,7 +36,7 @@ static sqlite3 *open_and_init_db() {
|
||||
if (ver > DB_VERSION) {
|
||||
// Don't support downgrading database
|
||||
sqlite3_close_v2(db);
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
if (ver < 3) {
|
||||
// Policies
|
||||
@ -44,20 +44,20 @@ static sqlite3 *open_and_init_db() {
|
||||
"CREATE TABLE IF NOT EXISTS policies "
|
||||
"(uid INT, package_name TEXT, policy INT, until INT, "
|
||||
"logging INT, notification INT, PRIMARY KEY(uid))",
|
||||
NULL, NULL, &err);
|
||||
nullptr, nullptr, &err);
|
||||
err_abort(err);
|
||||
// Logs
|
||||
sqlite3_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS logs "
|
||||
"(from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, "
|
||||
"to_uid INT, action INT, time INT, command TEXT)",
|
||||
NULL, NULL, &err);
|
||||
nullptr, nullptr, &err);
|
||||
err_abort(err);
|
||||
// Settings
|
||||
sqlite3_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS settings "
|
||||
"(key TEXT, value INT, PRIMARY KEY(key))",
|
||||
NULL, NULL, &err);
|
||||
nullptr, nullptr, &err);
|
||||
err_abort(err);
|
||||
ver = 3;
|
||||
upgrade = 1;
|
||||
@ -67,13 +67,13 @@ static sqlite3 *open_and_init_db() {
|
||||
sqlite3_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS strings "
|
||||
"(key TEXT, value TEXT, PRIMARY KEY(key))",
|
||||
NULL, NULL, &err);
|
||||
nullptr, nullptr, &err);
|
||||
err_abort(err);
|
||||
ver = 4;
|
||||
upgrade = 1;
|
||||
}
|
||||
if (ver == 4) {
|
||||
sqlite3_exec(db, "UPDATE policies SET uid=uid%100000", NULL, NULL, &err);
|
||||
sqlite3_exec(db, "UPDATE policies SET uid=uid%100000", nullptr, nullptr, &err);
|
||||
err_abort(err);
|
||||
/* Skip version 5 */
|
||||
ver = 6;
|
||||
@ -84,7 +84,7 @@ static sqlite3 *open_and_init_db() {
|
||||
sqlite3_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS hidelist "
|
||||
"(process TEXT, PRIMARY KEY(process))",
|
||||
NULL, NULL, &err);
|
||||
nullptr, nullptr, &err);
|
||||
err_abort(err);
|
||||
ver = 7;
|
||||
upgrade =1 ;
|
||||
@ -94,7 +94,7 @@ static sqlite3 *open_and_init_db() {
|
||||
// Set version
|
||||
char query[32];
|
||||
sprintf(query, "PRAGMA user_version=%d", ver);
|
||||
sqlite3_exec(db, query, NULL, NULL, &err);
|
||||
sqlite3_exec(db, query, nullptr, nullptr, &err);
|
||||
err_abort(err);
|
||||
}
|
||||
return db;
|
||||
@ -102,7 +102,7 @@ static sqlite3 *open_and_init_db() {
|
||||
|
||||
sqlite3 *get_magiskdb() {
|
||||
sqlite3 *db = open_and_init_db();
|
||||
if (db == NULL) {
|
||||
if (db == nullptr) {
|
||||
// Open fails, remove and reconstruct
|
||||
unlink(MAGISKDB);
|
||||
db = open_and_init_db();
|
||||
@ -111,7 +111,7 @@ sqlite3 *get_magiskdb() {
|
||||
}
|
||||
|
||||
static int settings_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
struct db_settings *dbs = v;
|
||||
auto dbs = (db_settings *) v;
|
||||
int key = -1, value;
|
||||
for (int i = 0; i < col_num; ++i) {
|
||||
if (strcmp(col_name[i], "key") == 0) {
|
||||
@ -131,7 +131,7 @@ static int settings_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
}
|
||||
|
||||
int get_db_settings(sqlite3 *db, int key, struct db_settings *dbs) {
|
||||
if (db == NULL)
|
||||
if (db == nullptr)
|
||||
return 1;
|
||||
char *err;
|
||||
if (key > 0) {
|
||||
@ -150,7 +150,7 @@ int get_db_settings(sqlite3 *db, int key, struct db_settings *dbs) {
|
||||
}
|
||||
|
||||
static int strings_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
struct db_strings *dbs = v;
|
||||
auto dbs = (db_strings *) v;
|
||||
int key = -1;
|
||||
char *value;
|
||||
for (int i = 0; i < col_num; ++i) {
|
||||
@ -171,7 +171,7 @@ static int strings_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
}
|
||||
|
||||
int get_db_strings(sqlite3 *db, int key, struct db_strings *str) {
|
||||
if (db == NULL)
|
||||
if (db == nullptr)
|
||||
return 1;
|
||||
char *err;
|
||||
if (key > 0) {
|
||||
@ -190,7 +190,7 @@ int get_db_strings(sqlite3 *db, int key, struct db_strings *str) {
|
||||
}
|
||||
|
||||
static int policy_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
struct su_access *su = v;
|
||||
auto su = (su_access *) v;
|
||||
for (int i = 0; i < col_num; i++) {
|
||||
if (strcmp(col_name[i], "policy") == 0)
|
||||
su->policy = (policy_t) atoi(data[i]);
|
||||
@ -204,11 +204,11 @@ static int policy_cb(void *v, int col_num, char **data, char **col_name) {
|
||||
}
|
||||
|
||||
int get_uid_policy(sqlite3 *db, int uid, struct su_access *su) {
|
||||
if (db == NULL)
|
||||
if (db == nullptr)
|
||||
return 1;
|
||||
char query[256], *err;
|
||||
sprintf(query, "SELECT policy, logging, notification FROM policies "
|
||||
"WHERE uid=%d AND (until=0 OR until>%li)", uid, time(NULL));
|
||||
"WHERE uid=%d AND (until=0 OR until>%li)", uid, time(nullptr));
|
||||
sqlite3_exec(db, query, policy_cb, su, &err);
|
||||
if (err) {
|
||||
LOGE("sqlite3_exec: %s\n", err);
|
||||
@ -219,7 +219,7 @@ int get_uid_policy(sqlite3 *db, int uid, struct su_access *su) {
|
||||
}
|
||||
|
||||
int validate_manager(char *alt_pkg, int userid, struct stat *st) {
|
||||
if (st == NULL) {
|
||||
if (st == nullptr) {
|
||||
struct stat stat;
|
||||
st = &stat;
|
||||
}
|
||||
@ -229,7 +229,7 @@ int validate_manager(char *alt_pkg, int userid, struct stat *st) {
|
||||
sprintf(app_path, "%s/%d/%s", base, userid, alt_pkg[0] ? alt_pkg : "xxx");
|
||||
if (stat(app_path, st)) {
|
||||
// Check the official package name
|
||||
sprintf(app_path, "%s/%d/"JAVA_PACKAGE_NAME, base, userid);
|
||||
sprintf(app_path, "%s/%d/" JAVA_PACKAGE_NAME, base, userid);
|
||||
if (stat(app_path, st)) {
|
||||
LOGE("su: cannot find manager");
|
||||
memset(st, 0, sizeof(*st));
|
||||
@ -256,7 +256,7 @@ int exec_sql(const char *sql) {
|
||||
sqlite3 *db = get_magiskdb();
|
||||
if (db) {
|
||||
char *err;
|
||||
sqlite3_exec(db, sql, print_cb, NULL, &err);
|
||||
sqlite3_exec(db, sql, print_cb, nullptr, &err);
|
||||
sqlite3_close_v2(db);
|
||||
if (err) {
|
||||
fprintf(stderr, "sql_err: %s\n", err);
|
@ -19,7 +19,7 @@
|
||||
#include "flags.h"
|
||||
|
||||
int log_daemon_started = 0;
|
||||
static struct vector log_cmd, clear_cmd;
|
||||
static Array<const char *> log_cmd, clear_cmd;
|
||||
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
enum {
|
||||
@ -33,7 +33,7 @@ struct log_listener {
|
||||
};
|
||||
|
||||
static int am_proc_start_filter(const char *log) {
|
||||
return strstr(log, "am_proc_start") != NULL;
|
||||
return strstr(log, "am_proc_start") != nullptr;
|
||||
}
|
||||
|
||||
static int magisk_log_filter(const char *log) {
|
||||
@ -52,17 +52,17 @@ static struct log_listener events[] = {
|
||||
};
|
||||
#define EVENT_NUM (sizeof(events) / sizeof(struct log_listener))
|
||||
|
||||
static void sigpipe_handler(int sig) {
|
||||
static void sigpipe_handler(int) {
|
||||
close(events[HIDE_EVENT].fd);
|
||||
events[HIDE_EVENT].fd = -1;
|
||||
}
|
||||
|
||||
static void *monitor_thread(void *args) {
|
||||
static void *monitor_thread(void *) {
|
||||
// Block SIGPIPE to prevent interruption
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
sigaddset(&block_set, SIGPIPE);
|
||||
pthread_sigmask(SIG_SETMASK, &block_set, NULL);
|
||||
pthread_sigmask(SIG_SETMASK, &block_set, nullptr);
|
||||
// Give the main daemon some time before we monitor it
|
||||
sleep(5);
|
||||
int fd;
|
||||
@ -77,12 +77,12 @@ static void *monitor_thread(void *args) {
|
||||
}
|
||||
}
|
||||
|
||||
static void *logcat_thread(void *args) {
|
||||
static void *logcat_thread(void *) {
|
||||
int log_fd = -1, log_pid;
|
||||
char line[4096];
|
||||
while (1) {
|
||||
// Start logcat
|
||||
log_pid = exec_array(0, &log_fd, NULL, (const char **) vec_entry(&log_cmd));
|
||||
log_pid = exec_array(0, &log_fd, nullptr, log_cmd.data());
|
||||
FILE *logs = fdopen(log_fd, "r");
|
||||
while (fgets(line, sizeof(line), logs)) {
|
||||
if (line[0] == '-')
|
||||
@ -99,12 +99,12 @@ static void *logcat_thread(void *args) {
|
||||
fclose(logs);
|
||||
log_fd = -1;
|
||||
kill(log_pid, SIGTERM);
|
||||
waitpid(log_pid, NULL, 0);
|
||||
waitpid(log_pid, nullptr, 0);
|
||||
|
||||
LOGI("magisklogd: logcat output EOF");
|
||||
// Clear buffer
|
||||
log_pid = exec_array(0, NULL, NULL, (const char **) vec_entry(&clear_cmd));
|
||||
waitpid(log_pid, NULL, 0);
|
||||
log_pid = exec_array(0, nullptr, nullptr, clear_cmd.data());
|
||||
waitpid(log_pid, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,36 +117,42 @@ static void log_daemon() {
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = sigpipe_handler;
|
||||
sigaction(SIGPIPE, &act, NULL);
|
||||
sigaction(SIGPIPE, &act, nullptr);
|
||||
|
||||
// Setup log dumps
|
||||
rename(LOGFILE, LOGFILE ".bak");
|
||||
events[LOG_EVENT].fd = xopen(LOGFILE, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC | O_APPEND, 0644);
|
||||
|
||||
// Construct cmdline
|
||||
vec_init(&log_cmd);
|
||||
vec_push_back(&log_cmd, MIRRDIR "/system/bin/logcat");
|
||||
log_cmd.push_back(MIRRDIR "/system/bin/logcat");
|
||||
// Test whether these buffers actually works
|
||||
const char* b[] = { "main", "events", "crash" };
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (exec_command_sync(MIRRDIR "/system/bin/logcat", "-b", b[i], "-d", "-f", "/dev/null", NULL) == 0)
|
||||
vec_push_back_all(&log_cmd, "-b", b[i], NULL);
|
||||
if (exec_command_sync(MIRRDIR "/system/bin/logcat", "-b", b[i], "-d", "-f", "/dev/null", nullptr) == 0) {
|
||||
log_cmd.push_back("-b");
|
||||
log_cmd.push_back(b[i]);
|
||||
}
|
||||
}
|
||||
chmod("/dev/null", 0666);
|
||||
vec_dup(&log_cmd, &clear_cmd);
|
||||
vec_push_back_all(&log_cmd, "-v", "threadtime", "-s", "am_proc_start", "Magisk", NULL);
|
||||
clear_cmd = log_cmd;
|
||||
log_cmd.push_back("-v");
|
||||
log_cmd.push_back("threadtime");
|
||||
log_cmd.push_back("-s");
|
||||
log_cmd.push_back("am_proc_start");
|
||||
log_cmd.push_back("Magisk");
|
||||
#ifdef MAGISK_DEBUG
|
||||
vec_push_back(&log_cmd, "*:F");
|
||||
log_cmd.push_back("*:F");
|
||||
#endif
|
||||
vec_push_back(&log_cmd, NULL);
|
||||
vec_push_back(&clear_cmd, "-c");
|
||||
vec_push_back(&clear_cmd, NULL);
|
||||
log_cmd.push_back(nullptr);
|
||||
|
||||
clear_cmd.push_back("-c");
|
||||
clear_cmd.push_back(nullptr);
|
||||
|
||||
// Start worker threads
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, monitor_thread, NULL);
|
||||
pthread_create(&thread, nullptr, monitor_thread, nullptr);
|
||||
pthread_detach(thread);
|
||||
xpthread_create(&thread, NULL, logcat_thread, NULL);
|
||||
xpthread_create(&thread, nullptr, logcat_thread, nullptr);
|
||||
pthread_detach(thread);
|
||||
|
||||
// Handle socket requests
|
||||
@ -157,7 +163,7 @@ static void log_daemon() {
|
||||
exit(1);
|
||||
xlisten(sockfd, 10);
|
||||
while(1) {
|
||||
int fd = xaccept4(sockfd, NULL, NULL, SOCK_CLOEXEC);
|
||||
int fd = xaccept4(sockfd, nullptr, nullptr, SOCK_CLOEXEC);
|
||||
switch(read_int(fd)) {
|
||||
case HIDE_CONNECT:
|
||||
pthread_mutex_lock(&lock);
|
||||
@ -175,7 +181,7 @@ static void log_daemon() {
|
||||
|
||||
int start_log_daemon() {
|
||||
if (!log_daemon_started) {
|
||||
if (exec_command_sync(MIRRDIR "/system/bin/logcat", "-d", "-f", "/dev/null", NULL) == 0) {
|
||||
if (exec_command_sync(MIRRDIR "/system/bin/logcat", "-d", "-f", "/dev/null", nullptr) == 0) {
|
||||
if (fork_dont_care() == 0)
|
||||
log_daemon();
|
||||
log_daemon_started = 1;
|
@ -26,7 +26,7 @@ static int create_links(const char *bin, const char *path) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usage() {
|
||||
[[noreturn]] static void usage() {
|
||||
fprintf(stderr,
|
||||
"Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") (by topjohnwu) multi-call binary\n"
|
||||
"\n"
|
||||
@ -117,5 +117,4 @@ int magisk_main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
usage();
|
||||
return 1;
|
||||
}
|
@ -50,6 +50,7 @@ int socket_accept(int sockfd, int timeout) {
|
||||
int recv_fd(int sockfd) {
|
||||
// Need to receive data from the message, otherwise don't care about it.
|
||||
char iovbuf;
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
struct iovec iov = {
|
||||
.iov_base = &iovbuf,
|
||||
@ -79,7 +80,7 @@ int recv_fd(int sockfd) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
if (cmsg == NULL ||
|
||||
cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
|
||||
@ -104,8 +105,9 @@ error:
|
||||
*/
|
||||
void send_fd(int sockfd, int fd) {
|
||||
// Need to send some data in the message, this will do.
|
||||
char junk[] = { '\0' };
|
||||
struct iovec iov = {
|
||||
.iov_base = "",
|
||||
.iov_base = junk,
|
||||
.iov_len = 1,
|
||||
};
|
||||
|
||||
@ -166,7 +168,7 @@ void write_int_be(int fd, int val) {
|
||||
}
|
||||
|
||||
static char *rd_str(int fd, int len) {
|
||||
char* val = xmalloc(sizeof(char) * (len + 1));
|
||||
char *val = (char *) xmalloc(sizeof(char) * (len + 1));
|
||||
xxread(fd, val, len);
|
||||
val[len] = '\0';
|
||||
return val;
|
@ -4,10 +4,6 @@
|
||||
#include <sqlite3.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/***************
|
||||
* DB Settings *
|
||||
***************/
|
||||
@ -19,7 +15,7 @@ extern "C" {
|
||||
"mnt_ns" \
|
||||
})
|
||||
|
||||
#define DB_SETTINGS_NUM (sizeof(DB_SETTING_KEYS) / sizeof(char*))
|
||||
#define DB_SETTINGS_NUM 3
|
||||
|
||||
// Settings indices
|
||||
enum {
|
||||
@ -52,15 +48,14 @@ enum {
|
||||
|
||||
struct db_settings {
|
||||
int v[DB_SETTINGS_NUM];
|
||||
db_settings()
|
||||
: v {
|
||||
ROOT_ACCESS_APPS_AND_ADB,
|
||||
MULTIUSER_MODE_OWNER_ONLY,
|
||||
NAMESPACE_MODE_REQUESTER
|
||||
} {}
|
||||
};
|
||||
|
||||
#define DEFAULT_DB_SETTINGS \
|
||||
(struct db_settings) { .v = { \
|
||||
ROOT_ACCESS_APPS_AND_ADB, \
|
||||
MULTIUSER_MODE_OWNER_ONLY, \
|
||||
NAMESPACE_MODE_REQUESTER, \
|
||||
}}
|
||||
|
||||
/**************
|
||||
* DB Strings *
|
||||
**************/
|
||||
@ -70,7 +65,7 @@ NAMESPACE_MODE_REQUESTER, \
|
||||
"requester", \
|
||||
})
|
||||
|
||||
#define DB_STRING_NUM (sizeof(DB_STRING_KEYS) / sizeof(char*))
|
||||
#define DB_STRING_NUM 1
|
||||
|
||||
// Strings indices
|
||||
enum {
|
||||
@ -79,6 +74,10 @@ enum {
|
||||
|
||||
struct db_strings {
|
||||
char s[DB_STRING_NUM][128];
|
||||
db_strings() {
|
||||
for (int i = 0; i < DB_STRING_NUM; ++i)
|
||||
s[i][0] = '\0';
|
||||
}
|
||||
};
|
||||
|
||||
/*************
|
||||
@ -97,19 +96,19 @@ struct su_access {
|
||||
int notify;
|
||||
};
|
||||
|
||||
#define DEFAULT_SU_ACCESS (struct su_access) { \
|
||||
#define DEFAULT_SU_ACCESS (su_access) { \
|
||||
.policy = QUERY, \
|
||||
.log = 1, \
|
||||
.notify = 1 \
|
||||
}
|
||||
|
||||
#define SILENT_SU_ACCESS (struct su_access) { \
|
||||
#define SILENT_SU_ACCESS (su_access) { \
|
||||
.policy = ALLOW, \
|
||||
.log = 0, \
|
||||
.notify = 0 \
|
||||
}
|
||||
|
||||
#define NO_SU_ACCESS (struct su_access) { \
|
||||
#define NO_SU_ACCESS (su_access) { \
|
||||
.policy = DENY, \
|
||||
.log = 0, \
|
||||
.notify = 0 \
|
||||
@ -126,8 +125,4 @@ int get_uid_policy(sqlite3 *db, int uid, struct su_access *su);
|
||||
int validate_manager(char *alt_pkg, int userid, struct stat *st);
|
||||
int exec_sql(const char *sql);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //DB_H
|
||||
|
@ -1,6 +1,10 @@
|
||||
#ifndef IMG_H
|
||||
#define IMG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int create_img(const char *img, int size);
|
||||
int resize_img(const char *img, int size);
|
||||
char *mount_image(const char *img, const char *target);
|
||||
@ -8,4 +12,8 @@ int umount_image(const char *target, const char *device);
|
||||
int merge_img(const char *source, const char *target);
|
||||
int trim_img(const char *img, const char *mount, char *loop);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //IMG_H
|
||||
|
@ -45,8 +45,8 @@ extern "C" {
|
||||
|
||||
extern char *argv0; /* For changing process name */
|
||||
|
||||
#define applet_names ((char *[]) { "magisk", "su", "resetprop", "magiskhide", "imgtool", NULL })
|
||||
#define init_applet ((char *[]) { "magiskpolicy", "supolicy", NULL })
|
||||
#define applet_names ((const char *[]) { "magisk", "su", "resetprop", "magiskhide", "imgtool", NULL })
|
||||
#define init_applet ((const char *[]) { "magiskpolicy", "supolicy", NULL })
|
||||
|
||||
extern int (*applet_main[]) (int, char *[]), (*init_applet_main[]) (int, char *[]);
|
||||
|
||||
|
@ -1,25 +1,12 @@
|
||||
/* resetprop.h - API for resetprop
|
||||
*/
|
||||
|
||||
#ifndef _RESETPROP_H_
|
||||
#define _RESETPROP_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
int prop_exist(const char *name);
|
||||
int setprop(const char *name, const char *value);
|
||||
int setprop2(const char *name, const char *value, const int trigger);
|
||||
char *getprop(const char *name);
|
||||
char *getprop2(const char *name, int persist);
|
||||
int deleteprop(const char *name);
|
||||
int deleteprop2(const char *name, const int persist);
|
||||
int read_prop_file(const char* filename, const int trigger);
|
||||
void getprop_all(void (*callback)(const char *, const char *, void *), void *cookie, int persist);
|
||||
int setprop(const char *name, const char *value, const bool trigger = true);
|
||||
char *getprop(const char *name, bool persist = false);
|
||||
void getprop(void (*callback)(const char *, const char *, void *), void *cookie, bool persist = false);
|
||||
int deleteprop(const char *name, bool persist = false);
|
||||
int load_prop_file(const char *filename, const bool trigger = true);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -51,7 +51,7 @@ void hide_sensitive_props() {
|
||||
value = getprop(prop_key[i]);
|
||||
if (value) {
|
||||
if (strcmp(value, prop_value[i]) != 0)
|
||||
setprop2(prop_key[i], prop_value[i], 0);
|
||||
setprop(prop_key[i], prop_value[i], false);
|
||||
free(value);
|
||||
}
|
||||
}
|
||||
@ -133,10 +133,10 @@ static void kill_process(const char *name) {
|
||||
|
||||
void clean_magisk_props() {
|
||||
LOGD("hide_utils: Cleaning magisk props\n");
|
||||
getprop_all([](const char *name, auto, auto) -> void {
|
||||
getprop([](const char *name, auto, auto) -> void {
|
||||
if (strstr(name, "magisk"))
|
||||
deleteprop2(name, 0);
|
||||
}, nullptr, 0);
|
||||
deleteprop(name);
|
||||
}, nullptr, false);
|
||||
}
|
||||
|
||||
static int add_list(sqlite3 *db, char *proc) {
|
||||
|
@ -39,14 +39,14 @@ int launch_magiskhide() {
|
||||
if (!log_daemon_started) {
|
||||
setprop(MAGISKHIDE_PROP, "0");
|
||||
// Remove without actually removing persist props
|
||||
deleteprop2(MAGISKHIDE_PROP, 0);
|
||||
deleteprop(MAGISKHIDE_PROP);
|
||||
return LOGCAT_DISABLED;
|
||||
}
|
||||
|
||||
hide_enabled = 1;
|
||||
LOGI("* Starting MagiskHide\n");
|
||||
|
||||
deleteprop2(MAGISKHIDE_PROP, 1);
|
||||
deleteprop(MAGISKHIDE_PROP, true);
|
||||
|
||||
hide_sensitive_props();
|
||||
|
||||
@ -77,7 +77,7 @@ int stop_magiskhide() {
|
||||
hide_enabled = 0;
|
||||
setprop(MAGISKHIDE_PROP, "0");
|
||||
// Remove without actually removing persist props
|
||||
deleteprop2(MAGISKHIDE_PROP, 0);
|
||||
deleteprop(MAGISKHIDE_PROP);
|
||||
pthread_kill(proc_monitor_thread, TERM_THREAD);
|
||||
|
||||
return DAEMON_SUCCESS;
|
||||
|
@ -9,9 +9,7 @@
|
||||
#define TERM_THREAD SIGUSR1
|
||||
|
||||
// Daemon entries
|
||||
extern "C" {
|
||||
int launch_magiskhide();
|
||||
}
|
||||
int stop_magiskhide();
|
||||
int add_list(int client);
|
||||
int rm_list(int client);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* img.c - All image related functions
|
||||
/* img.cpp - All image related functions
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
@ -112,9 +112,9 @@ static int init_resetprop() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_props(int persist) {
|
||||
static void print_props(bool persist) {
|
||||
auto prop_list = Array<prop_t>();
|
||||
getprop_all(collect_props, &prop_list, persist);
|
||||
getprop(collect_props, &prop_list, persist);
|
||||
prop_list.sort();
|
||||
for (auto &prop : prop_list)
|
||||
printf("[%s]: [%s]\n", prop.name, prop.value);
|
||||
@ -129,12 +129,8 @@ int prop_exist(const char *name) {
|
||||
return __system_property_find(name) != nullptr;
|
||||
}
|
||||
|
||||
char *getprop(const char *name) {
|
||||
return getprop2(name, 0);
|
||||
}
|
||||
|
||||
// Get prop by name, return string (should free manually!)
|
||||
char *getprop2(const char *name, int persist) {
|
||||
char *getprop(const char *name, bool persist) {
|
||||
if (!check_legal_property_name(name) || init_resetprop())
|
||||
return nullptr;
|
||||
const prop_info *pi = __system_property_find(name);
|
||||
@ -157,7 +153,7 @@ char *getprop2(const char *name, int persist) {
|
||||
}
|
||||
}
|
||||
|
||||
void getprop_all(void (*callback)(const char *, const char *, void *), void *cookie, int persist) {
|
||||
void getprop(void (*callback)(const char *, const char *, void *), void *cookie, bool persist) {
|
||||
if (init_resetprop()) return;
|
||||
read_cb_t read_cb(callback, cookie);
|
||||
__system_property_foreach(read_props, &read_cb);
|
||||
@ -167,11 +163,7 @@ void getprop_all(void (*callback)(const char *, const char *, void *), void *coo
|
||||
}
|
||||
}
|
||||
|
||||
int setprop(const char *name, const char *value) {
|
||||
return setprop2(name, value, 1);
|
||||
}
|
||||
|
||||
int setprop2(const char *name, const char *value, const int trigger) {
|
||||
int setprop(const char *name, const char *value, const bool trigger) {
|
||||
if (!check_legal_property_name(name))
|
||||
return 1;
|
||||
if (init_resetprop())
|
||||
@ -205,11 +197,7 @@ int setprop2(const char *name, const char *value, const int trigger) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int deleteprop(const char *name) {
|
||||
return deleteprop2(name, 1);
|
||||
}
|
||||
|
||||
int deleteprop2(const char *name, int persist) {
|
||||
int deleteprop(const char *name, bool persist) {
|
||||
if (!check_legal_property_name(name))
|
||||
return 1;
|
||||
if (init_resetprop()) return -1;
|
||||
@ -221,7 +209,7 @@ int deleteprop2(const char *name, int persist) {
|
||||
return __system_property_del(name) && !(persist && strncmp(name, "persist.", 8) == 0);
|
||||
}
|
||||
|
||||
int read_prop_file(const char* filename, const int trigger) {
|
||||
int load_prop_file(const char *filename, const bool trigger) {
|
||||
if (init_resetprop()) return -1;
|
||||
LOGD("resetprop: Load prop file [%s]\n", filename);
|
||||
FILE *fp = fopen(filename, "r");
|
||||
@ -255,7 +243,7 @@ int read_prop_file(const char* filename, const int trigger) {
|
||||
if ( ((pch == nullptr) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue;
|
||||
// Separate the string
|
||||
*pch = '\0';
|
||||
setprop2(line + i, pch + 1, trigger);
|
||||
setprop(line + i, pch + 1, trigger);
|
||||
}
|
||||
free(line);
|
||||
fclose(fp);
|
||||
@ -265,7 +253,7 @@ int read_prop_file(const char* filename, const int trigger) {
|
||||
int resetprop_main(int argc, char *argv[]) {
|
||||
log_cb.d = [](auto fmt, auto ap) -> int { return verbose ? vfprintf(stderr, fmt, ap) : 0; };
|
||||
|
||||
int trigger = 1, persist = 0;
|
||||
bool trigger = true, persist = false;
|
||||
char *argv0 = argv[0], *prop;
|
||||
|
||||
--argc;
|
||||
@ -277,9 +265,9 @@ int resetprop_main(int argc, char *argv[]) {
|
||||
switch (argv[0][idx]) {
|
||||
case '-':
|
||||
if (strcmp(argv[0], "--file") == 0 && argc == 2) {
|
||||
return read_prop_file(argv[1], trigger);
|
||||
return load_prop_file(argv[1], trigger);
|
||||
} else if (strcmp(argv[0], "--delete") == 0 && argc == 2) {
|
||||
return deleteprop2(argv[1], persist);
|
||||
return deleteprop(argv[1], persist);
|
||||
} else if (strcmp(argv[0], "--help") == 0) {
|
||||
usage(argv0);
|
||||
}
|
||||
@ -309,13 +297,13 @@ int resetprop_main(int argc, char *argv[]) {
|
||||
print_props(persist);
|
||||
return 0;
|
||||
case 1:
|
||||
prop = getprop2(argv[0], persist);
|
||||
prop = getprop(argv[0], persist);
|
||||
if (prop == nullptr) return 1;
|
||||
printf("%s\n", prop);
|
||||
free(prop);
|
||||
return 0;
|
||||
case 2:
|
||||
return setprop2(argv[0], argv[1], trigger);
|
||||
return setprop(argv[0], argv[1], trigger);
|
||||
default:
|
||||
usage(argv0);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am"
|
||||
|
||||
static char *get_command(const struct su_request *to) {
|
||||
static const char *get_command(const struct su_request *to) {
|
||||
if (to->command[0])
|
||||
return to->command;
|
||||
if (to->shell[0])
|
||||
@ -27,7 +27,7 @@ static char *get_command(const struct su_request *to) {
|
||||
return DEFAULT_SHELL;
|
||||
}
|
||||
|
||||
static void silent_run(char * const args[]) {
|
||||
static void silent_run(const char *args[]) {
|
||||
if (fork_dont_care())
|
||||
return;
|
||||
int zero = open("/dev/zero", O_RDONLY | O_CLOEXEC);
|
||||
@ -36,7 +36,7 @@ static void silent_run(char * const args[]) {
|
||||
xdup2(null, 1);
|
||||
xdup2(null, 2);
|
||||
setenv("CLASSPATH", "/system/framework/am.jar", 1);
|
||||
execv(args[0], args);
|
||||
execv(args[0], (char **) args);
|
||||
PLOGE("exec am");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
@ -71,7 +71,7 @@ void app_log(struct su_context *ctx) {
|
||||
char policy[2];
|
||||
sprintf(policy, "%d", ctx->info->access.policy);
|
||||
|
||||
char *cmd[] = {
|
||||
const char *cmd[] = {
|
||||
AM_PATH, "broadcast",
|
||||
"-a", "android.intent.action.BOOT_COMPLETED",
|
||||
"-p", DB_STR(ctx->info, SU_MANAGER),
|
||||
@ -84,7 +84,7 @@ void app_log(struct su_context *ctx) {
|
||||
"--ei", "policy", policy,
|
||||
"--es", "command", get_command(&ctx->req),
|
||||
"--ez", "notify", ctx->info->access.notify ? "true" : "false",
|
||||
NULL
|
||||
nullptr
|
||||
};
|
||||
silent_run(cmd);
|
||||
}
|
||||
@ -101,7 +101,7 @@ void app_notify(struct su_context *ctx) {
|
||||
char policy[2];
|
||||
sprintf(policy, "%d", ctx->info->access.policy);
|
||||
|
||||
char *cmd[] = {
|
||||
const char *cmd[] = {
|
||||
AM_PATH, "broadcast",
|
||||
"-a", "android.intent.action.BOOT_COMPLETED",
|
||||
"-p", DB_STR(ctx->info, SU_MANAGER),
|
||||
@ -110,7 +110,7 @@ void app_notify(struct su_context *ctx) {
|
||||
"--es", "action", "notify",
|
||||
"--ei", "from.uid", fromUid,
|
||||
"--ei", "policy", policy,
|
||||
NULL
|
||||
nullptr
|
||||
};
|
||||
silent_run(cmd);
|
||||
}
|
||||
@ -118,15 +118,15 @@ void app_notify(struct su_context *ctx) {
|
||||
void app_connect(const char *socket, struct su_info *info) {
|
||||
char user[8];
|
||||
setup_user(user, info);
|
||||
char *cmd[] = {
|
||||
const char *cmd[] = {
|
||||
AM_PATH, "broadcast",
|
||||
"-a", "android.intent.action.BOOT_COMPLETED",
|
||||
"-p", DB_STR(info, SU_MANAGER),
|
||||
"-f", "0x00000020",
|
||||
"--user", user,
|
||||
"--es", "action", "request",
|
||||
"--es", "socket", (char *) socket,
|
||||
NULL
|
||||
"--es", "socket", socket,
|
||||
nullptr
|
||||
};
|
||||
silent_run(cmd);
|
||||
}
|
||||
@ -135,4 +135,3 @@ void socket_send_request(int fd, struct su_info *info) {
|
||||
write_key_token(fd, "uid", info->uid);
|
||||
write_string_be(fd, "eof");
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
* helper functions to handle raw input mode and terminal window resizing
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
@ -8,7 +8,6 @@
|
||||
/* su.c - The main function running in the daemon
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -95,6 +94,12 @@ static void setup_sighandlers(void (*handler)(int)) {
|
||||
}
|
||||
}
|
||||
|
||||
// Default values
|
||||
su_req_base::su_req_base()
|
||||
: uid(UID_ROOT), login(false), keepenv(false), mount_master(false) {}
|
||||
su_request::su_request()
|
||||
: shell(DEFAULT_SHELL), command("") {}
|
||||
|
||||
/*
|
||||
* Connect daemon, send argc, argv, cwd, pts slave
|
||||
*/
|
||||
@ -112,15 +117,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
||||
struct su_request su_req = {
|
||||
.uid = UID_ROOT,
|
||||
.login = 0,
|
||||
.keepenv = 0,
|
||||
.mount_master = 0,
|
||||
.shell = DEFAULT_SHELL,
|
||||
.command = "",
|
||||
};
|
||||
|
||||
su_request su_req;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
// Replace -cn with -z, -mm with -M for supporting getopt_long
|
||||
@ -140,11 +137,11 @@ int su_client_main(int argc, char *argv[]) {
|
||||
usage(EXIT_SUCCESS);
|
||||
break;
|
||||
case 'l':
|
||||
su_req.login = 1;
|
||||
su_req.login = true;
|
||||
break;
|
||||
case 'm':
|
||||
case 'p':
|
||||
su_req.keepenv = 1;
|
||||
su_req.keepenv = true;
|
||||
break;
|
||||
case 's':
|
||||
su_req.shell = optarg;
|
||||
@ -159,7 +156,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
// Do nothing, placed here for legacy support :)
|
||||
break;
|
||||
case 'M':
|
||||
su_req.mount_master = 1;
|
||||
su_req.mount_master = true;
|
||||
break;
|
||||
default:
|
||||
/* Bionic getopt_long doesn't terminate its error output by newline */
|
||||
@ -169,7 +166,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
if (optind < argc && strcmp(argv[optind], "-") == 0) {
|
||||
su_req.login = 1;
|
||||
su_req.login = true;
|
||||
optind++;
|
||||
}
|
||||
/* username or uid */
|
||||
@ -200,7 +197,7 @@ int su_client_main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// Send su_request
|
||||
xwrite(fd, &su_req, 4 * sizeof(unsigned));
|
||||
xwrite(fd, &su_req, sizeof(su_req_base));
|
||||
write_string(fd, su_req.shell);
|
||||
write_string(fd, su_req.command);
|
||||
|
@ -17,9 +17,9 @@
|
||||
#define ATTY_OUT 2
|
||||
#define ATTY_ERR 4
|
||||
|
||||
struct su_info {
|
||||
class su_info {
|
||||
public:
|
||||
unsigned uid; /* Unique key to find su_info */
|
||||
pthread_mutex_t lock; /* Internal lock */
|
||||
int count; /* Just a count for debugging purpose */
|
||||
|
||||
/* These values should be guarded with internal lock */
|
||||
@ -31,19 +31,33 @@ struct su_info {
|
||||
/* These should be guarded with global cache lock */
|
||||
int ref;
|
||||
int life;
|
||||
|
||||
su_info(unsigned uid);
|
||||
~su_info();
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
pthread_mutex_t _lock; /* Internal lock */
|
||||
};
|
||||
|
||||
#define DB_SET(i, e) (i)->dbs.v[e]
|
||||
#define DB_STR(i, e) (i)->str.s[e]
|
||||
|
||||
struct su_request {
|
||||
struct su_req_base {
|
||||
unsigned uid;
|
||||
unsigned login;
|
||||
unsigned keepenv;
|
||||
unsigned mount_master;
|
||||
char *shell;
|
||||
char *command;
|
||||
};
|
||||
bool login;
|
||||
bool keepenv;
|
||||
bool mount_master;
|
||||
protected:
|
||||
su_req_base();
|
||||
} __attribute__((packed));
|
||||
|
||||
struct su_request : public su_req_base {
|
||||
const char *shell;
|
||||
const char *command;
|
||||
su_request();
|
||||
} __attribute__((packed));
|
||||
|
||||
struct su_context {
|
||||
struct su_info *info;
|
||||
|
@ -1,8 +1,3 @@
|
||||
/* su_daemon.c - The entrypoint for su, connect to daemon and send correct info
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
@ -25,32 +20,45 @@
|
||||
#define TIMEOUT 3
|
||||
|
||||
#define LOCK_CACHE() pthread_mutex_lock(&cache_lock)
|
||||
#define LOCK_INFO() pthread_mutex_lock(&info->lock)
|
||||
#define UNLOCK_CACHE() pthread_mutex_unlock(&cache_lock)
|
||||
#define UNLOCK_INFO() pthread_mutex_unlock(&info->lock)
|
||||
|
||||
static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static struct su_info *cache;
|
||||
static su_info *cache;
|
||||
|
||||
su_info::su_info(unsigned uid) :
|
||||
uid(uid), access(DEFAULT_SU_ACCESS), _lock(PTHREAD_MUTEX_INITIALIZER),
|
||||
count(0), ref(0), life(0), mgr_st({}) {}
|
||||
|
||||
su_info::~su_info() {
|
||||
pthread_mutex_destroy(&_lock);
|
||||
}
|
||||
|
||||
void su_info::lock() {
|
||||
pthread_mutex_lock(&_lock);
|
||||
}
|
||||
|
||||
void su_info::unlock() {
|
||||
pthread_mutex_unlock(&_lock);
|
||||
}
|
||||
|
||||
static void *info_collector(void *node) {
|
||||
struct su_info *info = node;
|
||||
su_info *info = (su_info *) node;
|
||||
while (1) {
|
||||
sleep(1);
|
||||
if (info->life) {
|
||||
LOCK_CACHE();
|
||||
if (--info->life == 0 && cache && info->uid == cache->uid)
|
||||
cache = NULL;
|
||||
cache = nullptr;
|
||||
UNLOCK_CACHE();
|
||||
}
|
||||
if (!info->life && !info->ref) {
|
||||
pthread_mutex_destroy(&info->lock);
|
||||
free(info);
|
||||
return NULL;
|
||||
delete info;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void database_check(struct su_info *info) {
|
||||
static void database_check(su_info *info) {
|
||||
int uid = info->uid;
|
||||
sqlite3 *db = get_magiskdb();
|
||||
if (db) {
|
||||
@ -84,20 +92,16 @@ static void database_check(struct su_info *info) {
|
||||
}
|
||||
|
||||
static struct su_info *get_su_info(unsigned uid) {
|
||||
struct su_info *info;
|
||||
int cache_miss = 0;
|
||||
su_info *info;
|
||||
bool cache_miss = false;
|
||||
|
||||
LOCK_CACHE();
|
||||
|
||||
if (cache && cache->uid == uid) {
|
||||
info = cache;
|
||||
} else {
|
||||
cache_miss = 1;
|
||||
info = xcalloc(1, sizeof(*info));
|
||||
info->uid = uid;
|
||||
info->dbs = DEFAULT_DB_SETTINGS;
|
||||
info->access = DEFAULT_SU_ACCESS;
|
||||
pthread_mutex_init(&info->lock, NULL);
|
||||
cache_miss = true;
|
||||
info = new su_info(uid);
|
||||
cache = info;
|
||||
}
|
||||
|
||||
@ -108,7 +112,7 @@ static struct su_info *get_su_info(unsigned uid) {
|
||||
// Start a thread to maintain the cache
|
||||
if (cache_miss) {
|
||||
pthread_t thread;
|
||||
xpthread_create(&thread, NULL, info_collector, info);
|
||||
xpthread_create(&thread, nullptr, info_collector, info);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
|
||||
@ -117,7 +121,7 @@ static struct su_info *get_su_info(unsigned uid) {
|
||||
LOGD("su: request from uid=[%d] (#%d)\n", info->uid, ++info->count);
|
||||
|
||||
// Lock before the policy is determined
|
||||
LOCK_INFO();
|
||||
info->lock();
|
||||
|
||||
if (info->access.policy == QUERY) {
|
||||
// Not cached, get data from database
|
||||
@ -173,14 +177,14 @@ static struct su_info *get_su_info(unsigned uid) {
|
||||
} else {
|
||||
socket_send_request(fd, info);
|
||||
int ret = read_int_be(fd);
|
||||
info->access.policy = ret < 0 ? DENY : ret;
|
||||
info->access.policy = ret < 0 ? DENY : static_cast<policy_t>(ret);
|
||||
close(fd);
|
||||
}
|
||||
close(sockfd);
|
||||
}
|
||||
|
||||
// Unlock
|
||||
UNLOCK_INFO();
|
||||
info->unlock();
|
||||
|
||||
return info;
|
||||
}
|
||||
@ -224,7 +228,7 @@ static void set_identity(unsigned uid) {
|
||||
void su_daemon_handler(int client, struct ucred *credential) {
|
||||
LOGD("su: request from client: %d\n", client);
|
||||
|
||||
struct su_info *info = get_su_info(credential->uid);
|
||||
su_info *info = get_su_info(credential->uid);
|
||||
|
||||
// Fail fast
|
||||
if (info->access.policy == DENY && DB_STR(info, SU_MANAGER)[0] == '\0') {
|
||||
@ -289,7 +293,7 @@ void su_daemon_handler(int client, struct ucred *credential) {
|
||||
}
|
||||
|
||||
// Read su_request
|
||||
xxread(client, &ctx.req, 4 * sizeof(unsigned));
|
||||
xxread(client, &ctx.req, sizeof(su_req_base));
|
||||
ctx.req.shell = read_string(client);
|
||||
ctx.req.command = read_string(client);
|
||||
|
||||
@ -367,7 +371,7 @@ void su_daemon_handler(int client, struct ucred *credential) {
|
||||
app_notify(&ctx);
|
||||
|
||||
if (info->access.policy == ALLOW) {
|
||||
char* argv[] = { NULL, NULL, NULL, NULL };
|
||||
const char *argv[] = { nullptr, nullptr, nullptr, nullptr };
|
||||
|
||||
argv[0] = ctx.req.login ? "-" : ctx.req.shell;
|
||||
|
||||
@ -381,7 +385,7 @@ void su_daemon_handler(int client, struct ucred *credential) {
|
||||
populate_environment(&ctx.req);
|
||||
set_identity(ctx.req.uid);
|
||||
|
||||
execvp(ctx.req.shell, argv);
|
||||
execvp(ctx.req.shell, (char **) argv);
|
||||
fprintf(stderr, "Cannot execute %s: %s\n", ctx.req.shell, strerror(errno));
|
||||
PLOGE("exec");
|
||||
exit(EXIT_FAILURE);
|
||||
@ -391,4 +395,3 @@ void su_daemon_handler(int client, struct ucred *credential) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ LOCAL_SRC_FILES := \
|
||||
selinux.cpp \
|
||||
logging.cpp \
|
||||
xwrap.cpp \
|
||||
vector.c \
|
||||
legacy.c
|
||||
vector.c
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
@ -66,6 +66,15 @@ public:
|
||||
T* _node;
|
||||
};
|
||||
|
||||
T& operator = (const T& a) {
|
||||
_size = a._size;
|
||||
_capacity = a._capacity;
|
||||
_data = new T[_capacity];
|
||||
for(int i = 0; i < _size; ++i)
|
||||
_data[i] = (T&&) a[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator begin() const { return iterator(_data); }
|
||||
|
||||
iterator end() const { return iterator(_data + _size); }
|
||||
@ -149,7 +158,8 @@ private:
|
||||
_capacity *= 2;
|
||||
T* temp = _data;
|
||||
_data = new T[_capacity];
|
||||
for(int i = 0; i < _size; ++i) _data[i] = (T&&) temp[i];
|
||||
for(int i = 0; i < _size; ++i)
|
||||
_data[i] = (T&&) temp[i];
|
||||
delete [] temp;
|
||||
}
|
||||
};
|
||||
|
@ -4,7 +4,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
extern void (*freecon)(char * con);
|
||||
extern int (*setcon)(const char * con);
|
||||
extern int (*getfilecon)(const char *path, char ** con);
|
||||
|
@ -18,8 +18,6 @@ int file_to_array(const char* filename, Array<char *> &arr);
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
#define UID_SHELL (get_shell_uid())
|
||||
#define UID_ROOT 0
|
||||
#define UID_SYSTEM (get_system_uid())
|
||||
@ -86,8 +84,6 @@ int xpoll(struct pollfd *fds, nfds_t nfds, int timeout);
|
||||
unsigned get_shell_uid();
|
||||
unsigned get_system_uid();
|
||||
unsigned get_radio_uid();
|
||||
int file_to_vector(const char* filename, struct vector *v);
|
||||
int vector_to_file(const char* filename, struct vector *v);
|
||||
ssize_t fdgets(char *buf, size_t size, int fd);
|
||||
int is_num(const char *s);
|
||||
int exec_array(int err, int *fd, void (*cb)(void), const char *argv[]);
|
||||
|
@ -1,41 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "logging.h"
|
||||
|
||||
/* All the string should be freed manually!! */
|
||||
int file_to_vector(const char* filename, struct vector *v) {
|
||||
if (access(filename, R_OK) != 0)
|
||||
return 1;
|
||||
char *line = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t read;
|
||||
|
||||
FILE *fp = xfopen(filename, "r");
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
|
||||
while ((read = getline(&line, &len, fp)) != -1) {
|
||||
// Remove end newline
|
||||
if (line[read - 1] == '\n')
|
||||
line[read - 1] = '\0';
|
||||
vec_push_back(v, line);
|
||||
line = NULL;
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vector_to_file(const char *filename, struct vector *v) {
|
||||
FILE *fp = xfopen(filename, "w");
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
char *line;
|
||||
vec_for_each(v, line) {
|
||||
fprintf(fp, "%s\n", line);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user