/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "private/android_filesystem_config.h" #include "cutils/log.h" void fatal(const char *msg) { fprintf(stderr, msg); LOG(LOG_ERROR, "logwrapper", msg); exit(-1); } void usage() { fatal( "Usage: logwrapper BINARY [ARGS ...]\n" "\n" "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n" "the Android logging system. Tag is set to BINARY, priority is\n" "always LOG_INFO.\n"); } void parent(const char *tag, int parent_read) { int status; char buffer[1024]; int a = 0; // start index of unprocessed data int b = 0; // end index of unprocessed data int sz; while ((sz = read(parent_read, &buffer[b], 1023 - b)) > 0) { // Log one line at a time for (b = a; b < sz; b++) { if (buffer[b] == '\n') { buffer[b] = '\0'; LOG(LOG_INFO, tag, &buffer[a]); a = b + 1; } } if (a == 0 && b == 1023) { // buffer is full, flush buffer[b] = '\0'; LOG(LOG_INFO, tag, &buffer[a]); b = 0; } else { // Keep left-overs b = sz - a; memmove(buffer, &buffer[a], b); a = 0; } } // Flush remaining data if (a != b) { buffer[b] = '\0'; LOG(LOG_INFO, tag, &buffer[a]); } wait(&status); // Wait for child } void child(int argc, char* argv[]) { // create null terminated argv_child array char* argv_child[argc + 1]; memcpy(argv_child, argv, argc * sizeof(char *)); argv_child[argc] = NULL; if (execvp(argv_child[0], argv_child)) { LOG(LOG_ERROR, "logwrapper", "executing %s failed: %s\n", argv_child[0], strerror(errno)); exit(-1); } } int main(int argc, char* argv[]) { pid_t pid; int pipe_fds[2]; int *parent_read = &pipe_fds[0]; int *child_write = &pipe_fds[1]; if (argc < 2) { usage(); } if (pipe(pipe_fds) < 0) { fatal("Cannot create pipe\n"); } pid = fork(); if (pid < 0) { fatal("Failed to fork\n"); } else if (pid == 0) { // redirect stdout and stderr close(*parent_read); dup2(*child_write, 1); dup2(*child_write, 2); close(*child_write); child(argc - 1, &argv[1]); } else { close(*child_write); // switch user and group to "log" // this may fail if we are not root, // but in that case switching user/group is unnecessary setgid(AID_LOG); setuid(AID_LOG); parent(argv[1], *parent_read); } return 0; }