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