1dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project/*
2dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
3dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project *
4dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * you may not use this file except in compliance with the License.
6dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * You may obtain a copy of the License at
7dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project *
8dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project *
10dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * See the License for the specific language governing permissions and
14dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * limitations under the License.
15dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */
16dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
17dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <string.h>
18dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <sys/types.h>
19dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <sys/wait.h>
20dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <stdio.h>
21dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <stdlib.h>
22dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <unistd.h>
23dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <errno.h>
24dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <fcntl.h>
251f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot#include <libgen.h>
26dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
27dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "private/android_filesystem_config.h"
28dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "cutils/log.h"
29dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
30dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectvoid fatal(const char *msg) {
31dd26bb3a00b95dc8448d2d35bcee52c8e1880096Nick Kralevich    fprintf(stderr, "%s", msg);
3261fbcbe9761bbfdebb0b1f8147faf00cbb1c6e6aSteve Block    ALOG(LOG_ERROR, "logwrapper", "%s", msg);
33dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    exit(-1);
34dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}
35dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
36dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectvoid usage() {
37dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    fatal(
381f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot        "Usage: logwrapper [-d] BINARY [ARGS ...]\n"
39dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        "\n"
40dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
41dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        "the Android logging system. Tag is set to BINARY, priority is\n"
42dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        "always LOG_INFO.\n"
43dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        "\n"
441f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot        "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n"
45dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        "    fault address is set to the status of wait()\n");
46dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}
47dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
48dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectvoid parent(const char *tag, int seg_fault_on_exit, int parent_read) {
49dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    int status;
50dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    char buffer[4096];
51dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
52dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    int a = 0;  // start index of unprocessed data
53dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    int b = 0;  // end index of unprocessed data
54dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    int sz;
551f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot
561f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot    char *btag = basename(tag);
571f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot    if (!btag) btag = (char*) tag;
581f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot
59dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) {
60dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
61dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        sz += b;
62dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        // Log one line at a time
63dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        for (b = 0; b < sz; b++) {
64dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            if (buffer[b] == '\r') {
65dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                buffer[b] = '\0';
66dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            } else if (buffer[b] == '\n') {
67dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                buffer[b] = '\0';
681f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot                ALOG(LOG_INFO, btag, "%s", &buffer[a]);
69dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                a = b + 1;
70dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            }
71dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        }
72dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
73dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        if (a == 0 && b == sizeof(buffer) - 1) {
74dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            // buffer is full, flush
75dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            buffer[b] = '\0';
761f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot            ALOG(LOG_INFO, btag, "%s", &buffer[a]);
77dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            b = 0;
78dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        } else if (a != b) {
79dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            // Keep left-overs
80dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            b -= a;
81dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            memmove(buffer, &buffer[a], b);
82dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            a = 0;
83dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        } else {
84dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            a = 0;
85dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            b = 0;
86dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        }
87dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
88dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
89dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    // Flush remaining data
90dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    if (a != b) {
91dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        buffer[b] = '\0';
921f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot        ALOG(LOG_INFO, btag, "%s", &buffer[a]);
93dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
94dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    status = 0xAAAA;
95dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    if (wait(&status) != -1) {  // Wait for child
961f28b775cfad4cf3d9112a4ad18603b34fb84facTanguy Pruvot        if (WIFEXITED(status) && WEXITSTATUS(status))
9761fbcbe9761bbfdebb0b1f8147faf00cbb1c6e6aSteve Block            ALOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
98dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                    WEXITSTATUS(status));
99dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        else if (WIFSIGNALED(status))
10061fbcbe9761bbfdebb0b1f8147faf00cbb1c6e6aSteve Block            ALOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
101dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                    WTERMSIG(status));
102dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        else if (WIFSTOPPED(status))
10361fbcbe9761bbfdebb0b1f8147faf00cbb1c6e6aSteve Block            ALOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
104dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                    WSTOPSIG(status));
105dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    } else
10661fbcbe9761bbfdebb0b1f8147faf00cbb1c6e6aSteve Block        ALOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
107dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project                strerror(errno), errno);
108dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    if (seg_fault_on_exit)
109dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        *(int *)status = 0;  // causes SIGSEGV with fault_address = status
110dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}
111dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
112dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectvoid child(int argc, char* argv[]) {
113dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    // create null terminated argv_child array
114dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    char* argv_child[argc + 1];
115dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    memcpy(argv_child, argv, argc * sizeof(char *));
116dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    argv_child[argc] = NULL;
117dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
118dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    if (execvp(argv_child[0], argv_child)) {
11961fbcbe9761bbfdebb0b1f8147faf00cbb1c6e6aSteve Block        ALOG(LOG_ERROR, "logwrapper",
120dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            "executing %s failed: %s\n", argv_child[0], strerror(errno));
121dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        exit(-1);
122dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
123dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}
124dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
125dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectint main(int argc, char* argv[]) {
126dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    pid_t pid;
127dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    int seg_fault_on_exit = 0;
128dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
129dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    int parent_ptty;
130dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    int child_ptty;
131dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    char *child_devname = NULL;
132dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
133dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    if (argc < 2) {
134dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        usage();
135dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
136dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
137dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    if (strncmp(argv[1], "-d", 2) == 0) {
138dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        seg_fault_on_exit = 1;
139dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        argc--;
140dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        argv++;
141dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
142dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
143dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    if (argc < 2) {
144dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        usage();
145dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
146dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
147dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    /* Use ptty instead of socketpair so that STDOUT is not buffered */
148dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    parent_ptty = open("/dev/ptmx", O_RDWR);
149dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    if (parent_ptty < 0) {
150dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        fatal("Cannot create parent ptty\n");
151dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
152dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
153dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
154dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
155dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        fatal("Problem with /dev/ptmx\n");
156dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
157dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
158dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    pid = fork();
159dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    if (pid < 0) {
160dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        fatal("Failed to fork\n");
161dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    } else if (pid == 0) {
162dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        child_ptty = open(child_devname, O_RDWR);
163dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        if (child_ptty < 0) {
164dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project            fatal("Problem with child ptty\n");
165dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        }
166dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
167dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        // redirect stdout and stderr
168dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        close(parent_ptty);
169dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        dup2(child_ptty, 1);
170dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        dup2(child_ptty, 2);
171dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        close(child_ptty);
172dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
173dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        child(argc - 1, &argv[1]);
174dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
175dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    } else {
176dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        // switch user and group to "log"
177dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        // this may fail if we are not root,
178dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        // but in that case switching user/group is unnecessary
179dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        setgid(AID_LOG);
180dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        setuid(AID_LOG);
181dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
182dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project        parent(argv[1], seg_fault_on_exit, parent_ptty);
183dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    }
184dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project
185dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project    return 0;
186dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project}
187