3 * Copyright 2014, Michal Orynicz for Tieto Corporation
4 * Copyright 2014, Michal Labedzki for Tieto Corporation
6 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include "file_wrappers.h"
16 #include "logcat_text.h"
23 /* Returns '?' for invalid priorities */
24 static gchar get_priority(const guint8 priority) {
25 static gchar priorities[] = "??VDIWEFS";
27 if (priority >= (guint8) sizeof(priorities))
30 return priorities[priority];
33 static gint buffered_detect_version(const guint8 *pd)
35 const struct logger_entry *log_entry;
36 const struct logger_entry_v2 *log_entry_v2;
38 const guint8 *msg_payload = NULL;
43 log_entry = (const struct logger_entry *)(const void *) pd;
44 log_entry_v2 = (const struct logger_entry_v2 *)(const void *) pd;
46 /* must contain at least priority and two nulls as separator */
47 if (log_entry->len < 3)
50 /* payload length may not exceed the maximum payload size */
51 if (log_entry->len > LOGGER_ENTRY_MAX_PAYLOAD)
54 /* cannot rely on __pad being 0 for v1, use heuristics to find out what
55 * version is in use. First assume the smallest msg. */
56 for (version = 1; version <= 2; ++version) {
58 msg_payload = (const guint8 *) (log_entry + 1);
59 } else if (version == 2) {
60 /* v2 is 4 bytes longer */
61 msg_payload = (const guint8 *) (log_entry_v2 + 1);
62 if (log_entry_v2->hdr_size != sizeof(*log_entry_v2))
66 /* A v2 msg has a 32-bit userid instead of v1 priority */
67 if (get_priority(msg_payload[0]) == '?')
70 /* Is there a terminating '\0' for the tag? */
71 msg_part = (guint8 *) memchr(msg_payload, '\0', log_entry->len - 1);
75 /* if msg is '\0'-terminated, is it equal to the payload len? */
77 msg_len = (guint16)(log_entry->len - (msg_part - msg_payload));
78 msg_end = (guint8 *) memchr(msg_part, '\0', msg_len);
79 /* is the end of the buffer (-1) equal to the end of msg? */
80 if (msg_end && (msg_payload + log_entry->len - 1 != msg_end))
89 static gchar *logcat_log(const struct dumper_t *dumper, guint32 seconds,
90 gint milliseconds, gint pid, gint tid, gchar priority, const gchar *tag,
93 gchar time_buffer[15];
97 datetime = (time_t) seconds;
99 switch (dumper->type) {
100 case WTAP_ENCAP_LOGCAT_BRIEF:
101 return g_strdup_printf("%c/%-8s(%5i): %s\n",
102 priority, tag, pid, log);
103 case WTAP_ENCAP_LOGCAT_PROCESS:
104 /* NOTE: Last parameter should be "process name", not tag;
105 Unfortunately, we do not have process name */
106 return g_strdup_printf("%c(%5i) %s (%s)\n",
107 priority, pid, log, "");
108 case WTAP_ENCAP_LOGCAT_TAG:
109 return g_strdup_printf("%c/%-8s: %s\n",
111 case WTAP_ENCAP_LOGCAT_THREAD:
112 return g_strdup_printf("%c(%5i:%5i) %s\n",
113 priority, pid, tid, log);
114 case WTAP_ENCAP_LOGCAT_TIME:
115 tm = gmtime(&datetime);
117 strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
119 return g_strdup_printf("%s.%03i %c/%-8s(%5i): %s\n",
120 time_buffer, milliseconds, priority, tag, pid, log);
122 return g_strdup_printf("Not representable %c/%-8s(%5i): %s\n",
123 priority, tag, pid, log);
125 case WTAP_ENCAP_LOGCAT_THREADTIME:
126 tm = gmtime(&datetime);
128 strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
130 return g_strdup_printf("%s.%03i %5i %5i %c %-8s: %s\n",
131 time_buffer, milliseconds, pid, tid, priority, tag, log);
133 return g_strdup_printf("Not representable %5i %5i %c %-8s: %s\n",
134 pid, tid, priority, tag, log);
136 case WTAP_ENCAP_LOGCAT_LONG:
137 tm = gmtime(&datetime);
139 strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
141 return g_strdup_printf("[ %s.%03i %5i:%5i %c/%-8s ]\n%s\n\n",
142 time_buffer, milliseconds, pid, tid, priority, tag, log);
144 return g_strdup_printf("[ Not representable %5i:%5i %c/%-8s ]\n%s\n\n",
145 pid, tid, priority, tag, log);
153 static void get_time(gchar *string, wtap_rec *rec) {
158 if (6 == sscanf(string, "%d-%d %d:%d:%d.%d", &date.tm_mon, &date.tm_mday, &date.tm_hour,
159 &date.tm_min, &date.tm_sec, &ms)) {
163 seconds = mktime(&date);
164 rec->ts.secs = seconds;
165 rec->ts.nsecs = (int) (ms * 1e6);
166 rec->presence_flags = WTAP_HAS_TS;
168 rec->presence_flags = 0;
169 rec->ts.secs = (time_t) 0;
174 static gboolean logcat_text_read_packet(FILE_T fh, wtap_rec *rec,
175 Buffer *buf, gint file_type) {
180 cbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
182 ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE_STANDARD, fh);
183 } while (NULL != ret && 3 > strlen(cbuff) && !file_eof(fh));
185 if (NULL == ret || 3 > strlen(cbuff)) {
190 if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type &&
191 !g_regex_match_simple(SPECIAL_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW), G_REGEX_MATCH_NOTEMPTY)) {
197 lbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
198 file_off = file_tell(fh);
199 ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE_STANDARD, fh);
200 while (NULL != ret2 && 2 < strlen(lbuff) && !file_eof(fh)) {
201 g_strlcat(cbuff,lbuff,WTAP_MAX_PACKET_SIZE_STANDARD);
202 file_off = file_tell(fh);
203 ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE_STANDARD, fh);
206 if(NULL == ret2 || 2 < strlen(lbuff)) {
212 file_seek(fh,file_off,SEEK_SET,&err);
216 rec->rec_type = REC_TYPE_PACKET;
217 rec->rec_header.packet_header.caplen = (guint32)strlen(cbuff);
218 rec->rec_header.packet_header.len = rec->rec_header.packet_header.caplen;
220 ws_buffer_assure_space(buf, rec->rec_header.packet_header.caplen + 1);
221 pd = ws_buffer_start_ptr(buf);
222 if ((WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME == file_type
223 || WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME == file_type
224 || WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type)
225 && '-' != cbuff[0]) { /* the last part filters out the -- beginning of... lines */
226 if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type) {
227 get_time(cbuff+2, rec);
229 get_time(cbuff, rec);
232 rec->presence_flags = 0;
233 rec->ts.secs = (time_t) 0;
236 memcpy(pd, cbuff, rec->rec_header.packet_header.caplen + 1);
241 static gboolean logcat_text_read(wtap *wth, int *err _U_ , gchar **err_info _U_,
242 gint64 *data_offset) {
243 *data_offset = file_tell(wth->fh);
245 return logcat_text_read_packet(wth->fh, &wth->rec, wth->rec_data,
246 wth->file_type_subtype);
249 static gboolean logcat_text_seek_read(wtap *wth, gint64 seek_off,
250 wtap_rec *rec, Buffer *buf, int *err, gchar **err_info _U_) {
251 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
254 if (!logcat_text_read_packet(wth->random_fh, rec, buf,
255 wth->file_type_subtype)) {
257 *err = WTAP_ERR_SHORT_READ;
263 wtap_open_return_val logcat_text_open(wtap *wth, int *err, gchar **err_info _U_) {
267 if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
268 return WTAP_OPEN_ERROR;
270 cbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
272 ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE_STANDARD, wth->fh);
273 } while (NULL != ret && !file_eof(wth->fh)
274 && ((3 > strlen(cbuff))
275 || g_regex_match_simple(SPECIAL_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
276 G_REGEX_MATCH_NOTEMPTY)));
278 if (g_regex_match_simple(BRIEF_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
279 G_REGEX_MATCH_NOTEMPTY)) {
280 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_BRIEF;
281 wth->file_encap = WTAP_ENCAP_LOGCAT_BRIEF;
282 } else if (g_regex_match_simple(TAG_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
283 G_REGEX_MATCH_NOTEMPTY)) {
284 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TAG;
285 wth->file_encap = WTAP_ENCAP_LOGCAT_TAG;
286 } else if (g_regex_match_simple(PROCESS_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
287 G_REGEX_MATCH_NOTEMPTY)) {
288 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_PROCESS;
289 wth->file_encap = WTAP_ENCAP_LOGCAT_PROCESS;
290 } else if (g_regex_match_simple(TIME_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
291 G_REGEX_MATCH_NOTEMPTY)) {
292 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME;
293 wth->file_encap = WTAP_ENCAP_LOGCAT_TIME;
294 } else if (g_regex_match_simple(THREAD_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
295 G_REGEX_MATCH_NOTEMPTY)) {
296 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREAD;
297 wth->file_encap = WTAP_ENCAP_LOGCAT_THREAD;
298 } else if (g_regex_match_simple(THREADTIME_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
299 G_REGEX_MATCH_NOTEMPTY)) {
300 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME;
301 wth->file_encap = WTAP_ENCAP_LOGCAT_THREADTIME;
302 } else if (g_regex_match_simple(LONG_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
303 G_REGEX_MATCH_NOTEMPTY)) {
304 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG;
305 wth->file_encap = WTAP_ENCAP_LOGCAT_LONG;
308 return WTAP_OPEN_NOT_MINE;
311 if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
313 return WTAP_OPEN_ERROR;
315 wth->snapshot_length = 0;
317 wth->subtype_read = logcat_text_read;
318 wth->subtype_seek_read = logcat_text_seek_read;
319 wth->file_tsprec = WTAP_TSPREC_USEC;
321 return WTAP_OPEN_MINE;
324 int logcat_text_brief_dump_can_write_encap(int encap) {
325 if (encap == WTAP_ENCAP_PER_PACKET)
326 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
329 case WTAP_ENCAP_LOGCAT:
330 case WTAP_ENCAP_LOGCAT_BRIEF:
331 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
334 return WTAP_ERR_UNWRITABLE_ENCAP;
338 int logcat_text_process_dump_can_write_encap(int encap) {
339 if (encap == WTAP_ENCAP_PER_PACKET)
340 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
343 case WTAP_ENCAP_LOGCAT:
344 case WTAP_ENCAP_LOGCAT_PROCESS:
345 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
348 return WTAP_ERR_UNWRITABLE_ENCAP;
352 int logcat_text_tag_dump_can_write_encap(int encap) {
353 if (encap == WTAP_ENCAP_PER_PACKET)
354 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
357 case WTAP_ENCAP_LOGCAT:
358 case WTAP_ENCAP_LOGCAT_TAG:
359 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
362 return WTAP_ERR_UNWRITABLE_ENCAP;
366 int logcat_text_time_dump_can_write_encap(int encap) {
367 if (encap == WTAP_ENCAP_PER_PACKET)
368 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
371 case WTAP_ENCAP_LOGCAT:
372 case WTAP_ENCAP_LOGCAT_TIME:
373 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
376 return WTAP_ERR_UNWRITABLE_ENCAP;
380 int logcat_text_thread_dump_can_write_encap(int encap) {
381 if (encap == WTAP_ENCAP_PER_PACKET)
382 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
385 case WTAP_ENCAP_LOGCAT:
386 case WTAP_ENCAP_LOGCAT_THREAD:
387 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
390 return WTAP_ERR_UNWRITABLE_ENCAP;
394 int logcat_text_threadtime_dump_can_write_encap(int encap) {
395 if (encap == WTAP_ENCAP_PER_PACKET)
396 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
399 case WTAP_ENCAP_LOGCAT:
400 case WTAP_ENCAP_LOGCAT_THREADTIME:
401 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
404 return WTAP_ERR_UNWRITABLE_ENCAP;
408 int logcat_text_long_dump_can_write_encap(int encap) {
409 if (encap == WTAP_ENCAP_PER_PACKET)
410 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
413 case WTAP_ENCAP_LOGCAT:
414 case WTAP_ENCAP_LOGCAT_LONG:
415 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
418 return WTAP_ERR_UNWRITABLE_ENCAP;
422 static gboolean logcat_text_dump_text(wtap_dumper *wdh,
424 const guint8 *pd, int *err, gchar **err_info)
429 const struct logger_entry *log_entry;
430 const struct logger_entry_v2 *log_entry_v2;
437 const guint8 *msg_payload = NULL;
438 const gchar *msg_begin;
444 const struct dumper_t *dumper = (const struct dumper_t *) wdh->priv;
446 /* We can only write packet records. */
447 if (rec->rec_type != REC_TYPE_PACKET) {
448 *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
453 * Make sure this packet doesn't have a link-layer type that
454 * differs from the one for the file.
456 if (wdh->encap != rec->rec_header.packet_header.pkt_encap) {
457 *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
461 switch (wdh->encap) {
462 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
466 skipped_length = logcat_exported_pdu_length(pd);
467 pd += skipped_length;
469 if (!wtap_dump_file_write(wdh, (const gchar*) pd, rec->rec_header.packet_header.caplen - skipped_length, err)) {
474 case WTAP_ENCAP_LOGCAT:
475 /* Skip EXPORTED_PDU*/
476 if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
479 skipped_length = logcat_exported_pdu_length(pd);
480 pd += skipped_length;
482 logcat_version = buffered_detect_version(pd);
484 const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
486 logcat_version = pseudo_header->logcat.version;
489 log_entry = (const struct logger_entry *)(const void *) pd;
490 log_entry_v2 = (const struct logger_entry_v2 *)(const void *) pd;
492 payload_length = GINT32_FROM_LE(log_entry->len);
493 pid = GINT32_FROM_LE(log_entry->pid);
494 tid = GINT32_FROM_LE(log_entry->tid);
495 seconds = GINT32_FROM_LE(log_entry->sec);
496 milliseconds = GINT32_FROM_LE(log_entry->nsec) / 1000000;
498 /* msg: <prio:1><tag:N>\0<msg:N>\0 with N >= 0, last \0 can be missing */
499 if (logcat_version == 1) {
500 msg_payload = (const guint8 *) (log_entry + 1);
502 priority = get_priority(msg_payload[0]);
503 tag = msg_payload + 1;
504 msg_pre_skip = 1 + (gint) strlen(tag) + 1;
505 msg_begin = msg_payload + msg_pre_skip;
506 } else if (logcat_version == 2) {
507 msg_payload = (const guint8 *) (log_entry_v2 + 1);
509 priority = get_priority(msg_payload[0]);
510 tag = msg_payload + 1;
511 msg_pre_skip = 1 + (gint) strlen(tag) + 1;
512 msg_begin = msg_payload + msg_pre_skip;
514 *err = WTAP_ERR_UNWRITABLE_REC_DATA;
515 *err_info = g_strdup_printf("logcat: version %d isn't supported",
520 /* copy the message part. If a nul byte was missing, it will be added. */
521 log = g_strndup(msg_begin, payload_length - msg_pre_skip);
523 /* long format: display one header followed by the whole message (which may
524 * contain new lines). Other formats: include tag, etc. with each line */
528 if (dumper->type == WTAP_ENCAP_LOGCAT_LONG) {
529 /* read until end, there is no next string */
532 /* read until next newline */
533 log_next = strchr(log_part, '\n');
534 if (log_next != NULL) {
537 /* ignore trailing newline */
538 if (*log_next == '\0') {
544 buf = logcat_log(dumper, seconds, milliseconds, pid, tid, priority, tag, log_part);
549 length = (guint32) strlen(buf);
551 if (!wtap_dump_file_write(wdh, buf, length, err)) {
556 wdh->bytes_dumped += length;
557 } while (log_next != NULL );
562 case WTAP_ENCAP_LOGCAT_BRIEF:
563 case WTAP_ENCAP_LOGCAT_TAG:
564 case WTAP_ENCAP_LOGCAT_PROCESS:
565 case WTAP_ENCAP_LOGCAT_TIME:
566 case WTAP_ENCAP_LOGCAT_THREAD:
567 case WTAP_ENCAP_LOGCAT_THREADTIME:
568 case WTAP_ENCAP_LOGCAT_LONG:
569 if (dumper->type == wdh->encap) {
570 if (!wtap_dump_file_write(wdh, (const gchar*) pd, rec->rec_header.packet_header.caplen, err)) {
574 *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
582 static gboolean logcat_text_dump_open(wtap_dumper *wdh, guint dump_type, int *err _U_) {
583 struct dumper_t *dumper;
585 dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
586 dumper->type = dump_type;
589 wdh->subtype_write = logcat_text_dump_text;
594 gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err) {
595 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_BRIEF, err);
598 gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err) {
599 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_PROCESS, err);
602 gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err) {
603 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TAG, err);
606 gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err) {
607 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TIME, err);
610 gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err) {
611 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREAD, err);
614 gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err) {
615 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREADTIME, err);
618 gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err) {
619 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_LONG, err);
623 * Editor modelines - http://www.wireshark.org/tools/modelines.html
628 * indent-tabs-mode: nil
631 * vi: set shiftwidth=4 tabstop=8 expandtab:
632 * :indentSize=4:tabSize=8:noTabs=true: