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#include <libgen.h> 26 27#include "private/android_filesystem_config.h" 28#include "cutils/log.h" 29 30void fatal(const char *msg) { 31 fprintf(stderr, "%s", msg); 32 ALOG(LOG_ERROR, "logwrapper", "%s", msg); 33 exit(-1); 34} 35 36void usage() { 37 fatal( 38 "Usage: logwrapper [-d] BINARY [ARGS ...]\n" 39 "\n" 40 "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n" 41 "the Android logging system. Tag is set to BINARY, priority is\n" 42 "always LOG_INFO.\n" 43 "\n" 44 "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n" 45 " fault address is set to the status of wait()\n"); 46} 47 48void parent(const char *tag, int seg_fault_on_exit, int parent_read) { 49 int status; 50 char buffer[4096]; 51 52 int a = 0; // start index of unprocessed data 53 int b = 0; // end index of unprocessed data 54 int sz; 55 56 char *btag = basename(tag); 57 if (!btag) btag = (char*) tag; 58 59 while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) { 60 61 sz += b; 62 // Log one line at a time 63 for (b = 0; b < sz; b++) { 64 if (buffer[b] == '\r') { 65 buffer[b] = '\0'; 66 } else if (buffer[b] == '\n') { 67 buffer[b] = '\0'; 68 ALOG(LOG_INFO, btag, "%s", &buffer[a]); 69 a = b + 1; 70 } 71 } 72 73 if (a == 0 && b == sizeof(buffer) - 1) { 74 // buffer is full, flush 75 buffer[b] = '\0'; 76 ALOG(LOG_INFO, btag, "%s", &buffer[a]); 77 b = 0; 78 } else if (a != b) { 79 // Keep left-overs 80 b -= a; 81 memmove(buffer, &buffer[a], b); 82 a = 0; 83 } else { 84 a = 0; 85 b = 0; 86 } 87 88 } 89 // Flush remaining data 90 if (a != b) { 91 buffer[b] = '\0'; 92 ALOG(LOG_INFO, btag, "%s", &buffer[a]); 93 } 94 status = 0xAAAA; 95 if (wait(&status) != -1) { // Wait for child 96 if (WIFEXITED(status) && WEXITSTATUS(status)) 97 ALOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag, 98 WEXITSTATUS(status)); 99 else if (WIFSIGNALED(status)) 100 ALOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag, 101 WTERMSIG(status)); 102 else if (WIFSTOPPED(status)) 103 ALOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag, 104 WSTOPSIG(status)); 105 } else 106 ALOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag, 107 strerror(errno), errno); 108 if (seg_fault_on_exit) 109 *(int *)status = 0; // causes SIGSEGV with fault_address = status 110} 111 112void child(int argc, char* argv[]) { 113 // create null terminated argv_child array 114 char* argv_child[argc + 1]; 115 memcpy(argv_child, argv, argc * sizeof(char *)); 116 argv_child[argc] = NULL; 117 118 if (execvp(argv_child[0], argv_child)) { 119 ALOG(LOG_ERROR, "logwrapper", 120 "executing %s failed: %s\n", argv_child[0], strerror(errno)); 121 exit(-1); 122 } 123} 124 125int main(int argc, char* argv[]) { 126 pid_t pid; 127 int seg_fault_on_exit = 0; 128 129 int parent_ptty; 130 int child_ptty; 131 char *child_devname = NULL; 132 133 if (argc < 2) { 134 usage(); 135 } 136 137 if (strncmp(argv[1], "-d", 2) == 0) { 138 seg_fault_on_exit = 1; 139 argc--; 140 argv++; 141 } 142 143 if (argc < 2) { 144 usage(); 145 } 146 147 /* Use ptty instead of socketpair so that STDOUT is not buffered */ 148 parent_ptty = open("/dev/ptmx", O_RDWR); 149 if (parent_ptty < 0) { 150 fatal("Cannot create parent ptty\n"); 151 } 152 153 if (grantpt(parent_ptty) || unlockpt(parent_ptty) || 154 ((child_devname = (char*)ptsname(parent_ptty)) == 0)) { 155 fatal("Problem with /dev/ptmx\n"); 156 } 157 158 pid = fork(); 159 if (pid < 0) { 160 fatal("Failed to fork\n"); 161 } else if (pid == 0) { 162 child_ptty = open(child_devname, O_RDWR); 163 if (child_ptty < 0) { 164 fatal("Problem with child ptty\n"); 165 } 166 167 // redirect stdout and stderr 168 close(parent_ptty); 169 dup2(child_ptty, 1); 170 dup2(child_ptty, 2); 171 close(child_ptty); 172 173 child(argc - 1, &argv[1]); 174 175 } else { 176 // switch user and group to "log" 177 // this may fail if we are not root, 178 // but in that case switching user/group is unnecessary 179 setgid(AID_LOG); 180 setuid(AID_LOG); 181 182 parent(argv[1], seg_fault_on_exit, parent_ptty); 183 } 184 185 return 0; 186} 187