/src/wireshark/wiretap/logcat_text.c
Line | Count | Source |
1 | | /* logcat_text.c |
2 | | * |
3 | | * Copyright 2014, Michal Orynicz for Tieto Corporation |
4 | | * Copyright 2014, Michal Labedzki for Tieto Corporation |
5 | | * |
6 | | * SPDX-License-Identifier: GPL-2.0-or-later |
7 | | */ |
8 | | |
9 | | #include "config.h" |
10 | | #include "logcat_text.h" |
11 | | |
12 | | #include <string.h> |
13 | | |
14 | | #include "wtap_module.h" |
15 | | #include "file_wrappers.h" |
16 | | |
17 | | #include "logcat.h" |
18 | | |
19 | | struct dumper_t { |
20 | | int type; |
21 | | }; |
22 | | |
23 | | static int logcat_text_brief_file_type_subtype = -1; |
24 | | static int logcat_text_process_file_type_subtype = -1; |
25 | | static int logcat_text_tag_file_type_subtype = -1; |
26 | | static int logcat_text_thread_file_type_subtype = -1; |
27 | | static int logcat_text_time_file_type_subtype = -1; |
28 | | static int logcat_text_threadtime_file_type_subtype = -1; |
29 | | static int logcat_text_long_file_type_subtype = -1; |
30 | | |
31 | | void register_logcat_text(void); |
32 | | |
33 | | /* Returns '?' for invalid priorities */ |
34 | 0 | static char get_priority(const uint8_t priority) { |
35 | 0 | static const char priorities[] = "??VDIWEFS"; |
36 | |
|
37 | 0 | if (priority >= (uint8_t) sizeof(priorities)) |
38 | 0 | return '?'; |
39 | | |
40 | 0 | return priorities[priority]; |
41 | 0 | } |
42 | | |
43 | | static int buffered_detect_version(const uint8_t *pd) |
44 | 0 | { |
45 | 0 | const struct logger_entry *log_entry; |
46 | 0 | const struct logger_entry_v2 *log_entry_v2; |
47 | 0 | int version; |
48 | 0 | const uint8_t *msg_payload = NULL; |
49 | 0 | uint8_t *msg_part; |
50 | 0 | uint8_t *msg_end; |
51 | 0 | uint16_t msg_len; |
52 | |
|
53 | 0 | log_entry = (const struct logger_entry *)(const void *) pd; |
54 | 0 | log_entry_v2 = (const struct logger_entry_v2 *)(const void *) pd; |
55 | | |
56 | | /* must contain at least priority and two nulls as separator */ |
57 | 0 | if (log_entry->len < 3) |
58 | 0 | return -1; |
59 | | |
60 | | /* payload length may not exceed the maximum payload size */ |
61 | 0 | if (log_entry->len > LOGGER_ENTRY_MAX_PAYLOAD) |
62 | 0 | return -1; |
63 | | |
64 | | /* cannot rely on __pad being 0 for v1, use heuristics to find out what |
65 | | * version is in use. First assume the smallest msg. */ |
66 | 0 | for (version = 1; version <= 2; ++version) { |
67 | 0 | if (version == 1) { |
68 | 0 | msg_payload = (const uint8_t *) (log_entry + 1); |
69 | 0 | } else if (version == 2) { |
70 | | /* v2 is 4 bytes longer */ |
71 | 0 | msg_payload = (const uint8_t *) (log_entry_v2 + 1); |
72 | 0 | if (log_entry_v2->hdr_size != sizeof(*log_entry_v2)) |
73 | 0 | continue; |
74 | 0 | } |
75 | | |
76 | | /* A v2 msg has a 32-bit userid instead of v1 priority */ |
77 | 0 | if (get_priority(msg_payload[0]) == '?') |
78 | 0 | continue; |
79 | | |
80 | | /* Is there a terminating '\0' for the tag? */ |
81 | 0 | msg_part = (uint8_t *) memchr(msg_payload, '\0', log_entry->len - 1); |
82 | 0 | if (msg_part == NULL) |
83 | 0 | continue; |
84 | | |
85 | | /* if msg is '\0'-terminated, is it equal to the payload len? */ |
86 | 0 | ++msg_part; |
87 | 0 | msg_len = (uint16_t)(log_entry->len - (msg_part - msg_payload)); |
88 | 0 | msg_end = (uint8_t *) memchr(msg_part, '\0', msg_len); |
89 | | /* is the end of the buffer (-1) equal to the end of msg? */ |
90 | 0 | if (msg_end && (msg_payload + log_entry->len - 1 != msg_end)) |
91 | 0 | continue; |
92 | | |
93 | 0 | return version; |
94 | 0 | } |
95 | | |
96 | 0 | return -1; |
97 | 0 | } |
98 | | |
99 | | static char *logcat_log(const struct dumper_t *dumper, uint32_t seconds, |
100 | | int milliseconds, int pid, int tid, char priority, const char *tag, |
101 | | const char *log) |
102 | 0 | { |
103 | 0 | char time_buffer[15]; |
104 | 0 | time_t datetime; |
105 | 0 | struct tm *tm; |
106 | |
|
107 | 0 | datetime = (time_t) seconds; |
108 | |
|
109 | 0 | switch (dumper->type) { |
110 | 0 | case WTAP_ENCAP_LOGCAT_BRIEF: |
111 | 0 | return ws_strdup_printf("%c/%-8s(%5i): %s\n", |
112 | 0 | priority, tag, pid, log); |
113 | 0 | case WTAP_ENCAP_LOGCAT_PROCESS: |
114 | | /* NOTE: Last parameter should be "process name", not tag; |
115 | | Unfortunately, we do not have process name */ |
116 | 0 | return ws_strdup_printf("%c(%5i) %s (%s)\n", |
117 | 0 | priority, pid, log, ""); |
118 | 0 | case WTAP_ENCAP_LOGCAT_TAG: |
119 | 0 | return ws_strdup_printf("%c/%-8s: %s\n", |
120 | 0 | priority, tag, log); |
121 | 0 | case WTAP_ENCAP_LOGCAT_THREAD: |
122 | 0 | return ws_strdup_printf("%c(%5i:%5i) %s\n", |
123 | 0 | priority, pid, tid, log); |
124 | 0 | case WTAP_ENCAP_LOGCAT_TIME: |
125 | 0 | tm = gmtime(&datetime); |
126 | 0 | if (tm != NULL) { |
127 | 0 | strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S", |
128 | 0 | tm); |
129 | 0 | return ws_strdup_printf("%s.%03i %c/%-8s(%5i): %s\n", |
130 | 0 | time_buffer, milliseconds, priority, tag, pid, log); |
131 | 0 | } else { |
132 | 0 | return ws_strdup_printf("Not representable %c/%-8s(%5i): %s\n", |
133 | 0 | priority, tag, pid, log); |
134 | 0 | } |
135 | 0 | case WTAP_ENCAP_LOGCAT_THREADTIME: |
136 | 0 | tm = gmtime(&datetime); |
137 | 0 | if (tm != NULL) { |
138 | 0 | strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S", |
139 | 0 | tm); |
140 | 0 | return ws_strdup_printf("%s.%03i %5i %5i %c %-8s: %s\n", |
141 | 0 | time_buffer, milliseconds, pid, tid, priority, tag, log); |
142 | 0 | } else { |
143 | 0 | return ws_strdup_printf("Not representable %5i %5i %c %-8s: %s\n", |
144 | 0 | pid, tid, priority, tag, log); |
145 | 0 | } |
146 | 0 | case WTAP_ENCAP_LOGCAT_LONG: |
147 | 0 | tm = gmtime(&datetime); |
148 | 0 | if (tm != NULL) { |
149 | 0 | strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S", |
150 | 0 | tm); |
151 | 0 | return ws_strdup_printf("[ %s.%03i %5i:%5i %c/%-8s ]\n%s\n\n", |
152 | 0 | time_buffer, milliseconds, pid, tid, priority, tag, log); |
153 | 0 | } else { |
154 | 0 | return ws_strdup_printf("[ Not representable %5i:%5i %c/%-8s ]\n%s\n\n", |
155 | 0 | pid, tid, priority, tag, log); |
156 | 0 | } |
157 | 0 | default: |
158 | 0 | return NULL; |
159 | 0 | } |
160 | |
|
161 | 0 | } |
162 | | |
163 | 0 | static void get_time(char *string, wtap_rec *rec) { |
164 | 0 | int ms; |
165 | 0 | struct tm date; |
166 | 0 | time_t seconds; |
167 | |
|
168 | 0 | if (6 == sscanf(string, "%d-%d %d:%d:%d.%d", &date.tm_mon, &date.tm_mday, &date.tm_hour, |
169 | 0 | &date.tm_min, &date.tm_sec, &ms)) { |
170 | 0 | date.tm_year = 70; |
171 | 0 | date.tm_mon -= 1; |
172 | 0 | date.tm_isdst = -1; |
173 | 0 | seconds = mktime(&date); |
174 | 0 | rec->ts.secs = seconds; |
175 | 0 | rec->ts.nsecs = (int) (ms * 1e6); |
176 | 0 | rec->presence_flags = WTAP_HAS_TS; |
177 | 0 | } else { |
178 | 0 | rec->presence_flags = 0; |
179 | 0 | rec->ts.secs = (time_t) 0; |
180 | 0 | rec->ts.nsecs = 0; |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | | static bool logcat_text_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, |
185 | 0 | int file_type) { |
186 | 0 | uint8_t *pd; |
187 | 0 | char *cbuff; |
188 | 0 | char *ret = NULL; |
189 | |
|
190 | 0 | cbuff = (char*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD); |
191 | 0 | do { |
192 | 0 | ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE_STANDARD, fh); |
193 | 0 | } while (NULL != ret && 3 > strlen(cbuff) && !file_eof(fh)); |
194 | |
|
195 | 0 | if (NULL == ret || 3 > strlen(cbuff)) { |
196 | 0 | g_free(cbuff); |
197 | 0 | return false; |
198 | 0 | } |
199 | | |
200 | 0 | if (logcat_text_long_file_type_subtype == file_type && |
201 | 0 | !g_regex_match_simple(SPECIAL_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW), G_REGEX_MATCH_NOTEMPTY)) { |
202 | 0 | int64_t file_off = 0; |
203 | 0 | char *lbuff; |
204 | 0 | int err; |
205 | 0 | char *ret2 = NULL; |
206 | |
|
207 | 0 | lbuff = (char*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD); |
208 | 0 | file_off = file_tell(fh); |
209 | 0 | ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE_STANDARD, fh); |
210 | 0 | while (NULL != ret2 && 2 < strlen(lbuff) && !file_eof(fh)) { |
211 | 0 | (void) g_strlcat(cbuff,lbuff,WTAP_MAX_PACKET_SIZE_STANDARD); |
212 | 0 | file_off = file_tell(fh); |
213 | 0 | ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE_STANDARD, fh); |
214 | 0 | } |
215 | |
|
216 | 0 | if(NULL == ret2 || 2 < strlen(lbuff)) { |
217 | 0 | g_free(cbuff); |
218 | 0 | g_free(lbuff); |
219 | 0 | return false; |
220 | 0 | } |
221 | | |
222 | 0 | if (file_seek(fh, file_off, SEEK_SET, &err) == -1) { |
223 | 0 | g_free(cbuff); |
224 | 0 | g_free(lbuff); |
225 | 0 | return false; |
226 | 0 | } |
227 | 0 | g_free(lbuff); |
228 | 0 | } |
229 | | |
230 | 0 | wtap_setup_packet_rec(rec, wth->file_encap); |
231 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
232 | 0 | rec->rec_header.packet_header.caplen = (uint32_t)strlen(cbuff); |
233 | 0 | rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen; |
234 | |
|
235 | 0 | ws_buffer_assure_space(&rec->data, rec->rec_header.packet_header.caplen + 1); |
236 | 0 | pd = ws_buffer_start_ptr(&rec->data); |
237 | 0 | if ((logcat_text_time_file_type_subtype == file_type |
238 | 0 | || logcat_text_threadtime_file_type_subtype == file_type |
239 | 0 | || logcat_text_long_file_type_subtype == file_type) |
240 | 0 | && '-' != cbuff[0]) { /* the last part filters out the -- beginning of... lines */ |
241 | 0 | if (logcat_text_long_file_type_subtype == file_type) { |
242 | 0 | get_time(cbuff+2, rec); |
243 | 0 | } else { |
244 | 0 | get_time(cbuff, rec); |
245 | 0 | } |
246 | 0 | } else { |
247 | 0 | rec->presence_flags = 0; |
248 | 0 | rec->ts.secs = (time_t) 0; |
249 | 0 | rec->ts.nsecs = 0; |
250 | 0 | } |
251 | 0 | memcpy(pd, cbuff, rec->rec_header.packet_header.caplen + 1); |
252 | 0 | g_free(cbuff); |
253 | 0 | return true; |
254 | 0 | } |
255 | | |
256 | | static bool logcat_text_read(wtap *wth, wtap_rec *rec, |
257 | 0 | int *err _U_ , char **err_info _U_, int64_t *data_offset) { |
258 | 0 | *data_offset = file_tell(wth->fh); |
259 | |
|
260 | 0 | return logcat_text_read_packet(wth, wth->fh, rec, wth->file_type_subtype); |
261 | 0 | } |
262 | | |
263 | | static bool logcat_text_seek_read(wtap *wth, int64_t seek_off, |
264 | 0 | wtap_rec *rec, int *err, char **err_info _U_) { |
265 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) |
266 | 0 | return false; |
267 | | |
268 | 0 | if (!logcat_text_read_packet(wth, wth->random_fh, rec, wth->file_type_subtype)) { |
269 | 0 | if (*err == 0) |
270 | 0 | *err = WTAP_ERR_SHORT_READ; |
271 | 0 | return false; |
272 | 0 | } |
273 | 0 | return true; |
274 | 0 | } |
275 | | |
276 | 0 | wtap_open_return_val logcat_text_open(wtap *wth, int *err, char **err_info _U_) { |
277 | 0 | char *cbuff; |
278 | 0 | char *ret = NULL; |
279 | |
|
280 | 0 | if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) |
281 | 0 | return WTAP_OPEN_ERROR; |
282 | | |
283 | 0 | cbuff = (char*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD); |
284 | 0 | do { |
285 | 0 | ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE_STANDARD, wth->fh); |
286 | 0 | } while (NULL != ret && !file_eof(wth->fh) |
287 | 0 | && ((3 > strlen(cbuff)) |
288 | 0 | || g_regex_match_simple(SPECIAL_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW), |
289 | 0 | G_REGEX_MATCH_NOTEMPTY))); |
290 | |
|
291 | 0 | if (g_regex_match_simple(BRIEF_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW), |
292 | 0 | G_REGEX_MATCH_NOTEMPTY)) { |
293 | 0 | wth->file_type_subtype = logcat_text_brief_file_type_subtype; |
294 | 0 | wth->file_encap = WTAP_ENCAP_LOGCAT_BRIEF; |
295 | 0 | } else if (g_regex_match_simple(TAG_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW), |
296 | 0 | G_REGEX_MATCH_NOTEMPTY)) { |
297 | 0 | wth->file_type_subtype = logcat_text_tag_file_type_subtype; |
298 | 0 | wth->file_encap = WTAP_ENCAP_LOGCAT_TAG; |
299 | 0 | } else if (g_regex_match_simple(PROCESS_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW), |
300 | 0 | G_REGEX_MATCH_NOTEMPTY)) { |
301 | 0 | wth->file_type_subtype = logcat_text_process_file_type_subtype; |
302 | 0 | wth->file_encap = WTAP_ENCAP_LOGCAT_PROCESS; |
303 | 0 | } else if (g_regex_match_simple(TIME_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW), |
304 | 0 | G_REGEX_MATCH_NOTEMPTY)) { |
305 | 0 | wth->file_type_subtype = logcat_text_time_file_type_subtype; |
306 | 0 | wth->file_encap = WTAP_ENCAP_LOGCAT_TIME; |
307 | 0 | } else if (g_regex_match_simple(THREAD_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW), |
308 | 0 | G_REGEX_MATCH_NOTEMPTY)) { |
309 | 0 | wth->file_type_subtype = logcat_text_thread_file_type_subtype; |
310 | 0 | wth->file_encap = WTAP_ENCAP_LOGCAT_THREAD; |
311 | 0 | } else if (g_regex_match_simple(THREADTIME_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW), |
312 | 0 | G_REGEX_MATCH_NOTEMPTY)) { |
313 | 0 | wth->file_type_subtype = logcat_text_threadtime_file_type_subtype; |
314 | 0 | wth->file_encap = WTAP_ENCAP_LOGCAT_THREADTIME; |
315 | 0 | } else if (g_regex_match_simple(LONG_STRING, cbuff, (GRegexCompileFlags)(G_REGEX_ANCHORED | G_REGEX_RAW), |
316 | 0 | G_REGEX_MATCH_NOTEMPTY)) { |
317 | 0 | wth->file_type_subtype = logcat_text_long_file_type_subtype; |
318 | 0 | wth->file_encap = WTAP_ENCAP_LOGCAT_LONG; |
319 | 0 | } else { |
320 | 0 | g_free(cbuff); |
321 | 0 | return WTAP_OPEN_NOT_MINE; |
322 | 0 | } |
323 | | |
324 | 0 | if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) { |
325 | 0 | g_free(cbuff); |
326 | 0 | return WTAP_OPEN_ERROR; |
327 | 0 | } |
328 | 0 | wth->snapshot_length = 0; |
329 | |
|
330 | 0 | wth->subtype_read = logcat_text_read; |
331 | 0 | wth->subtype_seek_read = logcat_text_seek_read; |
332 | 0 | wth->file_tsprec = WTAP_TSPREC_USEC; |
333 | 0 | g_free(cbuff); |
334 | 0 | return WTAP_OPEN_MINE; |
335 | 0 | } |
336 | | |
337 | 0 | static int logcat_text_brief_dump_can_write_encap(int encap) { |
338 | 0 | if (encap == WTAP_ENCAP_PER_PACKET) |
339 | 0 | return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
340 | | |
341 | 0 | switch (encap) { |
342 | 0 | case WTAP_ENCAP_LOGCAT: |
343 | 0 | case WTAP_ENCAP_LOGCAT_BRIEF: |
344 | 0 | case WTAP_ENCAP_WIRESHARK_UPPER_PDU: |
345 | 0 | return 0; |
346 | 0 | default: |
347 | 0 | return WTAP_ERR_UNWRITABLE_ENCAP; |
348 | 0 | } |
349 | 0 | } |
350 | | |
351 | 0 | static int logcat_text_process_dump_can_write_encap(int encap) { |
352 | 0 | if (encap == WTAP_ENCAP_PER_PACKET) |
353 | 0 | return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
354 | | |
355 | 0 | switch (encap) { |
356 | 0 | case WTAP_ENCAP_LOGCAT: |
357 | 0 | case WTAP_ENCAP_LOGCAT_PROCESS: |
358 | 0 | case WTAP_ENCAP_WIRESHARK_UPPER_PDU: |
359 | 0 | return 0; |
360 | 0 | default: |
361 | 0 | return WTAP_ERR_UNWRITABLE_ENCAP; |
362 | 0 | } |
363 | 0 | } |
364 | | |
365 | 0 | static int logcat_text_tag_dump_can_write_encap(int encap) { |
366 | 0 | if (encap == WTAP_ENCAP_PER_PACKET) |
367 | 0 | return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
368 | | |
369 | 0 | switch (encap) { |
370 | 0 | case WTAP_ENCAP_LOGCAT: |
371 | 0 | case WTAP_ENCAP_LOGCAT_TAG: |
372 | 0 | case WTAP_ENCAP_WIRESHARK_UPPER_PDU: |
373 | 0 | return 0; |
374 | 0 | default: |
375 | 0 | return WTAP_ERR_UNWRITABLE_ENCAP; |
376 | 0 | } |
377 | 0 | } |
378 | | |
379 | 0 | static int logcat_text_time_dump_can_write_encap(int encap) { |
380 | 0 | if (encap == WTAP_ENCAP_PER_PACKET) |
381 | 0 | return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
382 | | |
383 | 0 | switch (encap) { |
384 | 0 | case WTAP_ENCAP_LOGCAT: |
385 | 0 | case WTAP_ENCAP_LOGCAT_TIME: |
386 | 0 | case WTAP_ENCAP_WIRESHARK_UPPER_PDU: |
387 | 0 | return 0; |
388 | 0 | default: |
389 | 0 | return WTAP_ERR_UNWRITABLE_ENCAP; |
390 | 0 | } |
391 | 0 | } |
392 | | |
393 | 0 | static int logcat_text_thread_dump_can_write_encap(int encap) { |
394 | 0 | if (encap == WTAP_ENCAP_PER_PACKET) |
395 | 0 | return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
396 | | |
397 | 0 | switch (encap) { |
398 | 0 | case WTAP_ENCAP_LOGCAT: |
399 | 0 | case WTAP_ENCAP_LOGCAT_THREAD: |
400 | 0 | case WTAP_ENCAP_WIRESHARK_UPPER_PDU: |
401 | 0 | return 0; |
402 | 0 | default: |
403 | 0 | return WTAP_ERR_UNWRITABLE_ENCAP; |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | 0 | static int logcat_text_threadtime_dump_can_write_encap(int encap) { |
408 | 0 | if (encap == WTAP_ENCAP_PER_PACKET) |
409 | 0 | return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
410 | | |
411 | 0 | switch (encap) { |
412 | 0 | case WTAP_ENCAP_LOGCAT: |
413 | 0 | case WTAP_ENCAP_LOGCAT_THREADTIME: |
414 | 0 | case WTAP_ENCAP_WIRESHARK_UPPER_PDU: |
415 | 0 | return 0; |
416 | 0 | default: |
417 | 0 | return WTAP_ERR_UNWRITABLE_ENCAP; |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | 0 | static int logcat_text_long_dump_can_write_encap(int encap) { |
422 | 0 | if (encap == WTAP_ENCAP_PER_PACKET) |
423 | 0 | return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
424 | | |
425 | 0 | switch (encap) { |
426 | 0 | case WTAP_ENCAP_LOGCAT: |
427 | 0 | case WTAP_ENCAP_LOGCAT_LONG: |
428 | 0 | case WTAP_ENCAP_WIRESHARK_UPPER_PDU: |
429 | 0 | return 0; |
430 | 0 | default: |
431 | 0 | return WTAP_ERR_UNWRITABLE_ENCAP; |
432 | 0 | } |
433 | 0 | } |
434 | | |
435 | | static bool logcat_text_dump_text(wtap_dumper *wdh, const wtap_rec *rec, |
436 | | int *err, char **err_info) |
437 | 0 | { |
438 | 0 | char *buf; |
439 | 0 | int length; |
440 | 0 | char priority; |
441 | 0 | const struct logger_entry *log_entry; |
442 | 0 | const struct logger_entry_v2 *log_entry_v2; |
443 | 0 | int payload_length; |
444 | 0 | const char *tag; |
445 | 0 | int32_t pid; |
446 | 0 | int32_t tid; |
447 | 0 | int32_t seconds; |
448 | 0 | int32_t milliseconds; |
449 | 0 | const uint8_t *msg_payload = NULL; |
450 | 0 | const char *msg_begin; |
451 | 0 | int msg_pre_skip; |
452 | 0 | char *log; |
453 | 0 | char *log_part; |
454 | 0 | char *log_next; |
455 | 0 | int logcat_version; |
456 | 0 | const struct dumper_t *dumper = (const struct dumper_t *) wdh->priv; |
457 | | |
458 | | /* We can only write packet records. */ |
459 | 0 | if (rec->rec_type != REC_TYPE_PACKET) { |
460 | 0 | *err = WTAP_ERR_UNWRITABLE_REC_TYPE; |
461 | 0 | *err_info = wtap_unwritable_rec_type_err_string(rec); |
462 | 0 | return false; |
463 | 0 | } |
464 | | |
465 | | /* |
466 | | * Make sure this packet doesn't have a link-layer type that |
467 | | * differs from the one for the file. |
468 | | */ |
469 | 0 | if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) { |
470 | 0 | *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED; |
471 | 0 | return false; |
472 | 0 | } |
473 | | |
474 | 0 | const uint8_t *pd = ws_buffer_start_ptr(&rec->data); |
475 | |
|
476 | 0 | switch (wdh->file_encap) { |
477 | 0 | case WTAP_ENCAP_WIRESHARK_UPPER_PDU: |
478 | 0 | { |
479 | 0 | int skipped_length; |
480 | |
|
481 | 0 | skipped_length = logcat_exported_pdu_length(pd); |
482 | 0 | pd += skipped_length; |
483 | |
|
484 | 0 | if (!wtap_dump_file_write(wdh, (const char*) pd, rec->rec_header.packet_header.caplen - skipped_length, err)) { |
485 | 0 | return false; |
486 | 0 | } |
487 | 0 | } |
488 | 0 | break; |
489 | 0 | case WTAP_ENCAP_LOGCAT: |
490 | | /* Skip EXPORTED_PDU*/ |
491 | 0 | if (wdh->file_encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) { |
492 | 0 | int skipped_length; |
493 | |
|
494 | 0 | skipped_length = logcat_exported_pdu_length(pd); |
495 | 0 | pd += skipped_length; |
496 | |
|
497 | 0 | logcat_version = buffered_detect_version(pd); |
498 | 0 | } else { |
499 | 0 | const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header; |
500 | |
|
501 | 0 | logcat_version = pseudo_header->logcat.version; |
502 | 0 | } |
503 | |
|
504 | 0 | log_entry = (const struct logger_entry *)(const void *) pd; |
505 | 0 | log_entry_v2 = (const struct logger_entry_v2 *)(const void *) pd; |
506 | |
|
507 | 0 | payload_length = GINT32_FROM_LE(log_entry->len); |
508 | 0 | pid = GINT32_FROM_LE(log_entry->pid); |
509 | 0 | tid = GINT32_FROM_LE(log_entry->tid); |
510 | 0 | seconds = GINT32_FROM_LE(log_entry->sec); |
511 | 0 | milliseconds = GINT32_FROM_LE(log_entry->nsec) / 1000000; |
512 | | |
513 | | /* msg: <prio:1><tag:N>\0<msg:N>\0 with N >= 0, last \0 can be missing */ |
514 | 0 | if (logcat_version == 1) { |
515 | 0 | msg_payload = (const uint8_t *) (log_entry + 1); |
516 | |
|
517 | 0 | priority = get_priority(msg_payload[0]); |
518 | 0 | tag = (const char*)(msg_payload + 1); |
519 | 0 | msg_pre_skip = 1 + (int) strlen(tag) + 1; |
520 | 0 | msg_begin = (const char*)(msg_payload + msg_pre_skip); |
521 | 0 | } else if (logcat_version == 2) { |
522 | 0 | msg_payload = (const uint8_t *) (log_entry_v2 + 1); |
523 | |
|
524 | 0 | priority = get_priority(msg_payload[0]); |
525 | 0 | tag = (const char*)(msg_payload + 1); |
526 | 0 | msg_pre_skip = 1 + (int) strlen(tag) + 1; |
527 | 0 | msg_begin = (const char*)(msg_payload + msg_pre_skip); |
528 | 0 | } else { |
529 | 0 | *err = WTAP_ERR_UNWRITABLE_REC_DATA; |
530 | 0 | *err_info = ws_strdup_printf("logcat: version %d isn't supported", |
531 | 0 | logcat_version); |
532 | 0 | return false; |
533 | 0 | } |
534 | | |
535 | | /* copy the message part. If a nul byte was missing, it will be added. */ |
536 | 0 | log = g_strndup(msg_begin, payload_length - msg_pre_skip); |
537 | | |
538 | | /* long format: display one header followed by the whole message (which may |
539 | | * contain new lines). Other formats: include tag, etc. with each line */ |
540 | 0 | log_next = log; |
541 | 0 | do { |
542 | 0 | log_part = log_next; |
543 | 0 | if (dumper->type == WTAP_ENCAP_LOGCAT_LONG) { |
544 | | /* read until end, there is no next string */ |
545 | 0 | log_next = NULL; |
546 | 0 | } else { |
547 | | /* read until next newline */ |
548 | 0 | log_next = strchr(log_part, '\n'); |
549 | 0 | if (log_next != NULL) { |
550 | 0 | *log_next = '\0'; |
551 | 0 | ++log_next; |
552 | | /* ignore trailing newline */ |
553 | 0 | if (*log_next == '\0') { |
554 | 0 | log_next = NULL; |
555 | 0 | } |
556 | 0 | } |
557 | 0 | } |
558 | |
|
559 | 0 | buf = logcat_log(dumper, seconds, milliseconds, pid, tid, priority, tag, log_part); |
560 | 0 | if (!buf) { |
561 | 0 | g_free(log); |
562 | 0 | return false; |
563 | 0 | } |
564 | 0 | length = (uint32_t) strlen(buf); |
565 | |
|
566 | 0 | if (!wtap_dump_file_write(wdh, buf, length, err)) { |
567 | 0 | g_free(log); |
568 | 0 | return false; |
569 | 0 | } |
570 | 0 | } while (log_next != NULL ); |
571 | | |
572 | 0 | g_free(log); |
573 | |
|
574 | 0 | break; |
575 | 0 | case WTAP_ENCAP_LOGCAT_BRIEF: |
576 | 0 | case WTAP_ENCAP_LOGCAT_TAG: |
577 | 0 | case WTAP_ENCAP_LOGCAT_PROCESS: |
578 | 0 | case WTAP_ENCAP_LOGCAT_TIME: |
579 | 0 | case WTAP_ENCAP_LOGCAT_THREAD: |
580 | 0 | case WTAP_ENCAP_LOGCAT_THREADTIME: |
581 | 0 | case WTAP_ENCAP_LOGCAT_LONG: |
582 | 0 | if (dumper->type == wdh->file_encap) { |
583 | 0 | if (!wtap_dump_file_write(wdh, (const char*) pd, rec->rec_header.packet_header.caplen, err)) { |
584 | 0 | return false; |
585 | 0 | } |
586 | 0 | } else { |
587 | 0 | *err = WTAP_ERR_UNWRITABLE_FILE_TYPE; |
588 | 0 | return false; |
589 | 0 | } |
590 | 0 | } |
591 | | |
592 | 0 | return true; |
593 | 0 | } |
594 | | |
595 | 0 | static bool logcat_text_dump_open(wtap_dumper *wdh, unsigned dump_type) { |
596 | 0 | struct dumper_t *dumper; |
597 | |
|
598 | 0 | dumper = g_new(struct dumper_t, 1); |
599 | 0 | dumper->type = dump_type; |
600 | |
|
601 | 0 | wdh->priv = dumper; |
602 | 0 | wdh->subtype_write = logcat_text_dump_text; |
603 | |
|
604 | 0 | return true; |
605 | 0 | } |
606 | | |
607 | 0 | static bool logcat_text_brief_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_) { |
608 | 0 | return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_BRIEF); |
609 | 0 | } |
610 | | |
611 | 0 | static bool logcat_text_process_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_) { |
612 | 0 | return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_PROCESS); |
613 | 0 | } |
614 | | |
615 | 0 | static bool logcat_text_tag_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_) { |
616 | 0 | return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TAG); |
617 | 0 | } |
618 | | |
619 | 0 | static bool logcat_text_time_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_) { |
620 | 0 | return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TIME); |
621 | 0 | } |
622 | | |
623 | 0 | static bool logcat_text_thread_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_) { |
624 | 0 | return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREAD); |
625 | 0 | } |
626 | | |
627 | 0 | static bool logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_) { |
628 | 0 | return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREADTIME); |
629 | 0 | } |
630 | | |
631 | 0 | static bool logcat_text_long_dump_open(wtap_dumper *wdh, int *err _U_, char **err_info _U_) { |
632 | 0 | return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_LONG); |
633 | 0 | } |
634 | | |
635 | | static const struct supported_block_type logcat_text_brief_blocks_supported[] = { |
636 | | /* |
637 | | * We support packet blocks, with no comments or other options. |
638 | | */ |
639 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
640 | | }; |
641 | | |
642 | | static const struct file_type_subtype_info logcat_text_brief_info = { |
643 | | "Android Logcat Brief text format", "logcat-brief", NULL, NULL, |
644 | | false, BLOCKS_SUPPORTED(logcat_text_brief_blocks_supported), |
645 | | logcat_text_brief_dump_can_write_encap, logcat_text_brief_dump_open, NULL |
646 | | }; |
647 | | |
648 | | static const struct supported_block_type logcat_text_process_blocks_supported[] = { |
649 | | /* |
650 | | * We support packet blocks, with no comments or other options. |
651 | | */ |
652 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
653 | | }; |
654 | | |
655 | | static const struct file_type_subtype_info logcat_text_process_info = { |
656 | | "Android Logcat Process text format", "logcat-process", NULL, NULL, |
657 | | false, BLOCKS_SUPPORTED(logcat_text_process_blocks_supported), |
658 | | logcat_text_process_dump_can_write_encap, logcat_text_process_dump_open, NULL |
659 | | }; |
660 | | |
661 | | static const struct supported_block_type logcat_text_tag_blocks_supported[] = { |
662 | | /* |
663 | | * We support packet blocks, with no comments or other options. |
664 | | */ |
665 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
666 | | }; |
667 | | |
668 | | static const struct file_type_subtype_info logcat_text_tag_info = { |
669 | | "Android Logcat Tag text format", "logcat-tag", NULL, NULL, |
670 | | false, BLOCKS_SUPPORTED(logcat_text_tag_blocks_supported), |
671 | | logcat_text_tag_dump_can_write_encap, logcat_text_tag_dump_open, NULL |
672 | | }; |
673 | | |
674 | | static const struct supported_block_type logcat_text_thread_blocks_supported[] = { |
675 | | /* |
676 | | * We support packet blocks, with no comments or other options. |
677 | | */ |
678 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
679 | | }; |
680 | | |
681 | | static const struct file_type_subtype_info logcat_text_thread_info = { |
682 | | "Android Logcat Thread text format", "logcat-thread", NULL, NULL, |
683 | | false, BLOCKS_SUPPORTED(logcat_text_thread_blocks_supported), |
684 | | logcat_text_thread_dump_can_write_encap, logcat_text_thread_dump_open, NULL |
685 | | }; |
686 | | |
687 | | static const struct supported_block_type logcat_text_time_blocks_supported[] = { |
688 | | /* |
689 | | * We support packet blocks, with no comments or other options. |
690 | | */ |
691 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
692 | | }; |
693 | | |
694 | | static const struct file_type_subtype_info logcat_text_time_info = { |
695 | | "Android Logcat Time text format", "logcat-time", NULL, NULL, |
696 | | false, BLOCKS_SUPPORTED(logcat_text_time_blocks_supported), |
697 | | logcat_text_time_dump_can_write_encap, logcat_text_time_dump_open, NULL |
698 | | }; |
699 | | |
700 | | static const struct supported_block_type logcat_text_threadtime_blocks_supported[] = { |
701 | | /* |
702 | | * We support packet blocks, with no comments or other options. |
703 | | */ |
704 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
705 | | }; |
706 | | |
707 | | static const struct file_type_subtype_info logcat_text_threadtime_info = { |
708 | | "Android Logcat Threadtime text format", "logcat-threadtime", NULL, NULL, |
709 | | false, BLOCKS_SUPPORTED(logcat_text_threadtime_blocks_supported), |
710 | | logcat_text_threadtime_dump_can_write_encap, logcat_text_threadtime_dump_open, NULL |
711 | | }; |
712 | | |
713 | | static const struct supported_block_type logcat_text_long_blocks_supported[] = { |
714 | | /* |
715 | | * We support packet blocks, with no comments or other options. |
716 | | */ |
717 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
718 | | }; |
719 | | |
720 | | static const struct file_type_subtype_info logcat_text_long_info = { |
721 | | "Android Logcat Long text format", "logcat-long", NULL, NULL, |
722 | | false, BLOCKS_SUPPORTED(logcat_text_long_blocks_supported), |
723 | | logcat_text_long_dump_can_write_encap, logcat_text_long_dump_open, NULL |
724 | | }; |
725 | | |
726 | | void register_logcat_text(void) |
727 | 14 | { |
728 | 14 | logcat_text_brief_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_brief_info); |
729 | 14 | logcat_text_process_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_process_info); |
730 | 14 | logcat_text_tag_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_tag_info); |
731 | 14 | logcat_text_thread_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_thread_info); |
732 | 14 | logcat_text_time_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_time_info); |
733 | 14 | logcat_text_threadtime_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_threadtime_info); |
734 | 14 | logcat_text_long_file_type_subtype = wtap_register_file_type_subtype(&logcat_text_long_info); |
735 | | |
736 | | /* |
737 | | * Register names for backwards compatibility with the |
738 | | * wtap_filetypes table in Lua. |
739 | | */ |
740 | 14 | wtap_register_backwards_compatibility_lua_name("LOGCAT_BRIEF", |
741 | 14 | logcat_text_brief_file_type_subtype); |
742 | 14 | wtap_register_backwards_compatibility_lua_name("LOGCAT_PROCESS", |
743 | 14 | logcat_text_process_file_type_subtype); |
744 | 14 | wtap_register_backwards_compatibility_lua_name("LOGCAT_TAG", |
745 | 14 | logcat_text_tag_file_type_subtype); |
746 | 14 | wtap_register_backwards_compatibility_lua_name("LOGCAT_THREAD", |
747 | 14 | logcat_text_thread_file_type_subtype); |
748 | 14 | wtap_register_backwards_compatibility_lua_name("LOGCAT_TIME", |
749 | 14 | logcat_text_time_file_type_subtype); |
750 | 14 | wtap_register_backwards_compatibility_lua_name("LOGCAT_THREADTIME", |
751 | 14 | logcat_text_threadtime_file_type_subtype); |
752 | 14 | wtap_register_backwards_compatibility_lua_name("LOGCAT_LONG", |
753 | 14 | logcat_text_long_file_type_subtype); |
754 | 14 | } |
755 | | |
756 | | /* |
757 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
758 | | * |
759 | | * Local variables: |
760 | | * c-basic-offset: 4 |
761 | | * tab-width: 8 |
762 | | * indent-tabs-mode: nil |
763 | | * End: |
764 | | * |
765 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
766 | | * :indentSize=4:tabSize=8:noTabs=true: |
767 | | */ |