blob: 7a1ae54f966473cdb41dbc39972f353a72775f76 [file] [log] [blame]
Mark Salyzyn0175b072014-02-26 09:50:16 -08001/*
2 * Copyright (C) 2012-2013 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 <dirent.h>
18#include <errno.h>
19#include <fcntl.h>
Mark Salyzyn882f8562013-12-26 15:13:36 -080020#include <sched.h>
Mark Salyzyn0175b072014-02-26 09:50:16 -080021#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/capability.h>
Mark Salyzyneb06de72014-10-13 09:59:37 -070025#include <sys/klog.h>
Elliott Hughese5a0f202014-07-18 17:39:41 -070026#include <sys/prctl.h>
Mark Salyzyn0175b072014-02-26 09:50:16 -080027#include <sys/stat.h>
28#include <sys/types.h>
Mark Salyzyne457b742014-02-19 17:18:31 -080029#include <unistd.h>
Mark Salyzyn0175b072014-02-26 09:50:16 -080030
Mark Salyzyne457b742014-02-19 17:18:31 -080031#include <cutils/properties.h>
Mark Salyzyn56ba4b52015-01-30 15:19:48 -080032#include <cutils/sched_policy.h>
Mark Salyzyne457b742014-02-19 17:18:31 -080033
Mark Salyzyn0175b072014-02-26 09:50:16 -080034#include "private/android_filesystem_config.h"
35#include "CommandListener.h"
36#include "LogBuffer.h"
37#include "LogListener.h"
William Roberts29d238d2013-02-08 09:45:26 +090038#include "LogAudit.h"
Mark Salyzyn0175b072014-02-26 09:50:16 -080039
Mark Salyzyndfc47e82014-03-24 10:26:47 -070040//
41// The service is designed to be run by init, it does not respond well
42// to starting up manually. When starting up manually the sockets will
43// fail to open typically for one of the following reasons:
44// EADDRINUSE if logger is running.
45// EACCESS if started without precautions (below)
46//
47// Here is a cookbook procedure for starting up logd manually assuming
48// init is out of the way, pedantically all permissions and selinux
49// security is put back in place:
50//
51// setenforce 0
52// rm /dev/socket/logd*
53// chmod 777 /dev/socket
54// # here is where you would attach the debugger or valgrind for example
55// runcon u:r:logd:s0 /system/bin/logd </dev/null >/dev/null 2>&1 &
56// sleep 1
57// chmod 755 /dev/socket
58// chown logd.logd /dev/socket/logd*
59// restorecon /dev/socket/logd*
60// setenforce 1
61//
62// If minimalism prevails, typical for debugging and security is not a concern:
63//
64// setenforce 0
65// chmod 777 /dev/socket
66// logd
67//
68
Mark Salyzyn0175b072014-02-26 09:50:16 -080069static int drop_privs() {
Mark Salyzyn882f8562013-12-26 15:13:36 -080070 struct sched_param param;
71 memset(&param, 0, sizeof(param));
72
Mark Salyzyn56ba4b52015-01-30 15:19:48 -080073 if (set_sched_policy(0, SP_BACKGROUND) < 0) {
74 return -1;
75 }
76
Mark Salyzyn882f8562013-12-26 15:13:36 -080077 if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) {
78 return -1;
79 }
80
Mark Salyzyn0175b072014-02-26 09:50:16 -080081 if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
82 return -1;
83 }
84
85 if (setgid(AID_LOGD) != 0) {
86 return -1;
87 }
88
89 if (setuid(AID_LOGD) != 0) {
90 return -1;
91 }
92
93 struct __user_cap_header_struct capheader;
94 struct __user_cap_data_struct capdata[2];
95 memset(&capheader, 0, sizeof(capheader));
96 memset(&capdata, 0, sizeof(capdata));
97 capheader.version = _LINUX_CAPABILITY_VERSION_3;
98 capheader.pid = 0;
99
100 capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
William Roberts29d238d2013-02-08 09:45:26 +0900101 capdata[CAP_TO_INDEX(CAP_AUDIT_CONTROL)].permitted |= CAP_TO_MASK(CAP_AUDIT_CONTROL);
102
103 capdata[0].effective = capdata[0].permitted;
104 capdata[1].effective = capdata[1].permitted;
Mark Salyzyn0175b072014-02-26 09:50:16 -0800105 capdata[0].inheritable = 0;
106 capdata[1].inheritable = 0;
107
108 if (capset(&capheader, &capdata[0]) < 0) {
109 return -1;
110 }
111
112 return 0;
113}
114
Mark Salyzyne0fa2912014-04-28 16:39:04 -0700115// Property helper
116static bool property_get_bool(const char *key, bool def) {
117 char property[PROPERTY_VALUE_MAX];
118 property_get(key, property, "");
119
120 if (!strcasecmp(property, "true")) {
121 return true;
122 }
123 if (!strcasecmp(property, "false")) {
124 return false;
125 }
126
127 return def;
128}
129
Mark Salyzyn0175b072014-02-26 09:50:16 -0800130// Foreground waits for exit of the three main persistent threads that
131// are started here. The three threads are created to manage UNIX
132// domain client sockets for writing, reading and controlling the user
133// space logger. Additional transitory per-client threads are created
134// for each reader once they register.
135int main() {
Mark Salyzyne0fa2912014-04-28 16:39:04 -0700136 bool auditd = property_get_bool("logd.auditd", true);
137
Mark Salyzyne9bebd02014-04-03 09:55:26 -0700138 int fdDmesg = -1;
Mark Salyzyne0fa2912014-04-28 16:39:04 -0700139 if (auditd && property_get_bool("logd.auditd.dmesg", true)) {
Mark Salyzyne9bebd02014-04-03 09:55:26 -0700140 fdDmesg = open("/dev/kmsg", O_WRONLY);
141 }
142
Mark Salyzyn0175b072014-02-26 09:50:16 -0800143 if (drop_privs() != 0) {
144 return -1;
145 }
146
147 // Serves the purpose of managing the last logs times read on a
148 // socket connection, and as a reader lock on a range of log
149 // entries.
150
151 LastLogTimes *times = new LastLogTimes();
152
153 // LogBuffer is the object which is responsible for holding all
154 // log entries.
155
156 LogBuffer *logBuf = new LogBuffer(times);
157
Mark Salyzyne0fa2912014-04-28 16:39:04 -0700158 if (property_get_bool("logd.statistics.dgram_qlen", false)) {
Mark Salyzyne457b742014-02-19 17:18:31 -0800159 logBuf->enableDgramQlenStatistics();
160 }
Mark Salyzynf5fc5092014-09-21 14:22:18 -0700161 {
162 char property[PROPERTY_VALUE_MAX];
163 property_get("ro.build.type", property, "");
164 if (property_get_bool("logd.statistics",
165 !!strcmp(property, "user")
166 && !property_get_bool("ro.config.low_ram", false))) {
167 logBuf->enableStatistics();
168 }
169 }
Mark Salyzyne457b742014-02-19 17:18:31 -0800170
Mark Salyzyn0175b072014-02-26 09:50:16 -0800171 // LogReader listens on /dev/socket/logdr. When a client
172 // connects, log entries in the LogBuffer are written to the client.
173
174 LogReader *reader = new LogReader(logBuf);
175 if (reader->startListener()) {
176 exit(1);
177 }
178
179 // LogListener listens on /dev/socket/logdw for client
180 // initiated log messages. New log entries are added to LogBuffer
181 // and LogReader is notified to send updates to connected clients.
182
183 LogListener *swl = new LogListener(logBuf, reader);
Mark Salyzyn581edc12013-11-20 13:38:52 -0800184 // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
185 if (swl->startListener(300)) {
Mark Salyzyn0175b072014-02-26 09:50:16 -0800186 exit(1);
187 }
188
189 // Command listener listens on /dev/socket/logd for incoming logd
190 // administrative commands.
191
192 CommandListener *cl = new CommandListener(logBuf, reader, swl);
193 if (cl->startListener()) {
194 exit(1);
195 }
196
William Roberts29d238d2013-02-08 09:45:26 +0900197 // LogAudit listens on NETLINK_AUDIT socket for selinux
198 // initiated log messages. New log entries are added to LogBuffer
199 // and LogReader is notified to send updates to connected clients.
200
Mark Salyzyne0fa2912014-04-28 16:39:04 -0700201 if (auditd) {
202 // failure is an option ... messages are in dmesg (required by standard)
203 LogAudit *al = new LogAudit(logBuf, reader, fdDmesg);
Mark Salyzyneb06de72014-10-13 09:59:37 -0700204
205 int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
206 if (len > 0) {
207 len++;
208 char buf[len];
209
210 int rc = klogctl(KLOG_READ_ALL, buf, len);
211
212 buf[len - 1] = '\0';
213
214 for(char *ptr, *tok = buf;
215 (rc >= 0) && ((tok = strtok_r(tok, "\r\n", &ptr)));
216 tok = NULL) {
217 rc = al->log(tok);
218 }
219 }
220
Mark Salyzyne0fa2912014-04-28 16:39:04 -0700221 if (al->startListener()) {
222 delete al;
223 close(fdDmesg);
224 }
William Roberts29d238d2013-02-08 09:45:26 +0900225 }
226
Mark Salyzyn0175b072014-02-26 09:50:16 -0800227 pause();
228 exit(0);
229}
230