blob: 44d0c6ee83d59afe6a0968d3695071b393052efa [file] [log] [blame]
Alex Deng40e161a2022-04-07 08:24:23 +00001/*
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#include <signal.h>
27
28#include "cutils/log.h"
29
30// run process as root user in root group, check gid in /etc/group
31#define AID_LOG 0
32
33static pid_t child_pid = (pid_t)-1;
34
35void fatal(const char *msg) {
36 fprintf(stderr, "%s", msg);
37 ALOG(LOG_ERROR, "logwrapper", "%s", msg);
38 exit(-1);
39}
40
41void usage() {
42 fatal(
43 "Usage: logwrapper [-d] BINARY [ARGS ...]\n"
44 "\n"
45 "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
46 "the Android logging system. Tag is set to BINARY, priority is\n"
47 "always LOG_INFO.\n"
48 "\n"
49 "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n"
50 " fault address is set to the status of wait()\n");
51}
52
53void forward_signal(int sigid) {
54 ALOG(LOG_DEBUG, "logwrapper", "Got signal %d.\n", sigid);
55 if (child_pid != (pid_t)-1) {
56 ALOG(LOG_DEBUG, "logwrapper", "Forwards signal %d to %d.\n",
57 sigid, child_pid);
58 kill(child_pid, sigid);
59 }
60}
61
62void parent(const char *tag, int seg_fault_on_exit, int parent_read) {
63 int status;
64 char buffer[4096];
65
66 int a = 0; // start index of unprocessed data
67 int b = 0; // end index of unprocessed data
68 int sz;
69 bool should_log_line = true;
70
71 char *btag = basename((char *)tag);
72 if (!btag) btag = (char*) tag;
73
74 while ((sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b)) > 0) {
75
76 sz += b;
77 // Log one line at a time
78 for (b = 0; b < sz; b++) {
79 if (buffer[b] == '\r') {
80 buffer[b] = '\0';
81 } else if (buffer[b] == '\n') {
82 buffer[b] = '\0';
83 if (should_log_line) {
84 ALOG(LOG_INFO, btag, "%s", &buffer[a]);
85 } else {
86 ALOG(LOG_INFO, "logwrapper", "%s", "[TRUNCATED]");
87 }
88 should_log_line = true;
89 a = b + 1;
90 }
91 }
92
93 if (a == 0 && b == sizeof(buffer) - 1) {
94 // buffer is full, flush
95 buffer[b] = '\0';
96 if (should_log_line) {
97 ALOG(LOG_INFO, btag, "%s", &buffer[a]);
98 } else {
99 ALOG(LOG_INFO, "logwrapper", "%s", "[TRUNCATED]");
100 }
101 // Truncate the log line if its size is greater than 4096.
102 should_log_line = false;
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 // Flush remaining data
116 if (a != b) {
117 buffer[b] = '\0';
118 ALOG(LOG_INFO, btag, "%s", &buffer[a]);
119 }
120 status = 0xAAAA;
121 if (wait(&status) != -1) { // Wait for child
122 if (WIFEXITED(status) && WEXITSTATUS(status)) {
123 ALOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
124 WEXITSTATUS(status));
125 if (!seg_fault_on_exit) {
126 exit(WEXITSTATUS(status));
127 }
128 } else if (WIFSIGNALED(status))
129 ALOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
130 WTERMSIG(status));
131 else if (WIFSTOPPED(status))
132 ALOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
133 WSTOPSIG(status));
134 } else
135 ALOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
136 strerror(errno), errno);
137 if (seg_fault_on_exit)
138 *(int *)status = 0; // causes SIGSEGV with fault_address = status
139}
140
141void child(int argc, char* argv[]) {
142 // create null terminated argv_child array
143 char* argv_child[argc + 1];
144 memcpy(argv_child, argv, argc * sizeof(char *));
145 argv_child[argc] = NULL;
146
147 if (execvp(argv_child[0], argv_child)) {
148 ALOG(LOG_ERROR, "logwrapper",
149 "executing %s failed: %s\n", argv_child[0], strerror(errno));
150 exit(-1);
151 }
152}
153
154int main(int argc, char* argv[]) {
155 pid_t pid;
156 int seg_fault_on_exit = 0;
157
158 int parent_ptty;
159 int child_ptty;
160 char *child_devname = NULL;
161
162 if (argc < 2) {
163 usage();
164 }
165
166 if (strncmp(argv[1], "-d", 2) == 0) {
167 seg_fault_on_exit = 1;
168 argc--;
169 argv++;
170 }
171
172 if (argc < 2) {
173 usage();
174 }
175
176 /* Use ptty instead of socketpair so that STDOUT is not buffered */
177 parent_ptty = open("/dev/ptmx", O_RDWR);
178 if (parent_ptty < 0) {
179 fatal("Cannot create parent ptty\n");
180 }
181
182 if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
183 ((child_devname = (char*)ptsname(parent_ptty)) == 0)) {
184 fatal("Problem with /dev/ptmx\n");
185 }
186
187 pid = fork();
188 if (pid < 0) {
189 fatal("Failed to fork\n");
190 } else if (pid == 0) {
191 child_ptty = open(child_devname, O_RDWR);
192 if (child_ptty < 0) {
193 fatal("Problem with child ptty\n");
194 }
195
196 // redirect stdout and stderr
197 close(parent_ptty);
198 dup2(child_ptty, 1);
199 dup2(child_ptty, 2);
200 close(child_ptty);
201
202 child(argc - 1, &argv[1]);
203
204 } else {
205 // switch user and group to "log"
206 // this may fail if we are not root,
207 // but in that case switching user/group is unnecessary
208 setgid(AID_LOG);
209 if (setuid(AID_LOG) == -1) {
210 // set sighandler to forward signals to child process only when
211 // setuid fails and/so uid of this parent process is same to uid of
212 // child process.
213 child_pid = pid;
214 signal(SIGHUP, forward_signal);
215 signal(SIGTERM, forward_signal);
216 signal(SIGINT, forward_signal);
217 }
218
219 parent(argv[1], seg_fault_on_exit, parent_ptty);
220 }
221
222 return 0;
223}