1 /*
2 * Copyright (C) 2007-2016 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 "logd_writer.h"
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <inttypes.h>
22 #include <poll.h>
23 #include <stdarg.h>
24 #include <stdatomic.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/socket.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/un.h>
32 #include <time.h>
33 #include <unistd.h>
34
35 #include <private/android_filesystem_config.h>
36 #include <private/android_logger.h>
37
38 #include "logger.h"
39 #include "uio.h"
40
41 class LogdSocket {
42 public:
BlockingSocket()43 static LogdSocket& BlockingSocket() {
44 static LogdSocket logd_socket(true);
45 return logd_socket;
46 }
NonBlockingSocket()47 static LogdSocket& NonBlockingSocket() {
48 static LogdSocket logd_socket(false);
49 return logd_socket;
50 }
51
Reconnect()52 void Reconnect() { LogdConnect(sock_); }
53
54 // Zygote uses this to clean up open FD's after fork() and before specialization. It is single
55 // threaded at this point and therefore this function is explicitly not thread safe. It sets
56 // sock_ to kUninitialized, so future logs will be safely initialized whenever they happen.
Close()57 void Close() {
58 if (sock_ != kUninitialized) {
59 close(sock_);
60 }
61 sock_ = kUninitialized;
62 }
63
sock()64 int sock() {
65 GetSocket();
66 return sock_;
67 }
68
69 private:
LogdSocket(bool blocking)70 LogdSocket(bool blocking) : blocking_(blocking) {}
71
72 // Note that it is safe to call connect() multiple times on DGRAM Unix domain sockets, so this
73 // function is used to reconnect to logd without requiring a new socket.
LogdConnect(int sock)74 static void LogdConnect(int sock) {
75 sockaddr_un un = {};
76 un.sun_family = AF_UNIX;
77 strcpy(un.sun_path, "/dev/socket/logdw");
78 TEMP_FAILURE_RETRY(connect(sock, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)));
79 }
80
81 // sock_ should only be opened once. If we see that sock_ is uninitialized, we
82 // create a new socket and attempt to exchange it into the atomic sock_. If the
83 // compare/exchange was successful, then that will be the socket used for the duration of the
84 // program, otherwise a different thread has already opened and written the socket to the atomic,
85 // so close the new socket and return.
GetSocket()86 void GetSocket() {
87 if (sock_ != kUninitialized) {
88 return;
89 }
90
91 int flags = SOCK_DGRAM | SOCK_CLOEXEC;
92 if (!blocking_) {
93 flags |= SOCK_NONBLOCK;
94 }
95 int new_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
96 if (new_socket < 0) {
97 return;
98 }
99
100 LogdConnect(new_socket);
101
102 int uninitialized_value = kUninitialized;
103 if (!sock_.compare_exchange_strong(uninitialized_value, new_socket)) {
104 close(new_socket);
105 return;
106 }
107 }
108
109 static const int kUninitialized = -1;
110 atomic_int sock_ = kUninitialized;
111 bool blocking_;
112 };
113
LogdClose()114 void LogdClose() {
115 LogdSocket::BlockingSocket().Close();
116 LogdSocket::NonBlockingSocket().Close();
117 }
118
LogdWrite(log_id_t logId,struct timespec * ts,struct iovec * vec,size_t nr)119 int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
120 ssize_t ret;
121 static const unsigned headerLength = 1;
122 struct iovec newVec[nr + headerLength];
123 android_log_header_t header;
124 size_t i, payloadSize;
125 static atomic_int dropped;
126
127 LogdSocket& logd_socket =
128 logId == LOG_ID_SECURITY ? LogdSocket::BlockingSocket() : LogdSocket::NonBlockingSocket();
129
130 if (logd_socket.sock() < 0) {
131 return -EBADF;
132 }
133
134 /* logd, after initialization and priv drop */
135 if (getuid() == AID_LOGD) {
136 /*
137 * ignore log messages we send to ourself (logd).
138 * Such log messages are often generated by libraries we depend on
139 * which use standard Android logging.
140 */
141 return 0;
142 }
143
144 header.tid = gettid();
145 header.realtime.tv_sec = ts->tv_sec;
146 header.realtime.tv_nsec = ts->tv_nsec;
147
148 newVec[0].iov_base = (unsigned char*)&header;
149 newVec[0].iov_len = sizeof(header);
150
151 int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
152 if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
153 ANDROID_LOG_VERBOSE)) {
154 android_log_event_int_t buffer;
155
156 header.id = LOG_ID_EVENTS;
157 buffer.header.tag = LIBLOG_LOG_TAG;
158 buffer.payload.type = EVENT_TYPE_INT;
159 buffer.payload.data = snapshot;
160
161 newVec[headerLength].iov_base = &buffer;
162 newVec[headerLength].iov_len = sizeof(buffer);
163
164 ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, 2));
165 if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
166 atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
167 }
168 }
169
170 header.id = logId;
171
172 for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
173 newVec[i].iov_base = vec[i - headerLength].iov_base;
174 payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
175
176 if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
177 newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
178 if (newVec[i].iov_len) {
179 ++i;
180 }
181 break;
182 }
183 }
184
185 // EAGAIN occurs if logd is overloaded, other errors indicate that something went wrong with
186 // the connection, so we reset it and try again.
187 ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, i));
188 if (ret < 0 && errno != EAGAIN) {
189 logd_socket.Reconnect();
190
191 ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, i));
192 }
193
194 if (ret < 0) {
195 ret = -errno;
196 }
197
198 if (ret > (ssize_t)sizeof(header)) {
199 ret -= sizeof(header);
200 } else if (ret < 0) {
201 atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
202 }
203
204 return ret;
205 }
206