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