logwrap.c revision 611f5b4b938f2ffcf2a2e71a847e4fd12587ca4b
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/socket.h> 20#include <signal.h> 21#include <poll.h> 22#include <sys/wait.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <unistd.h> 26#include <errno.h> 27#include <fcntl.h> 28#include <libgen.h> 29#include <stdbool.h> 30 31#include <logwrap/logwrap.h> 32#include "private/android_filesystem_config.h" 33#include "cutils/log.h" 34 35#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) 36 37static int signal_fd_write; 38 39static void fatal(const char *msg) { 40 fprintf(stderr, "%s", msg); 41 ALOG(LOG_ERROR, "logwrapper", "%s", msg); 42} 43 44static int parent(const char *tag, int parent_read, int signal_fd, pid_t pid, 45 int *chld_sts) { 46 int status = 0; 47 char buffer[4096]; 48 struct pollfd poll_fds[] = { 49 [0] = { 50 .fd = signal_fd, 51 .events = POLLIN, 52 }, 53 [1] = { 54 .fd = parent_read, 55 .events = POLLIN, 56 }, 57 }; 58 int rc = 0; 59 sigset_t chldset; 60 61 int a = 0; // start index of unprocessed data 62 int b = 0; // end index of unprocessed data 63 int sz; 64 bool remote_hung = false; 65 bool found_child = false; 66 67 char *btag = basename(tag); 68 if (!btag) btag = (char*) tag; 69 70 sigemptyset(&chldset); 71 sigaddset(&chldset, SIGCHLD); 72 sigprocmask(SIG_UNBLOCK, &chldset, NULL); 73 74 while (!found_child) { 75 if (poll(poll_fds, remote_hung ? 1 : 2, -1) < 0) { 76 if (errno == EINTR) 77 continue; 78 fatal("poll failed\n"); 79 rc = -1; 80 goto err_poll; 81 } 82 83 if (!remote_hung) { 84 if (poll_fds[1].revents & POLLIN) { 85 sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b); 86 87 sz += b; 88 // Log one line at a time 89 for (b = 0; b < sz; b++) { 90 if (buffer[b] == '\r') { 91 buffer[b] = '\0'; 92 } else if (buffer[b] == '\n') { 93 buffer[b] = '\0'; 94 ALOG(LOG_INFO, btag, "%s", &buffer[a]); 95 a = b + 1; 96 } 97 } 98 99 if (a == 0 && b == sizeof(buffer) - 1) { 100 // buffer is full, flush 101 buffer[b] = '\0'; 102 ALOG(LOG_INFO, btag, "%s", &buffer[a]); 103 b = 0; 104 } else if (a != b) { 105 // Keep left-overs 106 b -= a; 107 memmove(buffer, &buffer[a], b); 108 a = 0; 109 } else { 110 a = 0; 111 b = 0; 112 } 113 } 114 115 if (poll_fds[1].revents & POLLHUP) { 116 remote_hung = true; 117 } 118 } 119 120 if (poll_fds[0].revents & POLLIN) { 121 char tmp[32]; 122 int ret; 123 124 read(signal_fd, tmp, sizeof(tmp)); 125 while (!found_child) { 126 do { 127 ret = waitpid(-1, &status, WNOHANG); 128 } while (ret < 0 && errno == EINTR); 129 130 if (ret <= 0) 131 break; 132 133 found_child = (pid == ret); 134 } 135 } 136 } 137 138 // Flush remaining data 139 if (a != b) { 140 buffer[b] = '\0'; 141 ALOG(LOG_INFO, btag, "%s", &buffer[a]); 142 } 143 144 if (WIFEXITED(status)) { 145 if (WEXITSTATUS(status)) 146 ALOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", btag, 147 WEXITSTATUS(status)); 148 } else if (WIFSIGNALED(status)) { 149 ALOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", btag, 150 WTERMSIG(status)); 151 } else if (WIFSTOPPED(status)) { 152 ALOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", btag, 153 WSTOPSIG(status)); 154 } 155 if (chld_sts != NULL) 156 *chld_sts = status; 157 158err_poll: 159 return rc; 160} 161 162static void child(int argc, char* argv[]) { 163 // create null terminated argv_child array 164 char* argv_child[argc + 1]; 165 memcpy(argv_child, argv, argc * sizeof(char *)); 166 argv_child[argc] = NULL; 167 168 if (execvp(argv_child[0], argv_child)) { 169 ALOG(LOG_ERROR, "logwrapper", 170 "executing %s failed: %s\n", argv_child[0], strerror(errno)); 171 exit(-1); 172 } 173} 174 175void sigchld_handler(int sig) { 176 write(signal_fd_write, &sig, 1); 177} 178 179int logwrap(int argc, char* argv[], int *status) { 180 pid_t pid; 181 int parent_ptty; 182 int child_ptty; 183 char *child_devname = NULL; 184 struct sigaction chldact; 185 struct sigaction oldchldact; 186 sigset_t blockset; 187 sigset_t oldset; 188 int sockets[2]; 189 int rc = 0; 190 191 /* Use ptty instead of socketpair so that STDOUT is not buffered */ 192 parent_ptty = open("/dev/ptmx", O_RDWR); 193 if (parent_ptty < 0) { 194 fatal("Cannot create parent ptty\n"); 195 rc = -1; 196 goto err_open; 197 } 198 199 if (grantpt(parent_ptty) || unlockpt(parent_ptty) || 200 ((child_devname = (char*)ptsname(parent_ptty)) == 0)) { 201 fatal("Problem with /dev/ptmx\n"); 202 rc = -1; 203 goto err_ptty; 204 } 205 206 sigemptyset(&blockset); 207 sigaddset(&blockset, SIGCHLD); 208 sigprocmask(SIG_BLOCK, &blockset, &oldset); 209 210 pid = fork(); 211 if (pid < 0) { 212 fatal("Failed to fork\n"); 213 rc = -1; 214 goto err_fork; 215 } else if (pid == 0) { 216 sigprocmask(SIG_SETMASK, &oldset, NULL); 217 close(parent_ptty); 218 219 child_ptty = open(child_devname, O_RDWR); 220 if (child_ptty < 0) { 221 fatal("Problem with child ptty\n"); 222 return -1; 223 } 224 225 // redirect stdout and stderr 226 dup2(child_ptty, 1); 227 dup2(child_ptty, 2); 228 close(child_ptty); 229 230 child(argc, argv); 231 fatal("This should never happen\n"); 232 return -1; 233 } else { 234 memset(&chldact, 0, sizeof(chldact)); 235 chldact.sa_handler = sigchld_handler; 236 chldact.sa_flags = SA_NOCLDSTOP; 237 238 sigaction(SIGCHLD, &chldact, &oldchldact); 239 if ((!(oldchldact.sa_flags & SA_SIGINFO) && 240 oldchldact.sa_handler != SIG_DFL && 241 oldchldact.sa_handler != SIG_IGN) || 242 ((oldchldact.sa_flags & SA_SIGINFO) && 243 oldchldact.sa_sigaction != NULL)) { 244 ALOG(LOG_WARN, "logwrapper", "logwrap replaced the SIGCHLD " 245 "handler and might cause interaction issues"); 246 } 247 248 rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); 249 if (rc == -1) { 250 char msg[40]; 251 252 snprintf(msg, sizeof(msg), "socketpair failed: %d\n", errno); 253 254 fatal(msg); 255 goto err_socketpair; 256 } 257 258 fcntl(sockets[0], F_SETFD, FD_CLOEXEC); 259 fcntl(sockets[0], F_SETFL, O_NONBLOCK); 260 fcntl(sockets[1], F_SETFD, FD_CLOEXEC); 261 fcntl(sockets[1], F_SETFL, O_NONBLOCK); 262 263 signal_fd_write = sockets[0]; 264 265 // switch user and group to "log" 266 // this may fail if we are not root, 267 // but in that case switching user/group is unnecessary 268 setgid(AID_LOG); 269 setuid(AID_LOG); 270 271 rc = parent(argv[0], parent_ptty, sockets[1], pid, status); 272 } 273 274 close(sockets[0]); 275 close(sockets[1]); 276err_socketpair: 277 sigaction(SIGCHLD, &oldchldact, NULL); 278err_fork: 279 sigprocmask(SIG_SETMASK, &oldset, NULL); 280err_ptty: 281 close(parent_ptty); 282err_open: 283 return rc; 284} 285