Home | History | Annotate | Download | only in logwrapper
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <string.h>
     18 #include <sys/types.h>
     19 #include <sys/wait.h>
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 #include <unistd.h>
     23 #include <errno.h>
     24 #include <fcntl.h>
     25 
     26 #include "private/android_filesystem_config.h"
     27 #include "cutils/log.h"
     28 
     29 void fatal(const char *msg) {
     30     fprintf(stderr, "%s", msg);
     31     LOG(LOG_ERROR, "logwrapper", "%s", msg);
     32     exit(-1);
     33 }
     34 
     35 void usage() {
     36     fatal(
     37         "Usage: logwrapper [-x] BINARY [ARGS ...]\n"
     38         "\n"
     39         "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
     40         "the Android logging system. Tag is set to BINARY, priority is\n"
     41         "always LOG_INFO.\n"
     42         "\n"
     43         "-x: Causes logwrapper to SIGSEGV when BINARY terminates\n"
     44         "    fault address is set to the status of wait()\n");
     45 }
     46 
     47 void parent(const char *tag, int seg_fault_on_exit, int parent_read) {
     48     int status;
     49     char buffer[4096];
     50 
     51     int a = 0;  // start index of unprocessed data
     52     int b = 0;  // end index of unprocessed data
     53     int sz;
     54     while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) {
     55 
     56         sz += b;
     57         // Log one line at a time
     58         for (b = 0; b < sz; b++) {
     59             if (buffer[b] == '\r') {
     60                 buffer[b] = '\0';
     61             } else if (buffer[b] == '\n') {
     62                 buffer[b] = '\0';
     63                 LOG(LOG_INFO, tag, "%s", &buffer[a]);
     64                 a = b + 1;
     65             }
     66         }
     67 
     68         if (a == 0 && b == sizeof(buffer) - 1) {
     69             // buffer is full, flush
     70             buffer[b] = '\0';
     71             LOG(LOG_INFO, tag, "%s", &buffer[a]);
     72             b = 0;
     73         } else if (a != b) {
     74             // Keep left-overs
     75             b -= a;
     76             memmove(buffer, &buffer[a], b);
     77             a = 0;
     78         } else {
     79             a = 0;
     80             b = 0;
     81         }
     82 
     83     }
     84     // Flush remaining data
     85     if (a != b) {
     86         buffer[b] = '\0';
     87 	LOG(LOG_INFO, tag, "%s", &buffer[a]);
     88     }
     89     status = 0xAAAA;
     90     if (wait(&status) != -1) {  // Wait for child
     91         if (WIFEXITED(status))
     92             LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
     93                     WEXITSTATUS(status));
     94         else if (WIFSIGNALED(status))
     95             LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
     96                     WTERMSIG(status));
     97         else if (WIFSTOPPED(status))
     98             LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
     99                     WSTOPSIG(status));
    100     } else
    101         LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
    102                 strerror(errno), errno);
    103     if (seg_fault_on_exit)
    104         *(int *)status = 0;  // causes SIGSEGV with fault_address = status
    105 }
    106 
    107 void child(int argc, char* argv[]) {
    108     // create null terminated argv_child array
    109     char* argv_child[argc + 1];
    110     memcpy(argv_child, argv, argc * sizeof(char *));
    111     argv_child[argc] = NULL;
    112 
    113     if (execvp(argv_child[0], argv_child)) {
    114         LOG(LOG_ERROR, "logwrapper",
    115             "executing %s failed: %s\n", argv_child[0], strerror(errno));
    116         exit(-1);
    117     }
    118 }
    119 
    120 int main(int argc, char* argv[]) {
    121     pid_t pid;
    122     int seg_fault_on_exit = 0;
    123 
    124     int parent_ptty;
    125     int child_ptty;
    126     char *child_devname = NULL;
    127 
    128     if (argc < 2) {
    129         usage();
    130     }
    131 
    132     if (strncmp(argv[1], "-d", 2) == 0) {
    133         seg_fault_on_exit = 1;
    134         argc--;
    135         argv++;
    136     }
    137 
    138     if (argc < 2) {
    139         usage();
    140     }
    141 
    142     /* Use ptty instead of socketpair so that STDOUT is not buffered */
    143     parent_ptty = open("/dev/ptmx", O_RDWR);
    144     if (parent_ptty < 0) {
    145         fatal("Cannot create parent ptty\n");
    146     }
    147 
    148     if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
    149             ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
    150         fatal("Problem with /dev/ptmx\n");
    151     }
    152 
    153     pid = fork();
    154     if (pid < 0) {
    155         fatal("Failed to fork\n");
    156     } else if (pid == 0) {
    157         child_ptty = open(child_devname, O_RDWR);
    158         if (child_ptty < 0) {
    159             fatal("Problem with child ptty\n");
    160         }
    161 
    162         // redirect stdout and stderr
    163         close(parent_ptty);
    164         dup2(child_ptty, 1);
    165         dup2(child_ptty, 2);
    166         close(child_ptty);
    167 
    168         child(argc - 1, &argv[1]);
    169 
    170     } else {
    171         // switch user and group to "log"
    172         // this may fail if we are not root,
    173         // but in that case switching user/group is unnecessary
    174         setgid(AID_LOG);
    175         setuid(AID_LOG);
    176 
    177         parent(argv[1], seg_fault_on_exit, parent_ptty);
    178     }
    179 
    180     return 0;
    181 }
    182