OSDN Git Service

Merge remote-tracking branch 'tance/terminal-support' into cm-11.0
authorKoushik Dutta <koushd@gmail.com>
Sun, 12 Jan 2014 19:54:13 +0000 (11:54 -0800)
committerKoushik Dutta <koushd@gmail.com>
Sun, 12 Jan 2014 19:54:13 +0000 (11:54 -0800)
Conflicts:
Superuser/jni/su/daemon.c

Change-Id: I2739f6bcbed03636d7a2fb66377977cd16999926

1  2 
Superuser/jni/Android.mk
Superuser/jni/su/daemon.c

@@@ -3,10 -3,10 +3,10 @@@ LOCAL_PATH := $(call my-dir
  include $(CLEAR_VARS)
  
  LOCAL_MODULE := su
 -LOCAL_LDFLAGS := -static
 -LOCAL_STATIC_LIBRARIES := sqlite3
 +LOCAL_FORCE_STATIC_EXECUTABLE := true
 +LOCAL_STATIC_LIBRARIES := sqlite3 libcutils libc
  LOCAL_C_INCLUDES := $(LOCAL_PATH)/sqlite3
- LOCAL_SRC_FILES := su/su.c su/activity.c su/db.c su/utils.c su/daemon.c
+ LOCAL_SRC_FILES := su/su.c su/activity.c su/db.c su/utils.c su/daemon.c su/pts.c
  include $(BUILD_EXECUTABLE)
  
  
  #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;
@@@ -54,7 -161,7 +171,7 @@@ static int read_int(int fd) 
      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;
@@@ -155,50 -225,19 +272,19 @@@ static int run_daemon_child(int infd, i
      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() {
@@@ -488,58 -496,89 +554,96 @@@ int connect_daemon(int argc, char *argv
          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;
  }