mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-25 11:17:38 +00:00
Checkout from seSuperuser/Superuser, leaving only native parts
- Checkout from https://github.com/seSuperuser/Superuser (commit: 69f84dd7a035b4a9f18dea69d9e0452bf0f73103) - Move Superuser/Superuser/jni/su/* to root - Move Superuser/jni/sqlite3/* to sqlite3
This commit is contained in:
commit
3dfcc6b0be
208
activity.c
Normal file
208
activity.c
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
** Copyright 2010, Adam Shanks (@ChainsDD)
|
||||
** Copyright 2008, Zinx Verituse (@zinxv)
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "su.h"
|
||||
|
||||
/* intent actions */
|
||||
#define ACTION_REQUEST "start", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".RequestActivity"
|
||||
#define ACTION_NOTIFY "start", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".NotifyActivity"
|
||||
#define ACTION_RESULT "broadcast", "-n", REQUESTOR "/" REQUESTOR_PREFIX ".SuReceiver"
|
||||
|
||||
#define AM_PATH "/system/bin/app_process", "/system/bin", "com.android.commands.am.Am"
|
||||
|
||||
// TODO: leverage this with exec_log?
|
||||
int silent_run(char* const args[]) {
|
||||
set_identity(0);
|
||||
pid_t pid;
|
||||
pid = fork();
|
||||
/* Parent */
|
||||
if (pid < 0) {
|
||||
PLOGE("fork");
|
||||
return -1;
|
||||
}
|
||||
else if (pid > 0) {
|
||||
return 0;
|
||||
}
|
||||
int zero = open("/dev/zero", O_RDONLY | O_CLOEXEC);
|
||||
dup2(zero, 0);
|
||||
int null = open("/dev/null", O_WRONLY | O_CLOEXEC);
|
||||
dup2(null, 1);
|
||||
dup2(null, 2);
|
||||
setenv("CLASSPATH", "/system/framework/am.jar", 1);
|
||||
execv(args[0], args);
|
||||
PLOGE("exec am");
|
||||
_exit(EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_owner_login_user_args(struct su_context *ctx, char* user, int user_len) {
|
||||
int needs_owner_login_prompt = 0;
|
||||
|
||||
if (ctx->user.multiuser_mode == MULTIUSER_MODE_OWNER_MANAGED) {
|
||||
if (0 != ctx->user.android_user_id) {
|
||||
needs_owner_login_prompt = 1;
|
||||
}
|
||||
snprintf(user, user_len, "0");
|
||||
}
|
||||
else if (ctx->user.multiuser_mode == MULTIUSER_MODE_USER) {
|
||||
snprintf(user, user_len, "%d", ctx->user.android_user_id);
|
||||
}
|
||||
else if (ctx->user.multiuser_mode == MULTIUSER_MODE_NONE) {
|
||||
user[0] = '\0';
|
||||
}
|
||||
else {
|
||||
snprintf(user, user_len, "0");
|
||||
}
|
||||
|
||||
return needs_owner_login_prompt;
|
||||
}
|
||||
|
||||
int send_result(struct su_context *ctx, policy_t policy) {
|
||||
char binary_version[256];
|
||||
sprintf(binary_version, "%d", VERSION_CODE);
|
||||
|
||||
char uid[256];
|
||||
sprintf(uid, "%d", ctx->from.uid);
|
||||
|
||||
char desired_uid[256];
|
||||
sprintf(desired_uid, "%d", ctx->to.uid);
|
||||
|
||||
char user[64];
|
||||
get_owner_login_user_args(ctx, user, sizeof(user));
|
||||
|
||||
if (0 != ctx->user.android_user_id) {
|
||||
char android_user_id[256];
|
||||
sprintf(android_user_id, "%d", ctx->user.android_user_id);
|
||||
|
||||
char *user_result_command[] = {
|
||||
AM_PATH,
|
||||
ACTION_RESULT,
|
||||
"--ei",
|
||||
"binary_version",
|
||||
binary_version,
|
||||
"--es",
|
||||
"from_name",
|
||||
ctx->from.name,
|
||||
"--es",
|
||||
"desired_name",
|
||||
ctx->to.name,
|
||||
"--ei",
|
||||
"uid",
|
||||
uid,
|
||||
"--ei",
|
||||
"desired_uid",
|
||||
desired_uid,
|
||||
"--es",
|
||||
"command",
|
||||
get_command(&ctx->to),
|
||||
"--es",
|
||||
"action",
|
||||
policy == ALLOW ? "allow" : "deny",
|
||||
user[0] ? "--user" : NULL,
|
||||
android_user_id,
|
||||
NULL
|
||||
};
|
||||
silent_run(user_result_command);
|
||||
}
|
||||
|
||||
char *result_command[] = {
|
||||
AM_PATH,
|
||||
ACTION_RESULT,
|
||||
"--ei",
|
||||
"binary_version",
|
||||
binary_version,
|
||||
"--es",
|
||||
"from_name",
|
||||
ctx->from.name,
|
||||
"--es",
|
||||
"desired_name",
|
||||
ctx->to.name,
|
||||
"--ei",
|
||||
"uid",
|
||||
uid,
|
||||
"--ei",
|
||||
"desired_uid",
|
||||
desired_uid,
|
||||
"--es",
|
||||
"command",
|
||||
get_command(&ctx->to),
|
||||
"--es",
|
||||
"action",
|
||||
policy == ALLOW ? "allow" : "deny",
|
||||
user[0] ? "--user" : NULL,
|
||||
user,
|
||||
NULL
|
||||
};
|
||||
return silent_run(result_command);
|
||||
}
|
||||
|
||||
int send_request(struct su_context *ctx) {
|
||||
// if su is operating in MULTIUSER_MODEL_OWNER,
|
||||
// and the user requestor is not the owner,
|
||||
// the owner needs to be notified of the request.
|
||||
// so there will be two activities shown.
|
||||
char user[64];
|
||||
int needs_owner_login_prompt = get_owner_login_user_args(ctx, user, sizeof(user));
|
||||
|
||||
int ret;
|
||||
if (needs_owner_login_prompt) {
|
||||
char uid[256];
|
||||
sprintf(uid, "%d", ctx->from.uid);
|
||||
|
||||
char android_user_id[256];
|
||||
sprintf(android_user_id, "%d", ctx->user.android_user_id);
|
||||
|
||||
// in multiuser mode, the owner gets the su prompt
|
||||
char *notify_command[] = {
|
||||
AM_PATH,
|
||||
ACTION_NOTIFY,
|
||||
"--ei",
|
||||
"caller_uid",
|
||||
uid,
|
||||
"--user",
|
||||
android_user_id,
|
||||
NULL
|
||||
};
|
||||
|
||||
int ret = silent_run(notify_command);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
char *request_command[] = {
|
||||
AM_PATH,
|
||||
ACTION_REQUEST,
|
||||
"--es",
|
||||
"socket",
|
||||
ctx->sock_path,
|
||||
user[0] ? "--user" : NULL,
|
||||
user,
|
||||
NULL
|
||||
};
|
||||
|
||||
return silent_run(request_command);
|
||||
}
|
246
binds.c
Normal file
246
binds.c
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
Copyright 2016, Pierre-Hugues Husson <phh@phh.me>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <strings.h>
|
||||
#include <stdint.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <selinux/selinux.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "binds.h"
|
||||
|
||||
int bind_foreach(bind_cb cb, void* arg) {
|
||||
int res = 0;
|
||||
char *str = NULL;
|
||||
int fd = open("/data/su/binds", O_RDONLY);
|
||||
if(fd<0)
|
||||
return 1;
|
||||
|
||||
off_t size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
str = malloc(size);
|
||||
if(read(fd, str, size) != size)
|
||||
goto error;
|
||||
|
||||
char *base = str;
|
||||
while(base < str+size) {
|
||||
char *parse_src, *parse_dst;
|
||||
int uid;
|
||||
|
||||
char *ptr = memchr(base, 0, size-(base-str));
|
||||
if(ptr == NULL)
|
||||
goto error;
|
||||
sscanf(base, "%d", &uid);
|
||||
|
||||
parse_src = strchr(base, ':');
|
||||
if(!parse_src)
|
||||
goto error;
|
||||
parse_src++;
|
||||
|
||||
parse_dst = strchr(parse_src, ':');
|
||||
if(!parse_dst)
|
||||
goto error;
|
||||
*parse_dst = 0; // Split parse_src string
|
||||
parse_dst++;
|
||||
|
||||
cb(arg, uid, parse_src, parse_dst);
|
||||
|
||||
base = ptr+1;
|
||||
}
|
||||
|
||||
res = 1;
|
||||
error:
|
||||
if(str) free(str);
|
||||
close(fd);
|
||||
return res;
|
||||
}
|
||||
|
||||
int bind_uniq_dst(const char *dst) {
|
||||
static int _res;
|
||||
static const char *_dst;
|
||||
|
||||
_res = 1;
|
||||
_dst = dst;
|
||||
auto void cb(void *arg, int uid, const char *src, const char *dst) {
|
||||
if(strcmp(dst, _dst) == 0)
|
||||
_res = 0;
|
||||
}
|
||||
if(!bind_foreach(cb, NULL))
|
||||
return 0;
|
||||
return _res;
|
||||
}
|
||||
|
||||
void bind_ls(int uid) {
|
||||
static int _uid;
|
||||
_uid=uid;
|
||||
auto void cb(void *arg, int uid, const char *src, const char *dst) {
|
||||
if(_uid == 0 || _uid == 2000 || _uid == uid) {
|
||||
fprintf(stderr, "%d %s => %s\n", uid, src, dst);
|
||||
}
|
||||
}
|
||||
bind_foreach(cb, NULL);
|
||||
}
|
||||
|
||||
int bind_remove(const char *path, int uid) {
|
||||
static int _found = 0;
|
||||
static const char *_path;
|
||||
static int _fd;
|
||||
static int _uid;
|
||||
|
||||
|
||||
_path = path;
|
||||
_found = 0;
|
||||
_uid = uid;
|
||||
|
||||
unlink("/data/su/bind.new");
|
||||
_fd = open("/data/su/bind.new", O_WRONLY|O_CREAT, 0600);
|
||||
if(_fd<0)
|
||||
return 0;
|
||||
|
||||
auto void cb(void *arg, int uid, const char *src, const char *dst) {
|
||||
//The one we want to drop
|
||||
if(strcmp(dst, _path) == 0 &&
|
||||
(_uid == 0 || _uid == 2000 || _uid == uid)) {
|
||||
_found = 1;
|
||||
return;
|
||||
}
|
||||
char *str = NULL;
|
||||
int len = asprintf(&str, "%d:%s:%s", uid, src, dst);
|
||||
write(_fd, str, len+1); //len doesn't include final \0 and we want to write it
|
||||
free(str);
|
||||
}
|
||||
bind_foreach(cb, NULL);
|
||||
close(_fd);
|
||||
unlink("/data/su/bind");
|
||||
rename("/data/su/bind.new", "/data/su/bind");
|
||||
return _found;
|
||||
}
|
||||
|
||||
int init_foreach(init_cb icb, void* arg) {
|
||||
int res = 0;
|
||||
char *str = NULL;
|
||||
int fd = open("/data/su/init", O_RDONLY);
|
||||
if(fd<0)
|
||||
return 1;
|
||||
|
||||
off_t size = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
str = malloc(size);
|
||||
if(read(fd, str, size) != size)
|
||||
goto error;
|
||||
|
||||
char *base = str;
|
||||
while(base < str+size) {
|
||||
char *parsed;
|
||||
int uid;
|
||||
|
||||
char *ptr = memchr(base, 0, size-(base-str));
|
||||
if(ptr == NULL)
|
||||
goto error;
|
||||
sscanf(base, "%d", &uid);
|
||||
|
||||
parsed = strchr(base, ':');
|
||||
if(!parsed)
|
||||
goto error;
|
||||
parsed++;
|
||||
|
||||
|
||||
icb(arg, uid, parsed);
|
||||
|
||||
base = ptr+1;
|
||||
}
|
||||
|
||||
res = 1;
|
||||
error:
|
||||
if(str) free(str);
|
||||
close(fd);
|
||||
return res;
|
||||
}
|
||||
|
||||
int init_uniq(const char *path) {
|
||||
static int _res;
|
||||
static const char *_path;
|
||||
|
||||
_res = 1;
|
||||
_path = path;
|
||||
auto void cb(void *arg, int uid, const char *path) {
|
||||
if(strcmp(path, _path) == 0)
|
||||
_res = 0;
|
||||
}
|
||||
if(!init_foreach(cb, NULL))
|
||||
return 0;
|
||||
return _res;
|
||||
}
|
||||
|
||||
int init_remove(const char *path, int uid) {
|
||||
static int _found = 0;
|
||||
static const char *_path;
|
||||
static int fd;
|
||||
static int _uid;
|
||||
|
||||
_path = path;
|
||||
_found = 0;
|
||||
_uid = uid;
|
||||
|
||||
unlink("/data/su/init.new");
|
||||
fd = open("/data/su/init.new", O_WRONLY|O_CREAT, 0600);
|
||||
if(fd<0)
|
||||
return 0;
|
||||
|
||||
auto void cb(void *arg, int uid, const char *path) {
|
||||
//The one we want to drop
|
||||
if(strcmp(path, _path) == 0 &&
|
||||
(_uid == 0 || _uid == 2000 || uid == _uid)) {
|
||||
_found = 1;
|
||||
return;
|
||||
}
|
||||
char *str = NULL;
|
||||
int len = asprintf(&str, "%d:%s", uid, path);
|
||||
write(fd, str, len+1); //len doesn't include final \0 and we want to write it
|
||||
free(str);
|
||||
}
|
||||
init_foreach(cb, NULL);
|
||||
close(fd);
|
||||
unlink("/data/su/init");
|
||||
rename("/data/su/init.new", "/data/su/init");
|
||||
return _found;
|
||||
}
|
||||
|
||||
void init_ls(int uid) {
|
||||
static int _uid;
|
||||
_uid = uid;
|
||||
auto void cb(void *arg, int uid, const char *path) {
|
||||
if(_uid == 2000 || _uid == 0 || _uid == uid)
|
||||
fprintf(stderr, "%d %s\n", uid, path);
|
||||
}
|
||||
init_foreach(cb, NULL);
|
||||
}
|
33
binds.h
Normal file
33
binds.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2016, Pierre-Hugues Husson <phh@phh.me>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef BINDSH
|
||||
#define BINDS_H
|
||||
|
||||
typedef void (*bind_cb)(void *arg, int uid, const char *src, const char *dst);
|
||||
extern int bind_foreach(bind_cb cb, void* arg);
|
||||
extern int bind_uniq_dst(const char *dst);
|
||||
extern int bind_remove(const char *path, int uid);
|
||||
extern void bind_ls(int uid);
|
||||
|
||||
typedef void (*init_cb)(void *arg, int uid, const char *path);
|
||||
extern int init_foreach(init_cb cb, void* arg);
|
||||
extern int init_uniq(const char *dst);
|
||||
extern int init_remove(const char *path, int uid);
|
||||
extern void init_ls(int uid);
|
||||
|
||||
|
||||
#endif /* BINDS_H */
|
826
daemon.c
Normal file
826
daemon.c
Normal file
@ -0,0 +1,826 @@
|
||||
/*
|
||||
** Copyright 2010, Adam Shanks (@ChainsDD)
|
||||
** Copyright 2008, Zinx Verituse (@zinxv)
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* for unshare() */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <stdint.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <termios.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
#ifdef SUPERUSER_EMBEDDED
|
||||
#include <cutils/multiuser.h>
|
||||
#endif
|
||||
|
||||
#include "binds.h"
|
||||
#include "su.h"
|
||||
#include "utils.h"
|
||||
#include "pts.h"
|
||||
|
||||
int is_daemon = 0;
|
||||
int daemon_from_uid = 0;
|
||||
int daemon_from_pid = 0;
|
||||
|
||||
// Constants for the atty bitfield
|
||||
#define ATTY_IN 1
|
||||
#define ATTY_OUT 2
|
||||
#define ATTY_ERR 4
|
||||
|
||||
/*
|
||||
* Receive a file descriptor from a Unix socket.
|
||||
* Contributed by @mkasick
|
||||
*
|
||||
* Returns the file descriptor on success, or -1 if a file
|
||||
* descriptor was not actually included in the message
|
||||
*
|
||||
* On error the function terminates by calling exit(-1)
|
||||
*/
|
||||
static int recv_fd(int sockfd) {
|
||||
// Need to receive data from the message, otherwise don't care about it.
|
||||
char iovbuf;
|
||||
|
||||
struct iovec iov = {
|
||||
.iov_base = &iovbuf,
|
||||
.iov_len = 1,
|
||||
};
|
||||
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cmsgbuf,
|
||||
.msg_controllen = sizeof(cmsgbuf),
|
||||
};
|
||||
|
||||
if (recvmsg(sockfd, &msg, MSG_WAITALL) != 1) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Was a control message actually sent?
|
||||
switch (msg.msg_controllen) {
|
||||
case 0:
|
||||
// No, so the file descriptor was closed and won't be used.
|
||||
return -1;
|
||||
case sizeof(cmsgbuf):
|
||||
// Yes, grab the file descriptor from it.
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
if (cmsg == NULL ||
|
||||
cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
|
||||
cmsg->cmsg_level != SOL_SOCKET ||
|
||||
cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
error:
|
||||
LOGE("unable to read fd");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return *(int *)CMSG_DATA(cmsg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a file descriptor through a Unix socket.
|
||||
* Contributed by @mkasick
|
||||
*
|
||||
* On error the function terminates by calling exit(-1)
|
||||
*
|
||||
* fd may be -1, in which case the dummy data is sent,
|
||||
* but no control message with the FD is sent.
|
||||
*/
|
||||
static void send_fd(int sockfd, int fd) {
|
||||
// Need to send some data in the message, this will do.
|
||||
struct iovec iov = {
|
||||
.iov_base = "",
|
||||
.iov_len = 1,
|
||||
};
|
||||
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
};
|
||||
|
||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
if (fd != -1) {
|
||||
// Is the file descriptor actually open?
|
||||
if (fcntl(fd, F_GETFD) == -1) {
|
||||
if (errno != EBADF) {
|
||||
goto error;
|
||||
}
|
||||
// It's closed, don't send a control message or sendmsg will EBADF.
|
||||
} else {
|
||||
// It's open, send the file descriptor in a control message.
|
||||
msg.msg_control = cmsgbuf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf);
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
|
||||
*(int *)CMSG_DATA(cmsg) = fd;
|
||||
}
|
||||
}
|
||||
|
||||
if (sendmsg(sockfd, &msg, 0) != 1) {
|
||||
error:
|
||||
PLOGE("unable to send fd");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static int read_int(int fd) {
|
||||
int val;
|
||||
int len = read(fd, &val, sizeof(int));
|
||||
if (len != sizeof(int)) {
|
||||
LOGE("unable to read int: %d", len);
|
||||
exit(-1);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void write_int(int fd, int val) {
|
||||
int written = write(fd, &val, sizeof(int));
|
||||
if (written != sizeof(int)) {
|
||||
PLOGE("unable to write int");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static char* read_string(int fd) {
|
||||
int len = read_int(fd);
|
||||
if (len > PATH_MAX || len < 0) {
|
||||
LOGE("invalid string length %d", len);
|
||||
exit(-1);
|
||||
}
|
||||
char* val = malloc(sizeof(char) * (len + 1));
|
||||
if (val == NULL) {
|
||||
LOGE("unable to malloc string");
|
||||
exit(-1);
|
||||
}
|
||||
val[len] = '\0';
|
||||
int amount = read(fd, val, len);
|
||||
if (amount != len) {
|
||||
LOGE("unable to read string");
|
||||
exit(-1);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void write_string(int fd, char* val) {
|
||||
int len = strlen(val);
|
||||
write_int(fd, len);
|
||||
int written = write(fd, val, len);
|
||||
if (written != len) {
|
||||
PLOGE("unable to write string");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SUPERUSER_EMBEDDED
|
||||
static void mount_emulated_storage(int user_id) {
|
||||
const char *emulated_source = getenv("EMULATED_STORAGE_SOURCE");
|
||||
const char *emulated_target = getenv("EMULATED_STORAGE_TARGET");
|
||||
const char* legacy = getenv("EXTERNAL_STORAGE");
|
||||
|
||||
if (!emulated_source || !emulated_target) {
|
||||
// No emulated storage is present
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a second private mount namespace for our process
|
||||
if (unshare(CLONE_NEWNS) < 0) {
|
||||
PLOGE("unshare");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mount("rootfs", "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
|
||||
PLOGE("mount rootfs as slave");
|
||||
return;
|
||||
}
|
||||
|
||||
// /mnt/shell/emulated -> /storage/emulated
|
||||
if (mount(emulated_source, emulated_target, NULL, MS_BIND, NULL) < 0) {
|
||||
PLOGE("mount emulated storage");
|
||||
}
|
||||
|
||||
char target_user[PATH_MAX];
|
||||
snprintf(target_user, PATH_MAX, "%s/%d", emulated_target, user_id);
|
||||
|
||||
// /mnt/shell/emulated/<user> -> /storage/emulated/legacy
|
||||
if (mount(target_user, legacy, NULL, MS_BIND | MS_REC, NULL) < 0) {
|
||||
PLOGE("mount legacy path");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int run_daemon_child(int infd, int outfd, int errfd, int argc, char** argv) {
|
||||
if (-1 == dup2(outfd, STDOUT_FILENO)) {
|
||||
PLOGE("dup2 child outfd");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (-1 == dup2(errfd, STDERR_FILENO)) {
|
||||
PLOGE("dup2 child errfd");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (-1 == dup2(infd, STDIN_FILENO)) {
|
||||
PLOGE("dup2 child infd");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
close(infd);
|
||||
close(outfd);
|
||||
close(errfd);
|
||||
|
||||
return su_main_nodaemon(argc, argv);
|
||||
}
|
||||
|
||||
static int daemon_accept(int fd) {
|
||||
is_daemon = 1;
|
||||
int pid = read_int(fd);
|
||||
LOGD("remote pid: %d", pid);
|
||||
char *pts_slave = read_string(fd);
|
||||
LOGD("remote pts_slave: %s", pts_slave);
|
||||
daemon_from_uid = read_int(fd);
|
||||
LOGD("remote uid: %d", daemon_from_uid);
|
||||
daemon_from_pid = read_int(fd);
|
||||
LOGD("remote req pid: %d", daemon_from_pid);
|
||||
|
||||
struct ucred credentials;
|
||||
int ucred_length = sizeof(struct ucred);
|
||||
/* fill in the user data structure */
|
||||
if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length)) {
|
||||
LOGE("could obtain credentials from unix domain socket");
|
||||
exit(-1);
|
||||
}
|
||||
// if the credentials on the other side of the wire are NOT root,
|
||||
// we can't trust anything being sent.
|
||||
if (credentials.uid != 0) {
|
||||
daemon_from_uid = credentials.uid;
|
||||
pid = credentials.pid;
|
||||
daemon_from_pid = credentials.pid;
|
||||
}
|
||||
|
||||
int mount_storage = read_int(fd);
|
||||
// The the FDs for each of the streams
|
||||
int infd = recv_fd(fd);
|
||||
int outfd = recv_fd(fd);
|
||||
int errfd = recv_fd(fd);
|
||||
|
||||
int argc = read_int(fd);
|
||||
if (argc < 0 || argc > 512) {
|
||||
LOGE("unable to allocate args: %d", argc);
|
||||
exit(-1);
|
||||
}
|
||||
LOGD("remote args: %d", argc);
|
||||
char** argv = (char**)malloc(sizeof(char*) * (argc + 1));
|
||||
argv[argc] = NULL;
|
||||
int i;
|
||||
for (i = 0; i < argc; i++) {
|
||||
argv[i] = read_string(fd);
|
||||
}
|
||||
|
||||
// ack
|
||||
write_int(fd, 1);
|
||||
|
||||
// Fork the child process. The fork has to happen before calling
|
||||
// setsid() and opening the pseudo-terminal so that the parent
|
||||
// is not affected
|
||||
int child = fork();
|
||||
if (child < 0) {
|
||||
// fork failed, send a return code and bail out
|
||||
PLOGE("unable to fork");
|
||||
write(fd, &child, sizeof(int));
|
||||
close(fd);
|
||||
return child;
|
||||
}
|
||||
|
||||
if (child != 0) {
|
||||
// In parent, wait for the child to exit, and send the exit code
|
||||
// across the wire.
|
||||
int status, code;
|
||||
|
||||
free(pts_slave);
|
||||
|
||||
LOGD("waiting for child exit");
|
||||
if (waitpid(child, &status, 0) > 0) {
|
||||
code = WEXITSTATUS(status);
|
||||
}
|
||||
else {
|
||||
code = -1;
|
||||
}
|
||||
|
||||
// Pass the return code back to the client
|
||||
LOGD("sending code");
|
||||
if (write(fd, &code, sizeof(int)) != sizeof(int)) {
|
||||
PLOGE("unable to write exit code");
|
||||
}
|
||||
|
||||
close(fd);
|
||||
LOGD("child exited");
|
||||
return code;
|
||||
}
|
||||
|
||||
// We are in the child now
|
||||
// Close the unix socket file descriptor
|
||||
close (fd);
|
||||
|
||||
// Become session leader
|
||||
if (setsid() == (pid_t) -1) {
|
||||
PLOGE("setsid");
|
||||
}
|
||||
|
||||
int ptsfd;
|
||||
if (pts_slave[0]) {
|
||||
//Check pts_slave file is owned by daemon_from_uid
|
||||
{
|
||||
struct stat stbuf;
|
||||
int res = stat(pts_slave, &stbuf);
|
||||
if(res) {
|
||||
PLOGE("stat(pts_slave) daemon");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
//If caller is not root, ensure the owner of pts_slave is the caller
|
||||
if(stbuf.st_uid != credentials.uid &&
|
||||
credentials.uid != 0) {
|
||||
PLOGE("Wrong permission of pts_slave");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Opening the TTY has to occur after the
|
||||
// fork() and setsid() so that it becomes
|
||||
// our controlling TTY and not the daemon's
|
||||
ptsfd = open(pts_slave, O_RDWR);
|
||||
if (ptsfd == -1) {
|
||||
PLOGE("open(pts_slave) daemon");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
//Check we haven't been fooled
|
||||
{
|
||||
struct stat stbuf;
|
||||
int res = fstat(ptsfd, &stbuf);
|
||||
if(res) {
|
||||
//If we have been fooled DO NOT WRITE ANYTHING
|
||||
_exit(2);
|
||||
}
|
||||
|
||||
if(stbuf.st_uid != credentials.uid &&
|
||||
credentials.uid != 0) {
|
||||
_exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (infd < 0) {
|
||||
LOGD("daemon: stdin using PTY");
|
||||
infd = ptsfd;
|
||||
}
|
||||
if (outfd < 0) {
|
||||
LOGD("daemon: stdout using PTY");
|
||||
outfd = ptsfd;
|
||||
}
|
||||
if (errfd < 0) {
|
||||
LOGD("daemon: stderr using PTY");
|
||||
errfd = ptsfd;
|
||||
}
|
||||
} else {
|
||||
// If a TTY was sent directly, make it the CTTY.
|
||||
if (isatty(infd)) {
|
||||
ioctl(infd, TIOCSCTTY, 1);
|
||||
}
|
||||
}
|
||||
free(pts_slave);
|
||||
|
||||
#ifdef SUPERUSER_EMBEDDED
|
||||
if (mount_storage) {
|
||||
mount_emulated_storage(multiuser_get_user_id(daemon_from_uid));
|
||||
}
|
||||
#endif
|
||||
|
||||
return run_daemon_child(infd, outfd, errfd, argc, argv);
|
||||
}
|
||||
|
||||
static int copy_file(const char* src, const char* dst, int mode) {
|
||||
int ifd = open(src, O_RDONLY);
|
||||
if(ifd<0)
|
||||
return 1;
|
||||
if(mode == 0) {
|
||||
struct stat stbuf;
|
||||
if(fstat(ifd, &stbuf))
|
||||
return 1;
|
||||
mode = stbuf.st_mode & 0777;
|
||||
LOGE("File %s found mode %o", src, mode);
|
||||
|
||||
}
|
||||
int ofd = open(dst, O_WRONLY|O_CREAT, mode);
|
||||
if(ofd<0)
|
||||
return 1;
|
||||
size_t s = lseek(ifd, 0, SEEK_END);
|
||||
if(s<0)
|
||||
return 1;
|
||||
lseek(ifd, 0, SEEK_SET);
|
||||
int ret = sendfile(ofd, ifd, NULL, s);
|
||||
if(ret<0)
|
||||
return 1;
|
||||
close(ofd);
|
||||
close(ifd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prepare_su_bind() {
|
||||
int ret = 0;
|
||||
|
||||
//Check if there is a use to mount bind
|
||||
if(access("/system/xbin/su", R_OK) != 0)
|
||||
return;
|
||||
|
||||
ret = copy_file("/sbin/su", "/dev/su/su", 0755);
|
||||
if(ret) {
|
||||
PLOGE("Failed to copy su");
|
||||
return;
|
||||
}
|
||||
chmod("/dev/su/su", 0755);
|
||||
|
||||
ret = setfilecon("/dev/su/su", "u:object_r:system_file:s0");
|
||||
if(ret) {
|
||||
LOGE("Failed to set file context");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = mount("/dev/su/su", "/system/xbin/su", "", MS_BIND, NULL);
|
||||
if(ret) {
|
||||
LOGE("Failed to mount bind");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void prepare_binds() {
|
||||
mkdir("/data/su", 0700);
|
||||
static int i = 0;
|
||||
|
||||
auto void cb(void *arg, int uid, const char *src, const char *dst) {
|
||||
int ret = 0;
|
||||
|
||||
char *tmpfile = NULL;
|
||||
asprintf(&tmpfile, "/dev/su/bind%d", i++);
|
||||
struct stat stbuf;
|
||||
ret = stat(src, &stbuf);
|
||||
if(ret) {
|
||||
free(tmpfile);
|
||||
LOGE("Failed to stat src %s file", src);
|
||||
return;
|
||||
}
|
||||
|
||||
//Only shell uid is allowed to bind files not his own
|
||||
if(uid != 2000 && uid != stbuf.st_uid) {
|
||||
LOGE("File %s has wrong owner: %d vs %d", src, uid, stbuf.st_uid);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = copy_file(src, tmpfile, 0);
|
||||
if(ret) {
|
||||
free(tmpfile);
|
||||
PLOGE("Failed to copy su");
|
||||
return;
|
||||
}
|
||||
chmod(tmpfile, stbuf.st_mode);
|
||||
|
||||
ret = setfilecon(tmpfile, "u:object_r:system_file:s0");
|
||||
if(ret) {
|
||||
LOGE("Failed to set file context");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = mount(tmpfile, dst, "", MS_BIND, NULL);
|
||||
if(ret) {
|
||||
LOGE("Failed to mount bind");
|
||||
return;
|
||||
}
|
||||
}
|
||||
bind_foreach(cb, NULL);
|
||||
}
|
||||
|
||||
static void do_init() {
|
||||
auto void cb(void *arg, int uid, const char *path) {
|
||||
int ret = 0;
|
||||
|
||||
int p = fork();
|
||||
if(p)
|
||||
return;
|
||||
|
||||
while(access("/system/bin/sh", R_OK)) sleep(1);
|
||||
ret = setexeccon("u:r:su:s0");
|
||||
execl(path, path, NULL);
|
||||
LOGE("Failed to execute %s. Trying as shell script, ret = %d", path, ret);
|
||||
|
||||
ret = setexeccon("u:r:su:s0");
|
||||
execl("/system/bin/sh", "/system/bin/sh", path, NULL);
|
||||
LOGE("Failed to execute %s as shell script", path);
|
||||
_exit(1);
|
||||
}
|
||||
init_foreach(cb, NULL);
|
||||
}
|
||||
|
||||
static void prepare() {
|
||||
setfscreatecon("u:object_r:su_daemon:s0");
|
||||
mkdir("/dev/su", 0700);
|
||||
prepare_su_bind();
|
||||
prepare_binds();
|
||||
do_init();
|
||||
setfscreatecon(NULL);
|
||||
}
|
||||
|
||||
int run_daemon() {
|
||||
if (getuid() != 0 || getgid() != 0) {
|
||||
PLOGE("daemon requires root. uid/gid not root");
|
||||
return -1;
|
||||
}
|
||||
|
||||
prepare();
|
||||
|
||||
int fd;
|
||||
struct sockaddr_un sun;
|
||||
|
||||
fd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
PLOGE("socket");
|
||||
return -1;
|
||||
}
|
||||
if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
|
||||
PLOGE("fcntl FD_CLOEXEC");
|
||||
goto err;
|
||||
}
|
||||
|
||||
memset(&sun, 0, sizeof(sun));
|
||||
sun.sun_family = AF_LOCAL;
|
||||
sprintf(sun.sun_path, "%s/server", REQUESTOR_DAEMON_PATH);
|
||||
|
||||
/*
|
||||
* Delete the socket to protect from situations when
|
||||
* something bad occured previously and the kernel reused pid from that process.
|
||||
* Small probability, isn't it.
|
||||
*/
|
||||
unlink(sun.sun_path);
|
||||
unlink(REQUESTOR_DAEMON_PATH);
|
||||
|
||||
int previous_umask = umask(027);
|
||||
mkdir(REQUESTOR_DAEMON_PATH, 0777);
|
||||
|
||||
memset(sun.sun_path, 0, sizeof(sun.sun_path));
|
||||
memcpy(sun.sun_path, "\0" "SUPERUSER", strlen("SUPERUSER") + 1);
|
||||
|
||||
if (bind(fd, (struct sockaddr*)&sun, sizeof(sun)) < 0) {
|
||||
PLOGE("daemon bind");
|
||||
goto err;
|
||||
}
|
||||
|
||||
chmod(REQUESTOR_DAEMON_PATH, 0755);
|
||||
chmod(sun.sun_path, 0777);
|
||||
|
||||
umask(previous_umask);
|
||||
|
||||
if (listen(fd, 10) < 0) {
|
||||
PLOGE("daemon listen");
|
||||
goto err;
|
||||
}
|
||||
|
||||
int client;
|
||||
while ((client = accept(fd, NULL, NULL)) > 0) {
|
||||
if (fork_zero_fucks() == 0) {
|
||||
close(fd);
|
||||
return daemon_accept(client);
|
||||
}
|
||||
else {
|
||||
close(client);
|
||||
}
|
||||
}
|
||||
|
||||
LOGE("daemon exiting");
|
||||
err:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// List of signals which cause process termination
|
||||
static int quit_signals[] = { SIGALRM, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
|
||||
|
||||
static void sighandler(int sig) {
|
||||
(void)sig;
|
||||
restore_stdin();
|
||||
|
||||
// Assume we'll only be called before death
|
||||
// See note before sigaction() in set_stdin_raw()
|
||||
//
|
||||
// Now, close all standard I/O to cause the pumps
|
||||
// to exit so we can continue and retrieve the exit
|
||||
// code
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
// Put back all the default handlers
|
||||
struct sigaction act;
|
||||
int i;
|
||||
|
||||
memset(&act, '\0', sizeof(act));
|
||||
act.sa_handler = SIG_DFL;
|
||||
for (i = 0; quit_signals[i]; i++) {
|
||||
if (sigaction(quit_signals[i], &act, NULL) < 0) {
|
||||
PLOGE("Error removing signal handler");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup signal handlers trap signals which should result in program termination
|
||||
* so that we can restore the terminal to its normal state and retrieve the
|
||||
* return code.
|
||||
*/
|
||||
static void setup_sighandlers(void) {
|
||||
struct sigaction act;
|
||||
int i;
|
||||
|
||||
// Install the termination handlers
|
||||
// Note: we're assuming that none of these signal handlers are already trapped.
|
||||
// If they are, we'll need to modify this code to save the previous handler and
|
||||
// call it after we restore stdin to its previous state.
|
||||
memset(&act, '\0', sizeof(act));
|
||||
act.sa_handler = &sighandler;
|
||||
for (i = 0; quit_signals[i]; i++) {
|
||||
if (sigaction(quit_signals[i], &act, NULL) < 0) {
|
||||
PLOGE("Error installing signal handler");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int connect_daemon(int argc, char *argv[], int ppid) {
|
||||
int uid = getuid();
|
||||
int ptmx = -1;
|
||||
char pts_slave[PATH_MAX];
|
||||
|
||||
struct sockaddr_un sun;
|
||||
|
||||
// Open a socket to the daemon
|
||||
int socketfd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
if (socketfd < 0) {
|
||||
PLOGE("socket");
|
||||
exit(-1);
|
||||
}
|
||||
if (fcntl(socketfd, F_SETFD, FD_CLOEXEC)) {
|
||||
PLOGE("fcntl FD_CLOEXEC");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
memset(&sun, 0, sizeof(sun));
|
||||
sun.sun_family = AF_LOCAL;
|
||||
sprintf(sun.sun_path, "%s/server", REQUESTOR_DAEMON_PATH);
|
||||
|
||||
memset(sun.sun_path, 0, sizeof(sun.sun_path));
|
||||
memcpy(sun.sun_path, "\0" "SUPERUSER", strlen("SUPERUSER") + 1);
|
||||
|
||||
if (0 != connect(socketfd, (struct sockaddr*)&sun, sizeof(sun))) {
|
||||
PLOGE("connect");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
LOGD("connecting client %d", getpid());
|
||||
|
||||
int mount_storage = getenv("MOUNT_EMULATED_STORAGE") != NULL;
|
||||
|
||||
// Determine which one of our streams are attached to a TTY
|
||||
int atty = 0;
|
||||
|
||||
// Send TTYs directly (instead of proxying with a PTY) if
|
||||
// the SUPERUSER_SEND_TTY environment variable is set.
|
||||
if (getenv("SUPERUSER_SEND_TTY") == NULL) {
|
||||
if (isatty(STDIN_FILENO)) atty |= ATTY_IN;
|
||||
if (isatty(STDOUT_FILENO)) atty |= ATTY_OUT;
|
||||
if (isatty(STDERR_FILENO)) atty |= ATTY_ERR;
|
||||
}
|
||||
|
||||
if (atty) {
|
||||
// We need a PTY. Get one.
|
||||
ptmx = pts_open(pts_slave, sizeof(pts_slave));
|
||||
if (ptmx < 0) {
|
||||
PLOGE("pts_open");
|
||||
exit(-1);
|
||||
}
|
||||
} else {
|
||||
pts_slave[0] = '\0';
|
||||
}
|
||||
|
||||
// Send some info to the daemon, starting with our PID
|
||||
write_int(socketfd, getpid());
|
||||
// Send the slave path to the daemon
|
||||
// (This is "" if we're not using PTYs)
|
||||
write_string(socketfd, pts_slave);
|
||||
// User ID
|
||||
write_int(socketfd, uid);
|
||||
// Parent PID
|
||||
write_int(socketfd, ppid);
|
||||
write_int(socketfd, mount_storage);
|
||||
|
||||
// Send stdin
|
||||
if (atty & ATTY_IN) {
|
||||
// Using PTY
|
||||
send_fd(socketfd, -1);
|
||||
} else {
|
||||
send_fd(socketfd, STDIN_FILENO);
|
||||
}
|
||||
|
||||
// Send stdout
|
||||
if (atty & ATTY_OUT) {
|
||||
// Forward SIGWINCH
|
||||
watch_sigwinch_async(STDOUT_FILENO, ptmx);
|
||||
|
||||
// Using PTY
|
||||
send_fd(socketfd, -1);
|
||||
} else {
|
||||
send_fd(socketfd, STDOUT_FILENO);
|
||||
}
|
||||
|
||||
// Send stderr
|
||||
if (atty & ATTY_ERR) {
|
||||
// Using PTY
|
||||
send_fd(socketfd, -1);
|
||||
} else {
|
||||
send_fd(socketfd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
// Number of command line arguments
|
||||
write_int(socketfd, mount_storage ? argc - 1 : argc);
|
||||
|
||||
// Command line arguments
|
||||
int i;
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (i == 1 && mount_storage) {
|
||||
continue;
|
||||
}
|
||||
write_string(socketfd, argv[i]);
|
||||
}
|
||||
|
||||
// Wait for acknowledgement from daemon
|
||||
read_int(socketfd);
|
||||
|
||||
if (atty & ATTY_IN) {
|
||||
setup_sighandlers();
|
||||
pump_stdin_async(ptmx);
|
||||
}
|
||||
if (atty & ATTY_OUT) {
|
||||
pump_stdout_blocking(ptmx);
|
||||
}
|
||||
|
||||
// Get the exit code
|
||||
int code = read_int(socketfd);
|
||||
close(socketfd);
|
||||
LOGD("client exited %d", code);
|
||||
|
||||
return code;
|
||||
}
|
105
db.c
Normal file
105
db.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
** Copyright 2013, Koushik Dutta (@koush)
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <sqlite3.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "su.h"
|
||||
|
||||
struct callback_data_t {
|
||||
struct su_context *ctx;
|
||||
policy_t policy;
|
||||
};
|
||||
|
||||
static int database_callback(void *v, int argc, char **argv, char **azColName){
|
||||
struct callback_data_t *data = (struct callback_data_t *)v;
|
||||
int command_match = 0;
|
||||
policy_t policy = DENY;
|
||||
int i;
|
||||
time_t until = 0;
|
||||
for(i = 0; i < argc; i++) {
|
||||
if (strcmp(azColName[i], "policy") == 0) {
|
||||
if (argv[i] == NULL) {
|
||||
policy = DENY;
|
||||
}
|
||||
if (strcmp(argv[i], "allow") == 0) {
|
||||
policy = ALLOW;
|
||||
}
|
||||
else if (strcmp(argv[i], "interactive") == 0) {
|
||||
policy = INTERACTIVE;
|
||||
}
|
||||
else {
|
||||
policy = DENY;
|
||||
}
|
||||
}
|
||||
else if (strcmp(azColName[i], "command") == 0) {
|
||||
// null or empty command means to match all commands (whitelist all from uid)
|
||||
command_match = argv[i] == NULL || strlen(argv[i]) == 0 || strcmp(argv[i], get_command(&(data->ctx->to))) == 0;
|
||||
}
|
||||
else if (strcmp(azColName[i], "until") == 0) {
|
||||
if (argv[i] != NULL) {
|
||||
until = atoi(argv[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for command match
|
||||
if (command_match) {
|
||||
// also make sure this policy has not expired
|
||||
if (until == 0 || until > time(NULL)) {
|
||||
if (policy == DENY) {
|
||||
data->policy = DENY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
data->policy = ALLOW;
|
||||
// even though we allow, continue, so we can see if there's another policy
|
||||
// that denies...
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
policy_t database_check(struct su_context *ctx) {
|
||||
sqlite3 *db = NULL;
|
||||
|
||||
char query[512];
|
||||
snprintf(query, sizeof(query), "select policy, until, command from uid_policy where uid=%d", ctx->from.uid);
|
||||
int ret = sqlite3_open_v2(ctx->user.database_path, &db, SQLITE_OPEN_READONLY, NULL);
|
||||
if (ret) {
|
||||
LOGE("sqlite3 open failure: %d", ret);
|
||||
sqlite3_close(db);
|
||||
return INTERACTIVE;
|
||||
}
|
||||
|
||||
int result;
|
||||
char *err = NULL;
|
||||
struct callback_data_t data;
|
||||
data.ctx = ctx;
|
||||
data.policy = INTERACTIVE;
|
||||
ret = sqlite3_exec(db, query, database_callback, &data, &err);
|
||||
sqlite3_close(db);
|
||||
if (err != NULL) {
|
||||
LOGE("sqlite3_exec: %s", err);
|
||||
return DENY;
|
||||
}
|
||||
|
||||
return data.policy;
|
||||
}
|
49
hacks.c
Normal file
49
hacks.c
Normal file
@ -0,0 +1,49 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "su.h"
|
||||
#include "utils.h"
|
||||
|
||||
enum {
|
||||
H_NO_CONTEXT = 0x0001,
|
||||
};
|
||||
|
||||
static struct {
|
||||
const char *package;
|
||||
int flags;
|
||||
int uid;
|
||||
} apps_list[] = {
|
||||
{ "com.keramidas.TitaniumBackup", H_NO_CONTEXT, },
|
||||
};
|
||||
|
||||
void hacks_init() {
|
||||
char oldCwd[512];
|
||||
int i;
|
||||
getcwd(oldCwd, sizeof(oldCwd));
|
||||
chdir("/data/data");
|
||||
for(i=0; i<(sizeof(apps_list)/sizeof(apps_list[0])); ++i) {
|
||||
apps_list[i].uid = -1;
|
||||
struct stat st_buf;
|
||||
int ret = stat(apps_list[i].package, &st_buf);
|
||||
LOGW("hacks: Testing (%s:%d:%d)", apps_list[i].package, ret, st_buf.st_uid);
|
||||
if(ret)
|
||||
continue;
|
||||
apps_list[i].uid = st_buf.st_uid;
|
||||
}
|
||||
}
|
||||
|
||||
void hacks_update_context(struct su_context* ctxt) {
|
||||
int i;
|
||||
for(i=0; i<(sizeof(apps_list)/sizeof(apps_list[0])); ++i) {
|
||||
LOGW("hacks: Testing (%s:%d), %d", apps_list[i].package, ctxt->from.uid);
|
||||
if(apps_list[i].uid != ctxt->from.uid)
|
||||
continue;
|
||||
|
||||
LOGW("hacks: Found app (%s:%d)", apps_list[i].package, ctxt->from.uid);
|
||||
if(apps_list[i].flags & H_NO_CONTEXT) {
|
||||
LOGW("hacks: Disabling context (%s:%d)", apps_list[i].package, ctxt->from.uid);
|
||||
ctxt->to.context = NULL;
|
||||
}
|
||||
}
|
||||
}
|
333
pts.c
Normal file
333
pts.c
Normal file
@ -0,0 +1,333 @@
|
||||
/*
|
||||
* Copyright 2013, Tan Chee Eng (@tan-ce)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* pts.c
|
||||
*
|
||||
* Manages the pseudo-terminal driver on Linux/Android and provides some
|
||||
* helper functions to handle raw input mode and terminal window resizing
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <termios.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "pts.h"
|
||||
|
||||
/**
|
||||
* Helper functions
|
||||
*/
|
||||
// Ensures all the data is written out
|
||||
static int write_blocking(int fd, char *buf, ssize_t bufsz) {
|
||||
ssize_t ret, written;
|
||||
|
||||
written = 0;
|
||||
do {
|
||||
ret = write(fd, buf + written, bufsz - written);
|
||||
if (ret == -1) return -1;
|
||||
written += ret;
|
||||
} while (written < bufsz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pump data from input FD to output FD. If close_output is
|
||||
* true, then close the output FD when we're done.
|
||||
*/
|
||||
static void pump_ex(int input, int output, int close_output) {
|
||||
char buf[4096];
|
||||
int len;
|
||||
while ((len = read(input, buf, 4096)) > 0) {
|
||||
if (write_blocking(output, buf, len) == -1) break;
|
||||
}
|
||||
close(input);
|
||||
if (close_output) close(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pump data from input FD to output FD. Will close the
|
||||
* output FD when done.
|
||||
*/
|
||||
static void pump(int input, int output) {
|
||||
pump_ex(input, output, 1);
|
||||
}
|
||||
|
||||
static void* pump_thread(void* data) {
|
||||
int* files = (int*)data;
|
||||
int input = files[0];
|
||||
int output = files[1];
|
||||
pump(input, output);
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void pump_async(int input, int output) {
|
||||
pthread_t writer;
|
||||
int* files = (int*)malloc(sizeof(int) * 2);
|
||||
if (files == NULL) {
|
||||
exit(-1);
|
||||
}
|
||||
files[0] = input;
|
||||
files[1] = output;
|
||||
pthread_create(&writer, NULL, pump_thread, files);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pts_open
|
||||
*
|
||||
* Opens a pts device and returns the name of the slave tty device.
|
||||
*
|
||||
* Arguments
|
||||
* slave_name the name of the slave device
|
||||
* slave_name_size the size of the buffer passed via slave_name
|
||||
*
|
||||
* Return Values
|
||||
* on failure either -2 or -1 (errno set) is returned.
|
||||
* on success, the file descriptor of the master device is returned.
|
||||
*/
|
||||
int pts_open(char *slave_name, size_t slave_name_size) {
|
||||
int fdm;
|
||||
char sn_tmp[256];
|
||||
|
||||
// Open master ptmx device
|
||||
fdm = open("/dev/ptmx", O_RDWR);
|
||||
if (fdm == -1) return -1;
|
||||
|
||||
// Get the slave name
|
||||
if (ptsname_r(fdm, slave_name, slave_name_size-1)) {
|
||||
close(fdm);
|
||||
return -2;
|
||||
}
|
||||
|
||||
slave_name[slave_name_size - 1] = '\0';
|
||||
|
||||
// Grant, then unlock
|
||||
if (grantpt(fdm) == -1) {
|
||||
close(fdm);
|
||||
return -1;
|
||||
}
|
||||
if (unlockpt(fdm) == -1) {
|
||||
close(fdm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fdm;
|
||||
}
|
||||
|
||||
// Stores the previous termios of stdin
|
||||
static struct termios old_stdin;
|
||||
static int stdin_is_raw = 0;
|
||||
|
||||
/**
|
||||
* set_stdin_raw
|
||||
*
|
||||
* Changes stdin to raw unbuffered mode, disables echo,
|
||||
* auto carriage return, etc.
|
||||
*
|
||||
* Return Value
|
||||
* on failure -1, and errno is set
|
||||
* on success 0
|
||||
*/
|
||||
int set_stdin_raw(void) {
|
||||
struct termios new_termios;
|
||||
|
||||
// Save the current stdin termios
|
||||
if (tcgetattr(STDIN_FILENO, &old_stdin) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Start from the current settings
|
||||
new_termios = old_stdin;
|
||||
|
||||
// Make the terminal like an SSH or telnet client
|
||||
new_termios.c_iflag |= IGNPAR;
|
||||
new_termios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
|
||||
new_termios.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
|
||||
new_termios.c_oflag &= ~OPOST;
|
||||
new_termios.c_cc[VMIN] = 1;
|
||||
new_termios.c_cc[VTIME] = 0;
|
||||
|
||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_termios) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
stdin_is_raw = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* restore_stdin
|
||||
*
|
||||
* Restore termios on stdin to the state it was before
|
||||
* set_stdin_raw() was called. If set_stdin_raw() was
|
||||
* never called, does nothing and doesn't return an error.
|
||||
*
|
||||
* This function is async-safe.
|
||||
*
|
||||
* Return Value
|
||||
* on failure, -1 and errno is set
|
||||
* on success, 0
|
||||
*/
|
||||
int restore_stdin(void) {
|
||||
if (!stdin_is_raw) return 0;
|
||||
|
||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_stdin) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
stdin_is_raw = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Flag indicating whether the sigwinch watcher should terminate.
|
||||
static volatile int closing_time = 0;
|
||||
|
||||
/**
|
||||
* Thread process. Wait for a SIGWINCH to be received, then update
|
||||
* the terminal size.
|
||||
*/
|
||||
static void *watch_sigwinch(void *data) {
|
||||
sigset_t winch;
|
||||
int sig;
|
||||
int master = ((int *)data)[0];
|
||||
int slave = ((int *)data)[1];
|
||||
|
||||
sigemptyset(&winch);
|
||||
sigaddset(&winch, SIGWINCH);
|
||||
|
||||
do {
|
||||
// Wait for a SIGWINCH
|
||||
sigwait(&winch, &sig);
|
||||
|
||||
if (closing_time) break;
|
||||
|
||||
// Get the new terminal size
|
||||
struct winsize w;
|
||||
if (ioctl(master, TIOCGWINSZ, &w) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set the new terminal size
|
||||
ioctl(slave, TIOCSWINSZ, &w);
|
||||
|
||||
} while (1);
|
||||
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* watch_sigwinch_async
|
||||
*
|
||||
* After calling this function, if the application receives
|
||||
* SIGWINCH, the terminal window size will be read from
|
||||
* "input" and set on "output".
|
||||
*
|
||||
* NOTE: This function blocks SIGWINCH and spawns a thread.
|
||||
* NOTE 2: This function must be called before any of the
|
||||
* pump functions.
|
||||
*
|
||||
* Arguments
|
||||
* master A file descriptor of the TTY window size to follow
|
||||
* slave A file descriptor of the TTY window size which is
|
||||
* to be set on SIGWINCH
|
||||
*
|
||||
* Return Value
|
||||
* on failure, -1 and errno will be set. In this case, no
|
||||
* thread has been spawned and SIGWINCH will not be
|
||||
* blocked.
|
||||
* on success, 0
|
||||
*/
|
||||
int watch_sigwinch_async(int master, int slave) {
|
||||
pthread_t watcher;
|
||||
int *files = (int *) malloc(sizeof(int) * 2);
|
||||
if (files == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Block SIGWINCH so sigwait can later receive it
|
||||
sigset_t winch;
|
||||
sigemptyset(&winch);
|
||||
sigaddset(&winch, SIGWINCH);
|
||||
if (sigprocmask(SIG_BLOCK, &winch, NULL) == -1) {
|
||||
free(files);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize some variables, then start the thread
|
||||
closing_time = 0;
|
||||
files[0] = master;
|
||||
files[1] = slave;
|
||||
int ret = pthread_create(&watcher, NULL, &watch_sigwinch, files);
|
||||
if (ret != 0) {
|
||||
free(files);
|
||||
errno = ret;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set the initial terminal size
|
||||
raise(SIGWINCH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* watch_sigwinch_cleanup
|
||||
*
|
||||
* Cause the SIGWINCH watcher thread to terminate
|
||||
*/
|
||||
void watch_sigwinch_cleanup(void) {
|
||||
closing_time = 1;
|
||||
raise(SIGWINCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* pump_stdin_async
|
||||
*
|
||||
* Forward data from STDIN to the given FD
|
||||
* in a seperate thread
|
||||
*/
|
||||
void pump_stdin_async(int outfd) {
|
||||
// Put stdin into raw mode
|
||||
set_stdin_raw();
|
||||
|
||||
// Pump data from stdin to the PTY
|
||||
pump_async(STDIN_FILENO, outfd);
|
||||
}
|
||||
|
||||
/**
|
||||
* pump_stdout_blocking
|
||||
*
|
||||
* Forward data from the FD to STDOUT.
|
||||
* Returns when the remote end of the FD closes.
|
||||
*
|
||||
* Before returning, restores stdin settings.
|
||||
*/
|
||||
void pump_stdout_blocking(int infd) {
|
||||
// Pump data from stdout to PTY
|
||||
pump_ex(infd, STDOUT_FILENO, 0 /* Don't close output when done */);
|
||||
|
||||
// Cleanup
|
||||
restore_stdin();
|
||||
watch_sigwinch_cleanup();
|
||||
}
|
116
pts.h
Normal file
116
pts.h
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2013, Tan Chee Eng (@tan-ce)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* pts.h
|
||||
*
|
||||
* Manages the pseudo-terminal driver on Linux/Android and provides some
|
||||
* helper functions to handle raw input mode and terminal window resizing
|
||||
*/
|
||||
|
||||
#ifndef _PTS_H_
|
||||
#define _PTS_H_
|
||||
|
||||
/**
|
||||
* pts_open
|
||||
*
|
||||
* Opens a pts device and returns the name of the slave tty device.
|
||||
*
|
||||
* Arguments
|
||||
* slave_name the name of the slave device
|
||||
* slave_name_size the size of the buffer passed via slave_name
|
||||
*
|
||||
* Return Values
|
||||
* on failure either -2 or -1 (errno set) is returned.
|
||||
* on success, the file descriptor of the master device is returned.
|
||||
*/
|
||||
int pts_open(char *slave_name, size_t slave_name_size);
|
||||
|
||||
/**
|
||||
* set_stdin_raw
|
||||
*
|
||||
* Changes stdin to raw unbuffered mode, disables echo,
|
||||
* auto carriage return, etc.
|
||||
*
|
||||
* Return Value
|
||||
* on failure -1, and errno is set
|
||||
* on success 0
|
||||
*/
|
||||
int set_stdin_raw(void);
|
||||
|
||||
/**
|
||||
* restore_stdin
|
||||
*
|
||||
* Restore termios on stdin to the state it was before
|
||||
* set_stdin_raw() was called. If set_stdin_raw() was
|
||||
* never called, does nothing and doesn't return an error.
|
||||
*
|
||||
* This function is async-safe.
|
||||
*
|
||||
* Return Value
|
||||
* on failure, -1 and errno is set
|
||||
* on success, 0
|
||||
*/
|
||||
int restore_stdin(void);
|
||||
|
||||
/**
|
||||
* watch_sigwinch_async
|
||||
*
|
||||
* After calling this function, if the application receives
|
||||
* SIGWINCH, the terminal window size will be read from
|
||||
* "input" and set on "output".
|
||||
*
|
||||
* NOTE: This function blocks SIGWINCH and spawns a thread.
|
||||
*
|
||||
* Arguments
|
||||
* master A file descriptor of the TTY window size to follow
|
||||
* slave A file descriptor of the TTY window size which is
|
||||
* to be set on SIGWINCH
|
||||
*
|
||||
* Return Value
|
||||
* on failure, -1 and errno will be set. In this case, no
|
||||
* thread has been spawned and SIGWINCH will not be
|
||||
* blocked.
|
||||
* on success, 0
|
||||
*/
|
||||
int watch_sigwinch_async(int master, int slave);
|
||||
|
||||
/**
|
||||
* watch_sigwinch_cleanup
|
||||
*
|
||||
* Cause the SIGWINCH watcher thread to terminate
|
||||
*/
|
||||
void watch_sigwinch_cleanup(void);
|
||||
|
||||
/**
|
||||
* pump_stdin_async
|
||||
*
|
||||
* Forward data from STDIN to the given FD
|
||||
* in a seperate thread
|
||||
*/
|
||||
void pump_stdin_async(int outfd);
|
||||
|
||||
/**
|
||||
* pump_stdout_blocking
|
||||
*
|
||||
* Forward data from the FD to STDOUT.
|
||||
* Returns when the remote end of the FD closes.
|
||||
*
|
||||
* Before returning, restores stdin settings.
|
||||
*/
|
||||
void pump_stdout_blocking(int infd);
|
||||
|
||||
#endif
|
145
reboot/reboot.c
Normal file
145
reboot/reboot.c
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
** Copyright 2013, Kevin Cernekee <cernekee@gmail.com>
|
||||
**
|
||||
** This was reverse engineered from an HTC "reboot" binary and is an attempt
|
||||
** to remain bug-compatible with the original.
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#define SETTINGS_DB "/data/data/com.android.providers.settings/databases/settings.db"
|
||||
#define SCREEN_LOCK_STATUS "/data/misc/screen_lock_status"
|
||||
|
||||
int pattern_lock;
|
||||
|
||||
int sqlcallback(void *private, int n_columns, char **col_values, char **col_names)
|
||||
{
|
||||
pattern_lock = 0;
|
||||
|
||||
if (n_columns == 0 || !col_values[0])
|
||||
return 0;
|
||||
|
||||
if (!strcmp(col_values[0], "1"))
|
||||
pattern_lock = 1;
|
||||
|
||||
__android_log_print(ANDROID_LOG_INFO, NULL,
|
||||
"sqlcallback %s = %s, pattern_locks= %d\n",
|
||||
col_names[0], col_values[0], pattern_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int checkPatternLock(void)
|
||||
{
|
||||
sqlite3 *pDb = NULL;
|
||||
char *errmsg = NULL;
|
||||
|
||||
if (sqlite3_open(SETTINGS_DB, &pDb) != 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, NULL,
|
||||
"sqlite3_open error");
|
||||
/* BUG? probably shouldn't call sqlite3_close() if open failed */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sqlite3_exec(pDb,
|
||||
"select value from system where name= \"lock_pattern_autolock\"",
|
||||
sqlcallback, "checkPatternLock", &errmsg) != 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, NULL,
|
||||
"SQL error: %s\n", errmsg);
|
||||
sqlite3_free(errmsg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
sqlite3_close(pDb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int no_sync = 0, power_off = 0;
|
||||
int ret;
|
||||
|
||||
opterr = 0;
|
||||
while ((ret = getopt(argc, argv, "np")) != -1) {
|
||||
switch (ret) {
|
||||
case 'n':
|
||||
no_sync = 1;
|
||||
break;
|
||||
case 'p':
|
||||
power_off = 1;
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n",
|
||||
argv[0]);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > (optind + 1)) {
|
||||
fprintf(stderr, "%s: too many arguments\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* BUG: this should use optind */
|
||||
if (argc > 1 && !strcmp(argv[1], "oem-78")) {
|
||||
/* HTC RUU mode: "reboot oem-78" */
|
||||
|
||||
FILE *f;
|
||||
char buf[5];
|
||||
|
||||
checkPatternLock();
|
||||
f = fopen(SCREEN_LOCK_STATUS, "r");
|
||||
if (!f) {
|
||||
fputs("5\n", stderr);
|
||||
exit(0);
|
||||
}
|
||||
fgets(buf, 5, f);
|
||||
if (atoi(buf) == 1) {
|
||||
if (pattern_lock != 0) {
|
||||
fputs("1\n", stderr);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
fputs("0\n", stderr);
|
||||
}
|
||||
|
||||
if (!no_sync)
|
||||
sync();
|
||||
|
||||
if (power_off) {
|
||||
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
|
||||
LINUX_REBOOT_CMD_POWER_OFF, 0);
|
||||
} else if (optind >= argc) {
|
||||
ret = reboot(LINUX_REBOOT_CMD_RESTART);
|
||||
} else {
|
||||
ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
|
||||
LINUX_REBOOT_CMD_RESTART2, argv[optind]);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
perror("reboot");
|
||||
exit(1);
|
||||
} else {
|
||||
fputs("reboot returned\n", stderr);
|
||||
exit(0);
|
||||
}
|
||||
}
|
3163
sqlite3/shell.c
Normal file
3163
sqlite3/shell.c
Normal file
File diff suppressed because it is too large
Load Diff
137414
sqlite3/sqlite3.c
Normal file
137414
sqlite3/sqlite3.c
Normal file
File diff suppressed because it is too large
Load Diff
7160
sqlite3/sqlite3.h
Normal file
7160
sqlite3/sqlite3.h
Normal file
File diff suppressed because it is too large
Load Diff
447
sqlite3/sqlite3ext.h
Normal file
447
sqlite3/sqlite3ext.h
Normal file
@ -0,0 +1,447 @@
|
||||
/*
|
||||
** 2006 June 7
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the SQLite interface for use by
|
||||
** shared libraries that want to be imported as extensions into
|
||||
** an SQLite instance. Shared libraries that intend to be loaded
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
*/
|
||||
#ifndef _SQLITE3EXT_H_
|
||||
#define _SQLITE3EXT_H_
|
||||
#include "sqlite3.h"
|
||||
|
||||
typedef struct sqlite3_api_routines sqlite3_api_routines;
|
||||
|
||||
/*
|
||||
** The following structure holds pointers to all of the SQLite API
|
||||
** routines.
|
||||
**
|
||||
** WARNING: In order to maintain backwards compatibility, add new
|
||||
** interfaces to the end of this structure only. If you insert new
|
||||
** interfaces in the middle of this structure, then older different
|
||||
** versions of SQLite will not be able to load each others' shared
|
||||
** libraries!
|
||||
*/
|
||||
struct sqlite3_api_routines {
|
||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
||||
int (*aggregate_count)(sqlite3_context*);
|
||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
||||
int (*bind_null)(sqlite3_stmt*,int);
|
||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
||||
int (*busy_timeout)(sqlite3*,int ms);
|
||||
int (*changes)(sqlite3*);
|
||||
int (*close)(sqlite3*);
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const void*));
|
||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_count)(sqlite3_stmt*pStmt);
|
||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
||||
const char * (*column_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||
int (*complete)(const char*sql);
|
||||
int (*complete16)(const void*sql);
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||
int (*data_count)(sqlite3_stmt*pStmt);
|
||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||
int (*declare_vtab)(sqlite3*,const char*);
|
||||
int (*enable_shared_cache)(int);
|
||||
int (*errcode)(sqlite3*db);
|
||||
const char * (*errmsg)(sqlite3*);
|
||||
const void * (*errmsg16)(sqlite3*);
|
||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
||||
int (*expired)(sqlite3_stmt*);
|
||||
int (*finalize)(sqlite3_stmt*pStmt);
|
||||
void (*free)(void*);
|
||||
void (*free_table)(char**result);
|
||||
int (*get_autocommit)(sqlite3*);
|
||||
void * (*get_auxdata)(sqlite3_context*,int);
|
||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||
int (*global_recover)(void);
|
||||
void (*interruptx)(sqlite3*);
|
||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||
const char * (*libversion)(void);
|
||||
int (*libversion_number)(void);
|
||||
void *(*malloc)(int);
|
||||
char * (*mprintf)(const char*,...);
|
||||
int (*open)(const char*,sqlite3**);
|
||||
int (*open16)(const void*,sqlite3**);
|
||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
||||
void *(*realloc)(void*,int);
|
||||
int (*reset)(sqlite3_stmt*pStmt);
|
||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_double)(sqlite3_context*,double);
|
||||
void (*result_error)(sqlite3_context*,const char*,int);
|
||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
||||
void (*result_int)(sqlite3_context*,int);
|
||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
||||
void (*result_null)(sqlite3_context*);
|
||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||
const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*snprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||
char const**,char const**,int*,int*,int*);
|
||||
void (*thread_cleanup)(void);
|
||||
int (*total_changes)(sqlite3*);
|
||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
||||
sqlite_int64),void*);
|
||||
void * (*user_data)(sqlite3_context*);
|
||||
const void * (*value_blob)(sqlite3_value*);
|
||||
int (*value_bytes)(sqlite3_value*);
|
||||
int (*value_bytes16)(sqlite3_value*);
|
||||
double (*value_double)(sqlite3_value*);
|
||||
int (*value_int)(sqlite3_value*);
|
||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
||||
int (*value_numeric_type)(sqlite3_value*);
|
||||
const unsigned char * (*value_text)(sqlite3_value*);
|
||||
const void * (*value_text16)(sqlite3_value*);
|
||||
const void * (*value_text16be)(sqlite3_value*);
|
||||
const void * (*value_text16le)(sqlite3_value*);
|
||||
int (*value_type)(sqlite3_value*);
|
||||
char *(*vmprintf)(const char*,va_list);
|
||||
/* Added ??? */
|
||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
||||
/* Added by 3.3.13 */
|
||||
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
int (*clear_bindings)(sqlite3_stmt*);
|
||||
/* Added by 3.4.1 */
|
||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
||||
void (*xDestroy)(void *));
|
||||
/* Added by 3.5.0 */
|
||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
||||
int (*blob_bytes)(sqlite3_blob*);
|
||||
int (*blob_close)(sqlite3_blob*);
|
||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
||||
int,sqlite3_blob**);
|
||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*),
|
||||
void(*)(void*));
|
||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
||||
sqlite3_int64 (*memory_highwater)(int);
|
||||
sqlite3_int64 (*memory_used)(void);
|
||||
sqlite3_mutex *(*mutex_alloc)(int);
|
||||
void (*mutex_enter)(sqlite3_mutex*);
|
||||
void (*mutex_free)(sqlite3_mutex*);
|
||||
void (*mutex_leave)(sqlite3_mutex*);
|
||||
int (*mutex_try)(sqlite3_mutex*);
|
||||
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
||||
int (*release_memory)(int);
|
||||
void (*result_error_nomem)(sqlite3_context*);
|
||||
void (*result_error_toobig)(sqlite3_context*);
|
||||
int (*sleep)(int);
|
||||
void (*soft_heap_limit)(int);
|
||||
sqlite3_vfs *(*vfs_find)(const char*);
|
||||
int (*vfs_register)(sqlite3_vfs*,int);
|
||||
int (*vfs_unregister)(sqlite3_vfs*);
|
||||
int (*xthreadsafe)(void);
|
||||
void (*result_zeroblob)(sqlite3_context*,int);
|
||||
void (*result_error_code)(sqlite3_context*,int);
|
||||
int (*test_control)(int, ...);
|
||||
void (*randomness)(int,void*);
|
||||
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
||||
int (*extended_result_codes)(sqlite3*,int);
|
||||
int (*limit)(sqlite3*,int,int);
|
||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
||||
const char *(*sql)(sqlite3_stmt*);
|
||||
int (*status)(int,int*,int*,int);
|
||||
int (*backup_finish)(sqlite3_backup*);
|
||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
||||
int (*backup_pagecount)(sqlite3_backup*);
|
||||
int (*backup_remaining)(sqlite3_backup*);
|
||||
int (*backup_step)(sqlite3_backup*,int);
|
||||
const char *(*compileoption_get)(int);
|
||||
int (*compileoption_used)(const char*);
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void(*xDestroy)(void*));
|
||||
int (*db_config)(sqlite3*,int,...);
|
||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||
int (*extended_errcode)(sqlite3*);
|
||||
void (*log)(int,const char*,...);
|
||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
||||
const char *(*sourceid)(void);
|
||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
||||
int (*strnicmp)(const char*,const char*,int);
|
||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
||||
int (*vtab_config)(sqlite3*,int op,...);
|
||||
int (*vtab_on_conflict)(sqlite3*);
|
||||
};
|
||||
|
||||
/*
|
||||
** The following macros redefine the API routines so that they are
|
||||
** redirected throught the global sqlite3_api structure.
|
||||
**
|
||||
** This header file is also used by the loadext.c source file
|
||||
** (part of the main SQLite library - not an extension) so that
|
||||
** it can get access to the sqlite3_api_routines structure
|
||||
** definition. But the main library does not want to redefine
|
||||
** the API. So the redefinition macros are only valid if the
|
||||
** SQLITE_CORE macros is undefined.
|
||||
*/
|
||||
#ifndef SQLITE_CORE
|
||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||
#endif
|
||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
||||
#define sqlite3_changes sqlite3_api->changes
|
||||
#define sqlite3_close sqlite3_api->close
|
||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
||||
#define sqlite3_column_count sqlite3_api->column_count
|
||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
||||
#define sqlite3_column_double sqlite3_api->column_double
|
||||
#define sqlite3_column_int sqlite3_api->column_int
|
||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
||||
#define sqlite3_column_name sqlite3_api->column_name
|
||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
||||
#define sqlite3_column_text sqlite3_api->column_text
|
||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
||||
#define sqlite3_column_type sqlite3_api->column_type
|
||||
#define sqlite3_column_value sqlite3_api->column_value
|
||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||||
#define sqlite3_complete sqlite3_api->complete
|
||||
#define sqlite3_complete16 sqlite3_api->complete16
|
||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||||
#define sqlite3_create_function sqlite3_api->create_function
|
||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||||
#define sqlite3_create_module sqlite3_api->create_module
|
||||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||||
#define sqlite3_data_count sqlite3_api->data_count
|
||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
||||
#define sqlite3_errcode sqlite3_api->errcode
|
||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||
#define sqlite3_exec sqlite3_api->exec
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_expired sqlite3_api->expired
|
||||
#endif
|
||||
#define sqlite3_finalize sqlite3_api->finalize
|
||||
#define sqlite3_free sqlite3_api->free
|
||||
#define sqlite3_free_table sqlite3_api->free_table
|
||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||
#define sqlite3_get_table sqlite3_api->get_table
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#endif
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||
#define sqlite3_malloc sqlite3_api->malloc
|
||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
||||
#define sqlite3_open sqlite3_api->open
|
||||
#define sqlite3_open16 sqlite3_api->open16
|
||||
#define sqlite3_prepare sqlite3_api->prepare
|
||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_profile sqlite3_api->profile
|
||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
||||
#define sqlite3_realloc sqlite3_api->realloc
|
||||
#define sqlite3_reset sqlite3_api->reset
|
||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
||||
#define sqlite3_result_double sqlite3_api->result_double
|
||||
#define sqlite3_result_error sqlite3_api->result_error
|
||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
||||
#define sqlite3_result_int sqlite3_api->result_int
|
||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
||||
#define sqlite3_result_null sqlite3_api->result_null
|
||||
#define sqlite3_result_text sqlite3_api->result_text
|
||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
||||
#define sqlite3_result_value sqlite3_api->result_value
|
||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||
#define sqlite3_snprintf sqlite3_api->snprintf
|
||||
#define sqlite3_step sqlite3_api->step
|
||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||
#define sqlite3_trace sqlite3_api->trace
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||
#endif
|
||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||
#define sqlite3_user_data sqlite3_api->user_data
|
||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
||||
#define sqlite3_value_double sqlite3_api->value_double
|
||||
#define sqlite3_value_int sqlite3_api->value_int
|
||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
||||
#define sqlite3_value_text sqlite3_api->value_text
|
||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
||||
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
||||
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
||||
#define sqlite3_blob_close sqlite3_api->blob_close
|
||||
#define sqlite3_blob_open sqlite3_api->blob_open
|
||||
#define sqlite3_blob_read sqlite3_api->blob_read
|
||||
#define sqlite3_blob_write sqlite3_api->blob_write
|
||||
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
||||
#define sqlite3_file_control sqlite3_api->file_control
|
||||
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
||||
#define sqlite3_memory_used sqlite3_api->memory_used
|
||||
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
||||
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
||||
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
||||
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
||||
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
||||
#define sqlite3_open_v2 sqlite3_api->open_v2
|
||||
#define sqlite3_release_memory sqlite3_api->release_memory
|
||||
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
||||
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
||||
#define sqlite3_sleep sqlite3_api->sleep
|
||||
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
||||
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
||||
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
||||
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
||||
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
||||
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
||||
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
||||
#define sqlite3_test_control sqlite3_api->test_control
|
||||
#define sqlite3_randomness sqlite3_api->randomness
|
||||
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
||||
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
||||
#define sqlite3_limit sqlite3_api->limit
|
||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
||||
#define sqlite3_sql sqlite3_api->sql
|
||||
#define sqlite3_status sqlite3_api->status
|
||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
||||
#define sqlite3_db_config sqlite3_api->db_config
|
||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
||||
#define sqlite3_db_status sqlite3_api->db_status
|
||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
||||
#define sqlite3_log sqlite3_api->log
|
||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
||||
#endif /* SQLITE_CORE */
|
||||
|
||||
#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
|
||||
#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v;
|
||||
|
||||
#endif /* _SQLITE3EXT_H_ */
|
221
su.h
Normal file
221
su.h
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
** Copyright 2010, Adam Shanks (@ChainsDD)
|
||||
** Copyright 2008, Zinx Verituse (@zinxv)
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SU_h
|
||||
#define SU_h 1
|
||||
|
||||
#ifdef LOG_TAG
|
||||
#undef LOG_TAG
|
||||
#endif
|
||||
#define LOG_TAG "su"
|
||||
|
||||
#ifndef AID_SHELL
|
||||
#define AID_SHELL (get_shell_uid())
|
||||
#endif
|
||||
|
||||
#ifndef AID_ROOT
|
||||
#define AID_ROOT 0
|
||||
#endif
|
||||
|
||||
#ifndef AID_SYSTEM
|
||||
#define AID_SYSTEM (get_system_uid())
|
||||
#endif
|
||||
|
||||
#ifndef AID_RADIO
|
||||
#define AID_RADIO (get_radio_uid())
|
||||
#endif
|
||||
|
||||
// CyanogenMod-specific behavior
|
||||
#define CM_ROOT_ACCESS_DISABLED 0
|
||||
#define CM_ROOT_ACCESS_APPS_ONLY 1
|
||||
#define CM_ROOT_ACCESS_ADB_ONLY 2
|
||||
#define CM_ROOT_ACCESS_APPS_AND_ADB 3
|
||||
|
||||
// DO NOT CHANGE LINE BELOW, java package name will always be the same
|
||||
#define JAVA_PACKAGE_NAME "com.koushikdutta.superuser"
|
||||
|
||||
// If --rename-manifest-package is used in AAPT, this
|
||||
// must be changed to correspond to the new APK package name
|
||||
// See the two Android.mk files for more details.
|
||||
#ifndef REQUESTOR
|
||||
#define REQUESTOR JAVA_PACKAGE_NAME
|
||||
#endif
|
||||
// This is used if wrapping the fragment classes and activities
|
||||
// with classes in another package. CM requirement.
|
||||
#ifndef REQUESTOR_PREFIX
|
||||
#define REQUESTOR_PREFIX JAVA_PACKAGE_NAME
|
||||
#endif
|
||||
#define REQUESTOR_DATA_PATH "/data/data/"
|
||||
#define REQUESTOR_FILES_PATH REQUESTOR_DATA_PATH REQUESTOR "/files"
|
||||
#define REQUESTOR_USER_PATH "/data/user/"
|
||||
#define REQUESTOR_CACHE_PATH "/dev/" REQUESTOR
|
||||
#define REQUESTOR_DAEMON_PATH REQUESTOR_CACHE_PATH ".daemon"
|
||||
|
||||
// there's no guarantee that the db or files are actually created named as such by
|
||||
// SQLiteOpenHelper, etc. Though that is the behavior as of current.
|
||||
// it is up to the Android application to symlink as appropriate.
|
||||
#define REQUESTOR_DATABASE_PATH REQUESTOR "/databases/su.sqlite"
|
||||
#define REQUESTOR_MULTIUSER_MODE REQUESTOR_FILES_PATH "/multiuser_mode"
|
||||
|
||||
#define DEFAULT_SHELL "/system/bin/sh"
|
||||
|
||||
#define xstr(a) str(a)
|
||||
#define str(a) #a
|
||||
|
||||
#ifndef VERSION_CODE
|
||||
#define VERSION_CODE 17
|
||||
#endif
|
||||
#define VERSION xstr(VERSION_CODE) " " REQUESTOR
|
||||
|
||||
#define PROTO_VERSION 1
|
||||
|
||||
struct su_initiator {
|
||||
pid_t pid;
|
||||
unsigned uid;
|
||||
unsigned user;
|
||||
char name[64];
|
||||
char bin[PATH_MAX];
|
||||
char args[4096];
|
||||
};
|
||||
|
||||
struct su_request {
|
||||
unsigned uid;
|
||||
char name[64];
|
||||
int login;
|
||||
int keepenv;
|
||||
char *shell;
|
||||
char *context;
|
||||
char *command;
|
||||
char **argv;
|
||||
int argc;
|
||||
int optind;
|
||||
};
|
||||
|
||||
struct su_user_info {
|
||||
// the user in android userspace (multiuser)
|
||||
// that invoked this action.
|
||||
unsigned android_user_id;
|
||||
// how su behaves with multiuser. see enum below.
|
||||
int multiuser_mode;
|
||||
// path to superuser directory. this is populated according
|
||||
// to the multiuser mode.
|
||||
// this is used to check uid/gid for protecting socket.
|
||||
// this is used instead of database, as it is more likely
|
||||
// to exist. db will not exist if su has never launched.
|
||||
char base_path[PATH_MAX];
|
||||
// path to su database. this is populated according
|
||||
// to the multiuser mode.
|
||||
char database_path[PATH_MAX];
|
||||
};
|
||||
|
||||
struct su_bind {
|
||||
const char *from;
|
||||
const char *to;
|
||||
};
|
||||
|
||||
struct su_context {
|
||||
struct su_initiator from;
|
||||
struct su_request to;
|
||||
struct su_user_info user;
|
||||
struct su_bind bind;
|
||||
const char *init;
|
||||
mode_t umask;
|
||||
char sock_path[PATH_MAX];
|
||||
};
|
||||
|
||||
// multiuser su behavior
|
||||
typedef enum {
|
||||
// only owner can su
|
||||
MULTIUSER_MODE_OWNER_ONLY = 0,
|
||||
// owner gets a su prompt
|
||||
MULTIUSER_MODE_OWNER_MANAGED = 1,
|
||||
// user gets a su prompt
|
||||
MULTIUSER_MODE_USER = 2,
|
||||
MULTIUSER_MODE_NONE = 3,
|
||||
} multiuser_mode_t;
|
||||
|
||||
#define MULTIUSER_VALUE_OWNER_ONLY "owner"
|
||||
#define MULTIUSER_VALUE_OWNER_MANAGED "managed"
|
||||
#define MULTIUSER_VALUE_USER "user"
|
||||
#define MULTIUSER_VALUE_NONE "none"
|
||||
|
||||
typedef enum {
|
||||
INTERACTIVE = 0,
|
||||
DENY = 1,
|
||||
ALLOW = 2,
|
||||
} policy_t;
|
||||
|
||||
extern policy_t database_check(struct su_context *ctx);
|
||||
extern void set_identity(unsigned int uid);
|
||||
extern int send_request(struct su_context *ctx);
|
||||
extern int send_result(struct su_context *ctx, policy_t policy);
|
||||
|
||||
static inline char *get_command(const struct su_request *to)
|
||||
{
|
||||
if (to->command)
|
||||
return to->command;
|
||||
if (to->shell)
|
||||
return to->shell;
|
||||
char* ret = to->argv[to->optind];
|
||||
if (ret)
|
||||
return ret;
|
||||
return DEFAULT_SHELL;
|
||||
}
|
||||
|
||||
void exec_loge(const char* fmt, ...);
|
||||
void exec_logw(const char* fmt, ...);
|
||||
void exec_logd(const char* fmt, ...);
|
||||
|
||||
int run_daemon();
|
||||
int connect_daemon(int argc, char *argv[], int ppid);
|
||||
int su_main(int argc, char *argv[]);
|
||||
int su_main_nodaemon(int argc, char *argv[]);
|
||||
// for when you give zero fucks about the state of the child process.
|
||||
// this version of fork understands you don't care about the child.
|
||||
// deadbeat dad fork.
|
||||
int fork_zero_fucks();
|
||||
|
||||
void hacks_init();
|
||||
void hacks_update_context(struct su_context* ctxt);
|
||||
|
||||
// fallback to using /system/bin/log.
|
||||
// can't use liblog.so because this is a static binary.
|
||||
#ifndef LOGE
|
||||
#define LOGE exec_loge
|
||||
#endif
|
||||
#ifndef LOGD
|
||||
#define LOGD exec_logd
|
||||
#endif
|
||||
#ifndef LOGW
|
||||
#define LOGW exec_logw
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#undef LOGE
|
||||
#define LOGE(fmt,args...) fprintf(stderr, fmt, ##args)
|
||||
#undef LOGD
|
||||
#define LOGD(fmt,args...) fprintf(stderr, fmt, ##args)
|
||||
#undef LOGW
|
||||
#define LOGW(fmt,args...) fprintf(stderr, fmt, ##args)
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#define PLOGE(fmt,args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
|
||||
#define PLOGEV(fmt,err,args...) LOGE(fmt " failed with %d: %s", ##args, err, strerror(err))
|
||||
|
||||
#endif
|
112
utils.c
Normal file
112
utils.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
** Copyright 2012, The CyanogenMod Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
/* reads a file, making sure it is terminated with \n \0 */
|
||||
char* read_file(const char *fn)
|
||||
{
|
||||
struct stat st;
|
||||
char *data = NULL;
|
||||
|
||||
int fd = open(fn, O_RDONLY);
|
||||
if (fd < 0) return data;
|
||||
|
||||
if (fstat(fd, &st)) goto oops;
|
||||
|
||||
data = malloc(st.st_size + 2);
|
||||
if (!data) goto oops;
|
||||
|
||||
if (read(fd, data, st.st_size) != st.st_size) goto oops;
|
||||
close(fd);
|
||||
data[st.st_size] = '\n';
|
||||
data[st.st_size + 1] = 0;
|
||||
return data;
|
||||
|
||||
oops:
|
||||
close(fd);
|
||||
if (data) free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int get_property(const char *data, char *found, const char *searchkey, const char *not_found)
|
||||
{
|
||||
char *key, *value, *eol, *sol, *tmp;
|
||||
if (data == NULL) goto defval;
|
||||
int matched = 0;
|
||||
sol = strdup(data);
|
||||
while((eol = strchr(sol, '\n'))) {
|
||||
key = sol;
|
||||
*eol++ = 0;
|
||||
sol = eol;
|
||||
|
||||
value = strchr(key, '=');
|
||||
if(value == 0) continue;
|
||||
*value++ = 0;
|
||||
|
||||
while(isspace(*key)) key++;
|
||||
if(*key == '#') continue;
|
||||
tmp = value - 2;
|
||||
while((tmp > key) && isspace(*tmp)) *tmp-- = 0;
|
||||
|
||||
while(isspace(*value)) value++;
|
||||
tmp = eol - 2;
|
||||
while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
|
||||
|
||||
if (strncmp(searchkey, key, strlen(searchkey)) == 0) {
|
||||
matched = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int len;
|
||||
if (matched) {
|
||||
len = strlen(value);
|
||||
if (len >= PROPERTY_VALUE_MAX)
|
||||
return -1;
|
||||
memcpy(found, value, len + 1);
|
||||
} else goto defval;
|
||||
return len;
|
||||
|
||||
defval:
|
||||
len = strlen(not_found);
|
||||
memcpy(found, not_found, len + 1);
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fast version of get_property which purpose is to check
|
||||
* whether the property with given prefix exists.
|
||||
*
|
||||
* Assume nobody is stupid enough to put a propery with prefix ro.cm.version
|
||||
* in his build.prop on a non-CM ROM and comment it out.
|
||||
*/
|
||||
int check_property(const char *data, const char *prefix)
|
||||
{
|
||||
if (!data)
|
||||
return 0;
|
||||
return strstr(data, prefix) != NULL;
|
||||
}
|
30
utils.h
Normal file
30
utils.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
** Copyright 2012, The CyanogenMod Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _UTILS_H_
|
||||
#define _UTILS_H_
|
||||
|
||||
#ifndef PROPERTY_VALUE_MAX
|
||||
#define PROPERTY_VALUE_MAX 92
|
||||
#endif
|
||||
|
||||
/* reads a file, making sure it is terminated with \n \0 */
|
||||
extern char* read_file(const char *fn);
|
||||
|
||||
extern int get_property(const char *data, char *found, const char *searchkey,
|
||||
const char *not_found);
|
||||
extern int check_property(const char *data, const char *prefix);
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user