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