#include <sys/stat.h>
#include <stdarg.h>
#include <sys/types.h>
+#include <pthread.h>
+#include <sched.h>
+#include <termios.h>
+ #include <signal.h>
+ #include <string.h>
+#ifdef SUPERUSER_EMBEDDED
+#include <cutils/multiuser.h>
+#endif
+
#include "su.h"
#include "utils.h"
+ #include "pts.h"
int is_daemon = 0;
int daemon_from_uid = 0;
int val;
int len = read(fd, &val, sizeof(int));
if (len != sizeof(int)) {
-- LOGE("unable to read int");
++ LOGE("unable to read int: %d", len);
exit(-1);
}
return val;
close(outfd);
close(errfd);
- return main(argc, argv);
+ return su_main(argc, argv, 0);
}
- static void pump(int input, int output) {
- char buf[4096];
- int len;
- while ((len = read(input, buf, 4096)) > 0) {
- write(output, buf, len);
- }
- close(input);
- close(output);
- }
-
- 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) {
- LOGE("unable to pump_async");
- exit(-1);
- }
- files[0] = input;
- files[1] = output;
- pthread_create(&writer, NULL, pump_thread, files);
- }
-
static int daemon_accept(int fd) {
is_daemon = 1;
int pid = read_int(fd);
- LOGV("remote pid: %d", pid);
- int atty = read_int(fd);
- LOGV("remote atty: %d", atty);
+ 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);
+ LOGV("remote uid: %d", daemon_from_uid);
daemon_from_pid = read_int(fd);
- LOGD("remote req pid: %d", daemon_from_pid);
+ LOGV("remote req pid: %d", daemon_from_pid);
struct ucred credentials;
int ucred_length = sizeof(struct ucred);
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);
// ack
write_int(fd, 1);
- int ptm = -1;
- char* devname = NULL;
- if (atty) {
- ptm = open("/dev/ptmx", O_RDWR);
- if (ptm <= 0) {
- PLOGE("ptm");
- goto unlink_n_exit;
- }
- if(grantpt(ptm) || unlockpt(ptm) || ((devname = (char*) ptsname(ptm)) == 0)) {
- PLOGE("ptm setup");
- close(ptm);
- unlink_n_exit:
- unlink(infile);
- unlink(errfile);
- unlink(outfile);
- exit(-1);
- }
- LOGV("devname: %s", devname);
- }
-
- int outfd = open(outfile, O_WRONLY);
- if (outfd <= 0) {
- PLOGE("outfd daemon %s", outfile);
- goto unlink_n_exit;
- }
- int errfd = open(errfile, O_WRONLY);
- if (errfd <= 0) {
- PLOGE("errfd daemon %s", errfile);
- goto unlink_n_exit;
- }
- int infd = open(infile, O_RDONLY);
- if (infd <= 0) {
- PLOGE("infd daemon %s", infile);
- goto unlink_n_exit;
- }
-
- // Wait for client to open pipes, then remove
- read_int(fd);
- unlink(infile);
- unlink(errfile);
- unlink(outfile);
-
- int code;
- // now fork and run main, watch for the child pid exit, and send that
- // across the control channel as the response.
+ // 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) {
- code = child;
- goto done;
- }
-
- // if this is the child, open the fifo streams
- // and dup2 them with stdin/stdout, and run main, which execs
- // the target.
- 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 (devname != NULL) {
- int pts = open(devname, O_RDWR);
- if(pts < 0) {
- PLOGE("pts");
- exit(-1);
- }
-
- struct termios slave_orig_term_settings; // Saved terminal settings
- tcgetattr(pts, &slave_orig_term_settings);
-
- struct termios new_term_settings;
- new_term_settings = slave_orig_term_settings;
- cfmakeraw(&new_term_settings);
- // WHY DOESN'T THIS WORK, FUUUUU
- new_term_settings.c_lflag &= ~(ECHO);
- tcsetattr(pts, TCSANOW, &new_term_settings);
+ if (child != 0) {
+ // In parent, wait for the child to exit, and send the exit code
+ // across the wire.
+ int status, code;
- setsid();
- ioctl(pts, TIOCSCTTY, 1);
+ free(pts_slave);
- close(infd);
- close(outfd);
- close(errfd);
- close(ptm);
+ LOGD("waiting for child exit");
+ if (waitpid(child, &status, 0) > 0) {
+ code = WEXITSTATUS(status);
+ }
+ else {
+ code = -1;
+ }
- errfd = pts;
- infd = pts;
- outfd = pts;
+ // 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");
}
- return run_daemon_child(infd, outfd, errfd, argc, argv);
+#ifdef SUPERUSER_EMBEDDED
+ if (mount_storage) {
+ mount_emulated_storage(multiuser_get_user_id(daemon_from_uid));
+ }
+#endif
+
+ close(fd);
+ LOGD("child exited");
+ return code;
}
- if (devname != NULL) {
- // pump ptm across the socket
- pump_async(infd, ptm);
- pump(ptm, outfd);
- }
- else {
- close(infd);
- close(outfd);
- close(errfd);
+ // We are in the child now
+ // Close the unix socket file descriptor
+ close (fd);
+
+ // Become session leader
+ if (setsid() == (pid_t) -1) {
+ PLOGE("setsid");
}
- // wait for the child to exit, and send the exit code
- // across the wire.
- int status;
- LOGV("waiting for child exit");
- if (waitpid(child, &status, 0) > 0) {
- if (WIFEXITED(status)) {
- code = WEXITSTATUS(status);
- } else if (WIFSIGNALED(status)) {
- code = 128 + WTERMSIG(status);
- } else {
- code = -1;
-
+ int ptsfd;
+ if (pts_slave[0]) {
+ // 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);
}
+
+ 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 {
+ // TODO: Check system property, if PTYs are disabled,
+ // made infd the CTTY using:
+ // ioctl(infd, TIOCSCTTY, 1);
}
- else {
- code = -1;
- }
+ free(pts_slave);
- done:
- write(fd, &code, sizeof(int));
- close(fd);
- LOGV("child exited");
- return code;
+ return run_daemon_child(infd, outfd, errfd, argc, argv);
}
int run_daemon() {
PLOGE("connect");
exit(-1);
}
- LOGD("connecting client %d", getpid());
- // Send some info to the daemon, starting with our PID
- write_int(socketfd, getpid());
+ LOGV("connecting client %d", getpid());
+
+ int mount_storage = getenv("MOUNT_EMULATED_STORAGE") != NULL;
- write_int(socketfd, isatty(STDIN_FILENO));
+ // Determine which one of our streams are attached to a TTY
+ int atty = 0;
+
+ // TODO: Check a system property and never use PTYs if
+ // the property is set.
+ 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, getppid());
- write_int(socketfd, argc);
+ 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]);
}
- // ack
+ // Wait for acknowledgement from daemon
read_int(socketfd);
- int outfd = open(outfile, O_RDONLY);
- if (outfd <= 0) {
- PLOGE("outfd %s ", outfile);
- exit(-1);
- }
- int errfd = open(errfile, O_RDONLY);
- if (errfd <= 0) {
- PLOGE("errfd %s", errfile);
- exit(-1);
+ if (atty & ATTY_IN) {
+ setup_sighandlers();
+ pump_stdin_async(ptmx);
}
- int infd = open(infile, O_WRONLY);
- if (infd <= 0) {
- PLOGE("infd %s", infile);
- exit(-1);
+ if (atty & ATTY_OUT) {
+ pump_stdout_blocking(ptmx);
}
- // notify daemon that the pipes are open.
- write_int(socketfd, 1);
-
- pump_async(STDIN_FILENO, infd);
- pump_async(errfd, STDERR_FILENO);
- pump(outfd, STDOUT_FILENO);
-
- close(infd);
- close(errfd);
- close(outfd);
-
+ // Get the exit code
int code = read_int(socketfd);
+ close(socketfd);
LOGD("client exited %d", code);
+
return code;
}