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 "pmsg_writer.h"
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/types.h>
24 #include <time.h>
25 
26 #include <log/log_properties.h>
27 #include <private/android_logger.h>
28 
29 #include "logger.h"
30 #include "uio.h"
31 
32 static atomic_int pmsg_fd;
33 
GetPmsgFd()34 static void GetPmsgFd() {
35   // Note if open() fails and returns -1, that value is stored into pmsg_fd as an indication that
36   // pmsg is not available and open() should not be retried.
37   if (pmsg_fd != 0) {
38     return;
39   }
40 
41   int new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
42 
43   // Unlikely that new_fd is 0, but that is synonymous with our uninitialized value, and we'd prefer
44   // STDIN_FILENO != stdin, so we call open() to get a new fd value in this case.
45   if (new_fd == 0) {
46     new_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
47     close(0);
48   }
49 
50   // pmsg_fd should only be opened once.  If we see that pmsg_fd is uninitialized, we open
51   // "/dev/pmsg0" then attempt to compare/exchange it into pmsg_fd.  If the compare/exchange was
52   // successful, then that will be the fd used for the duration of the program, otherwise a
53   // different thread has already opened and written the fd to the atomic, so close the new fd and
54   // return.
55   int uninitialized_value = 0;
56   if (!pmsg_fd.compare_exchange_strong(uninitialized_value, new_fd)) {
57     if (new_fd != -1) {
58       close(new_fd);
59     }
60   }
61 }
62 
PmsgClose()63 void PmsgClose() {
64   if (pmsg_fd > 0) {
65     close(pmsg_fd);
66   }
67   pmsg_fd = 0;
68 }
69 
PmsgWrite(log_id_t logId,struct timespec * ts,struct iovec * vec,size_t nr)70 int PmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
71   static const unsigned headerLength = 2;
72   struct iovec newVec[nr + headerLength];
73   android_log_header_t header;
74   android_pmsg_log_header_t pmsgHeader;
75   size_t i, payloadSize;
76   ssize_t ret;
77 
78   if (!__android_log_is_debuggable()) {
79     if (logId != LOG_ID_EVENTS && logId != LOG_ID_SECURITY) {
80       return -1;
81     }
82 
83     if (logId == LOG_ID_EVENTS) {
84       if (vec[0].iov_len < 4) {
85         return -EINVAL;
86       }
87 
88       if (SNET_EVENT_LOG_TAG != *static_cast<uint32_t*>(vec[0].iov_base)) {
89         return -EPERM;
90       }
91     }
92   }
93 
94   GetPmsgFd();
95 
96   if (pmsg_fd <= 0) {
97     return -EBADF;
98   }
99 
100   /*
101    *  struct {
102    *      // what we provide to pstore
103    *      android_pmsg_log_header_t pmsgHeader;
104    *      // what we provide to file
105    *      android_log_header_t header;
106    *      // caller provides
107    *      union {
108    *          struct {
109    *              char     prio;
110    *              char     payload[];
111    *          } string;
112    *          struct {
113    *              uint32_t tag
114    *              char     payload[];
115    *          } binary;
116    *      };
117    *  };
118    */
119 
120   pmsgHeader.magic = LOGGER_MAGIC;
121   pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header);
122   pmsgHeader.uid = getuid();
123   pmsgHeader.pid = getpid();
124 
125   header.id = logId;
126   header.tid = gettid();
127   header.realtime.tv_sec = ts->tv_sec;
128   header.realtime.tv_nsec = ts->tv_nsec;
129 
130   newVec[0].iov_base = (unsigned char*)&pmsgHeader;
131   newVec[0].iov_len = sizeof(pmsgHeader);
132   newVec[1].iov_base = (unsigned char*)&header;
133   newVec[1].iov_len = sizeof(header);
134 
135   for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
136     newVec[i].iov_base = vec[i - headerLength].iov_base;
137     payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
138 
139     if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
140       newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
141       if (newVec[i].iov_len) {
142         ++i;
143       }
144       payloadSize = LOGGER_ENTRY_MAX_PAYLOAD;
145       break;
146     }
147   }
148   pmsgHeader.len += payloadSize;
149 
150   ret = TEMP_FAILURE_RETRY(writev(pmsg_fd, newVec, i));
151   if (ret < 0) {
152     ret = errno ? -errno : -ENOTCONN;
153   }
154 
155   if (ret > (ssize_t)(sizeof(header) + sizeof(pmsgHeader))) {
156     ret -= sizeof(header) - sizeof(pmsgHeader);
157   }
158 
159   return ret;
160 }
161 
162 /*
163  * Virtual pmsg filesystem
164  *
165  * Payload will comprise the string "<basedir>:<basefile>\0<content>" to a
166  * maximum of LOGGER_ENTRY_MAX_PAYLOAD, but scaled to the last newline in the
167  * file.
168  *
169  * Will hijack the header.realtime.tv_nsec field for a sequence number in usec.
170  */
171 
strnrchr(const char * buf,size_t len,char c)172 static inline const char* strnrchr(const char* buf, size_t len, char c) {
173   const char* cp = buf + len;
174   while ((--cp > buf) && (*cp != c))
175     ;
176   if (cp <= buf) {
177     return buf + len;
178   }
179   return cp;
180 }
181 
182 /* Write a buffer as filename references (tag = <basedir>:<basename>) */
__android_log_pmsg_file_write(log_id_t logId,char prio,const char * filename,const char * buf,size_t len)183 ssize_t __android_log_pmsg_file_write(log_id_t logId, char prio, const char* filename,
184                                       const char* buf, size_t len) {
185   size_t length, packet_len;
186   const char* tag;
187   char *cp, *slash;
188   struct timespec ts;
189   struct iovec vec[3];
190 
191   /* Make sure the logId value is not a bad idea */
192   if ((logId == LOG_ID_KERNEL) ||   /* Verbotten */
193       (logId == LOG_ID_EVENTS) ||   /* Do not support binary content */
194       (logId == LOG_ID_SECURITY) || /* Bad idea to allow */
195       ((unsigned)logId >= 32)) {    /* fit within logMask on arch32 */
196     return -EINVAL;
197   }
198 
199   clock_gettime(CLOCK_REALTIME, &ts);
200 
201   cp = strdup(filename);
202   if (!cp) {
203     return -ENOMEM;
204   }
205 
206   tag = cp;
207   slash = strrchr(cp, '/');
208   if (slash) {
209     *slash = ':';
210     slash = strrchr(cp, '/');
211     if (slash) {
212       tag = slash + 1;
213     }
214   }
215 
216   length = strlen(tag) + 1;
217   packet_len = LOGGER_ENTRY_MAX_PAYLOAD - sizeof(char) - length;
218 
219   vec[0].iov_base = &prio;
220   vec[0].iov_len = sizeof(char);
221   vec[1].iov_base = (unsigned char*)tag;
222   vec[1].iov_len = length;
223 
224   for (ts.tv_nsec = 0, length = len; length; ts.tv_nsec += ANDROID_LOG_PMSG_FILE_SEQUENCE) {
225     ssize_t ret;
226     size_t transfer;
227 
228     if ((ts.tv_nsec / ANDROID_LOG_PMSG_FILE_SEQUENCE) >= ANDROID_LOG_PMSG_FILE_MAX_SEQUENCE) {
229       len -= length;
230       break;
231     }
232 
233     transfer = length;
234     if (transfer > packet_len) {
235       transfer = strnrchr(buf, packet_len - 1, '\n') - buf;
236       if ((transfer < length) && (buf[transfer] == '\n')) {
237         ++transfer;
238       }
239     }
240 
241     vec[2].iov_base = (unsigned char*)buf;
242     vec[2].iov_len = transfer;
243 
244     ret = PmsgWrite(logId, &ts, vec, sizeof(vec) / sizeof(vec[0]));
245 
246     if (ret <= 0) {
247       free(cp);
248       return ret ? ret : (len - length);
249     }
250     length -= transfer;
251     buf += transfer;
252   }
253   free(cp);
254   return len;
255 }
256