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