3 * Copyright 2014, Michal Orynicz for Tieto Corporation
4 * Copyright 2014, Michal Labedzki for Tieto Corporation
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "file_wrappers.h"
28 #include "logcat_text.h"
35 /* Returns '?' for invalid priorities */
36 static gchar get_priority(const guint8 priority) {
37 static gchar priorities[] = "??VDIWEFS";
39 if (priority >= (guint8) sizeof(priorities))
42 return priorities[priority];
45 static gint buffered_detect_version(const guint8 *pd)
47 const struct logger_entry *log_entry;
48 const struct logger_entry_v2 *log_entry_v2;
50 const guint8 *msg_payload = NULL;
55 log_entry = (const struct logger_entry *)(const void *) pd;
56 log_entry_v2 = (const struct logger_entry_v2 *)(const void *) pd;
58 /* must contain at least priority and two nulls as separator */
59 if (log_entry->len < 3)
62 /* payload length may not exceed the maximum payload size */
63 if (log_entry->len > LOGGER_ENTRY_MAX_PAYLOAD)
66 /* cannot rely on __pad being 0 for v1, use heuristics to find out what
67 * version is in use. First assume the smallest msg. */
68 for (version = 1; version <= 2; ++version) {
70 msg_payload = (const guint8 *) (log_entry + 1);
71 } else if (version == 2) {
72 /* v2 is 4 bytes longer */
73 msg_payload = (const guint8 *) (log_entry_v2 + 1);
74 if (log_entry_v2->hdr_size != sizeof(*log_entry_v2))
78 /* A v2 msg has a 32-bit userid instead of v1 priority */
79 if (get_priority(msg_payload[0]) == '?')
82 /* Is there a terminating '\0' for the tag? */
83 msg_part = (guint8 *) memchr(msg_payload, '\0', log_entry->len - 1);
87 /* if msg is '\0'-terminated, is it equal to the payload len? */
89 msg_len = (guint16)(log_entry->len - (msg_part - msg_payload));
90 msg_end = (guint8 *) memchr(msg_part, '\0', msg_len);
91 /* is the end of the buffer (-1) equal to the end of msg? */
92 if (msg_end && (msg_payload + log_entry->len - 1 != msg_end))
101 static gchar *logcat_log(const struct dumper_t *dumper, guint32 seconds,
102 gint milliseconds, gint pid, gint tid, gchar priority, const gchar *tag,
105 gchar time_buffer[15];
109 datetime = (time_t) seconds;
111 switch (dumper->type) {
112 case WTAP_ENCAP_LOGCAT_BRIEF:
113 return g_strdup_printf("%c/%-8s(%5i): %s\n",
114 priority, tag, pid, log);
115 case WTAP_ENCAP_LOGCAT_PROCESS:
116 /* NOTE: Last parameter should be "process name", not tag;
117 Unfortunately, we do not have process name */
118 return g_strdup_printf("%c(%5i) %s (%s)\n",
119 priority, pid, log, "");
120 case WTAP_ENCAP_LOGCAT_TAG:
121 return g_strdup_printf("%c/%-8s: %s\n",
123 case WTAP_ENCAP_LOGCAT_THREAD:
124 return g_strdup_printf("%c(%5i:%5i) %s\n",
125 priority, pid, tid, log);
126 case WTAP_ENCAP_LOGCAT_TIME:
127 tm = gmtime(&datetime);
129 strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
131 return g_strdup_printf("%s.%03i %c/%-8s(%5i): %s\n",
132 time_buffer, milliseconds, priority, tag, pid, log);
134 return g_strdup_printf("Not representable %c/%-8s(%5i): %s\n",
135 priority, tag, pid, log);
137 case WTAP_ENCAP_LOGCAT_THREADTIME:
138 tm = gmtime(&datetime);
140 strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
142 return g_strdup_printf("%s.%03i %5i %5i %c %-8s: %s\n",
143 time_buffer, milliseconds, pid, tid, priority, tag, log);
145 return g_strdup_printf("Not representable %5i %5i %c %-8s: %s\n",
146 pid, tid, priority, tag, log);
148 case WTAP_ENCAP_LOGCAT_LONG:
149 tm = gmtime(&datetime);
151 strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
153 return g_strdup_printf("[ %s.%03i %5i:%5i %c/%-8s ]\n%s\n\n",
154 time_buffer, milliseconds, pid, tid, priority, tag, log);
156 return g_strdup_printf("[ Not representable %5i:%5i %c/%-8s ]\n%s\n\n",
157 pid, tid, priority, tag, log);
165 static void get_time(gchar *string, struct wtap_pkthdr *phdr) {
170 if (6 == sscanf(string, "%d-%d %d:%d:%d.%d", &date.tm_mon, &date.tm_mday, &date.tm_hour,
171 &date.tm_min, &date.tm_sec, &ms)) {
174 seconds = mktime(&date);
175 phdr->ts.secs = (time_t) seconds;
176 phdr->ts.nsecs = (int) (ms * 1e6);
177 phdr->presence_flags = WTAP_HAS_TS;
179 phdr->presence_flags = 0;
180 phdr->ts.secs = (time_t) 0;
181 phdr->ts.nsecs = (int) 0;
185 static gboolean logcat_text_read_packet(FILE_T fh, struct wtap_pkthdr *phdr,
186 Buffer *buf, gint file_type) {
191 cbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
193 ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE_STANDARD, fh);
194 } while (NULL != ret && 3 > strlen(cbuff) && !file_eof(fh));
196 if (NULL == ret || 3 > strlen(cbuff)) {
201 if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type &&
202 !g_regex_match_simple(SPECIAL_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW), G_REGEX_MATCH_NOTEMPTY)) {
208 lbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
209 file_off = file_tell(fh);
210 ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE_STANDARD, fh);
211 while (NULL != ret2 && 2 < strlen(lbuff) && !file_eof(fh)) {
212 g_strlcat(cbuff,lbuff,WTAP_MAX_PACKET_SIZE_STANDARD);
213 file_off = file_tell(fh);
214 ret2 = file_gets(lbuff,WTAP_MAX_PACKET_SIZE_STANDARD, fh);
217 if(NULL == ret2 || 2 < strlen(lbuff)) {
223 file_seek(fh,file_off,SEEK_SET,&err);
227 phdr->rec_type = REC_TYPE_PACKET;
228 phdr->caplen = (guint32)strlen(cbuff);
229 phdr->len = phdr->caplen;
231 ws_buffer_assure_space(buf, phdr->caplen + 1);
232 pd = ws_buffer_start_ptr(buf);
233 if ((WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME == file_type
234 || WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME == file_type
235 || WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type)
236 && '-' != cbuff[0]) { /* the last part filters out the -- beginning of... lines */
237 if (WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG == file_type) {
238 get_time(cbuff+2, phdr);
240 get_time(cbuff, phdr);
243 phdr->presence_flags = 0;
244 phdr->ts.secs = (time_t) 0;
245 phdr->ts.nsecs = (int) 0;
247 memcpy(pd, cbuff, phdr->caplen + 1);
252 static gboolean logcat_text_read(wtap *wth, int *err _U_ , gchar **err_info _U_,
253 gint64 *data_offset) {
254 *data_offset = file_tell(wth->fh);
256 return logcat_text_read_packet(wth->fh, &wth->phdr, wth->frame_buffer,
257 wth->file_type_subtype);
260 static gboolean logcat_text_seek_read(wtap *wth, gint64 seek_off,
261 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info _U_) {
262 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
265 if (!logcat_text_read_packet(wth->random_fh, phdr, buf,
266 wth->file_type_subtype)) {
268 *err = WTAP_ERR_SHORT_READ;
274 wtap_open_return_val logcat_text_open(wtap *wth, int *err, gchar **err_info _U_) {
278 if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
279 return WTAP_OPEN_ERROR;
281 cbuff = (gchar*)g_malloc(WTAP_MAX_PACKET_SIZE_STANDARD);
283 ret = file_gets(cbuff, WTAP_MAX_PACKET_SIZE_STANDARD, wth->fh);
284 } while (NULL != ret && !file_eof(wth->fh)
285 && ((3 > strlen(cbuff))
286 || g_regex_match_simple(SPECIAL_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
287 G_REGEX_MATCH_NOTEMPTY)));
289 if (g_regex_match_simple(BRIEF_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
290 G_REGEX_MATCH_NOTEMPTY)) {
291 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_BRIEF;
292 wth->file_encap = WTAP_ENCAP_LOGCAT_BRIEF;
293 } else if (g_regex_match_simple(TAG_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
294 G_REGEX_MATCH_NOTEMPTY)) {
295 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TAG;
296 wth->file_encap = WTAP_ENCAP_LOGCAT_TAG;
297 } else if (g_regex_match_simple(PROCESS_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
298 G_REGEX_MATCH_NOTEMPTY)) {
299 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_PROCESS;
300 wth->file_encap = WTAP_ENCAP_LOGCAT_PROCESS;
301 } else if (g_regex_match_simple(TIME_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
302 G_REGEX_MATCH_NOTEMPTY)) {
303 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_TIME;
304 wth->file_encap = WTAP_ENCAP_LOGCAT_TIME;
305 } else if (g_regex_match_simple(THREAD_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
306 G_REGEX_MATCH_NOTEMPTY)) {
307 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREAD;
308 wth->file_encap = WTAP_ENCAP_LOGCAT_THREAD;
309 } else if (g_regex_match_simple(THREADTIME_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
310 G_REGEX_MATCH_NOTEMPTY)) {
311 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_THREADTIME;
312 wth->file_encap = WTAP_ENCAP_LOGCAT_THREADTIME;
313 } else if (g_regex_match_simple(LONG_STRING, cbuff, (GRegexCompileFlags)((gint) G_REGEX_ANCHORED | (gint) G_REGEX_RAW),
314 G_REGEX_MATCH_NOTEMPTY)) {
315 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT_LONG;
316 wth->file_encap = WTAP_ENCAP_LOGCAT_LONG;
319 return WTAP_OPEN_NOT_MINE;
322 if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
324 return WTAP_OPEN_ERROR;
326 wth->snapshot_length = 0;
328 wth->subtype_read = logcat_text_read;
329 wth->subtype_seek_read = logcat_text_seek_read;
330 wth->file_tsprec = WTAP_TSPREC_USEC;
332 return WTAP_OPEN_MINE;
335 int logcat_text_brief_dump_can_write_encap(int encap) {
336 if (encap == WTAP_ENCAP_PER_PACKET)
337 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
340 case WTAP_ENCAP_LOGCAT:
341 case WTAP_ENCAP_LOGCAT_BRIEF:
342 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
345 return WTAP_ERR_UNWRITABLE_ENCAP;
349 int logcat_text_process_dump_can_write_encap(int encap) {
350 if (encap == WTAP_ENCAP_PER_PACKET)
351 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
354 case WTAP_ENCAP_LOGCAT:
355 case WTAP_ENCAP_LOGCAT_PROCESS:
356 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
359 return WTAP_ERR_UNWRITABLE_ENCAP;
363 int logcat_text_tag_dump_can_write_encap(int encap) {
364 if (encap == WTAP_ENCAP_PER_PACKET)
365 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
368 case WTAP_ENCAP_LOGCAT:
369 case WTAP_ENCAP_LOGCAT_TAG:
370 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
373 return WTAP_ERR_UNWRITABLE_ENCAP;
377 int logcat_text_time_dump_can_write_encap(int encap) {
378 if (encap == WTAP_ENCAP_PER_PACKET)
379 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
382 case WTAP_ENCAP_LOGCAT:
383 case WTAP_ENCAP_LOGCAT_TIME:
384 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
387 return WTAP_ERR_UNWRITABLE_ENCAP;
391 int logcat_text_thread_dump_can_write_encap(int encap) {
392 if (encap == WTAP_ENCAP_PER_PACKET)
393 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
396 case WTAP_ENCAP_LOGCAT:
397 case WTAP_ENCAP_LOGCAT_THREAD:
398 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
401 return WTAP_ERR_UNWRITABLE_ENCAP;
405 int logcat_text_threadtime_dump_can_write_encap(int encap) {
406 if (encap == WTAP_ENCAP_PER_PACKET)
407 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
410 case WTAP_ENCAP_LOGCAT:
411 case WTAP_ENCAP_LOGCAT_THREADTIME:
412 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
415 return WTAP_ERR_UNWRITABLE_ENCAP;
419 int logcat_text_long_dump_can_write_encap(int encap) {
420 if (encap == WTAP_ENCAP_PER_PACKET)
421 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
424 case WTAP_ENCAP_LOGCAT:
425 case WTAP_ENCAP_LOGCAT_LONG:
426 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
429 return WTAP_ERR_UNWRITABLE_ENCAP;
433 static gboolean logcat_text_dump_text(wtap_dumper *wdh,
434 const struct wtap_pkthdr *phdr,
435 const guint8 *pd, int *err, gchar **err_info)
440 const struct logger_entry *log_entry;
441 const struct logger_entry_v2 *log_entry_v2;
448 const guint8 *msg_payload = NULL;
449 const gchar *msg_begin;
455 const struct dumper_t *dumper = (const struct dumper_t *) wdh->priv;
457 /* We can only write packet records. */
458 if (phdr->rec_type != REC_TYPE_PACKET) {
459 *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
463 switch (wdh->encap) {
464 case WTAP_ENCAP_WIRESHARK_UPPER_PDU:
468 skipped_length = logcat_exported_pdu_length(pd);
469 pd += skipped_length;
471 if (!wtap_dump_file_write(wdh, (const gchar*) pd, phdr->caplen - skipped_length, err)) {
476 case WTAP_ENCAP_LOGCAT:
477 /* Skip EXPORTED_PDU*/
478 if (wdh->encap == WTAP_ENCAP_WIRESHARK_UPPER_PDU) {
481 skipped_length = logcat_exported_pdu_length(pd);
482 pd += skipped_length;
484 logcat_version = buffered_detect_version(pd);
486 const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
488 logcat_version = pseudo_header->logcat.version;
491 log_entry = (const struct logger_entry *)(const void *) pd;
492 log_entry_v2 = (const struct logger_entry_v2 *)(const void *) pd;
494 payload_length = GINT32_FROM_LE(log_entry->len);
495 pid = GINT32_FROM_LE(log_entry->pid);
496 tid = GINT32_FROM_LE(log_entry->tid);
497 seconds = GINT32_FROM_LE(log_entry->sec);
498 milliseconds = GINT32_FROM_LE(log_entry->nsec) / 1000000;
500 /* msg: <prio:1><tag:N>\0<msg:N>\0 with N >= 0, last \0 can be missing */
501 if (logcat_version == 1) {
502 msg_payload = (const guint8 *) (log_entry + 1);
504 priority = get_priority(msg_payload[0]);
505 tag = msg_payload + 1;
506 msg_pre_skip = 1 + (gint) strlen(tag) + 1;
507 msg_begin = msg_payload + msg_pre_skip;
508 } else if (logcat_version == 2) {
509 msg_payload = (const guint8 *) (log_entry_v2 + 1);
511 priority = get_priority(msg_payload[0]);
512 tag = msg_payload + 1;
513 msg_pre_skip = 1 + (gint) strlen(tag) + 1;
514 msg_begin = msg_payload + msg_pre_skip;
516 *err = WTAP_ERR_UNWRITABLE_REC_DATA;
517 *err_info = g_strdup_printf("logcat: version %d isn't supported",
522 /* copy the message part. If a nul byte was missing, it will be added. */
523 log = g_strndup(msg_begin, payload_length - msg_pre_skip);
525 /* long format: display one header followed by the whole message (which may
526 * contain new lines). Other formats: include tag, etc. with each line */
530 if (dumper->type == WTAP_ENCAP_LOGCAT_LONG) {
531 /* read until end, there is no next string */
534 /* read until next newline */
535 log_next = strchr(log_part, '\n');
536 if (log_next != NULL) {
539 /* ignore trailing newline */
540 if (*log_next == '\0') {
546 buf = logcat_log(dumper, seconds, milliseconds, pid, tid, priority, tag, log_part);
551 length = (guint32) strlen(buf);
553 if (!wtap_dump_file_write(wdh, buf, length, err)) {
558 wdh->bytes_dumped += length;
559 } while (log_next != NULL );
564 case WTAP_ENCAP_LOGCAT_BRIEF:
565 case WTAP_ENCAP_LOGCAT_TAG:
566 case WTAP_ENCAP_LOGCAT_PROCESS:
567 case WTAP_ENCAP_LOGCAT_TIME:
568 case WTAP_ENCAP_LOGCAT_THREAD:
569 case WTAP_ENCAP_LOGCAT_THREADTIME:
570 case WTAP_ENCAP_LOGCAT_LONG:
571 if (dumper->type == wdh->encap) {
572 if (!wtap_dump_file_write(wdh, (const gchar*) pd, phdr->caplen, err)) {
576 *err = WTAP_ERR_UNWRITABLE_FILE_TYPE;
584 static gboolean logcat_text_dump_open(wtap_dumper *wdh, guint dump_type, int *err _U_) {
585 struct dumper_t *dumper;
587 dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
588 dumper->type = dump_type;
591 wdh->subtype_write = logcat_text_dump_text;
596 gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err) {
597 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_BRIEF, err);
600 gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err) {
601 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_PROCESS, err);
604 gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err) {
605 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TAG, err);
608 gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err) {
609 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_TIME, err);
612 gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err) {
613 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREAD, err);
616 gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err) {
617 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_THREADTIME, err);
620 gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err) {
621 return logcat_text_dump_open(wdh, WTAP_ENCAP_LOGCAT_LONG, err);
625 * Editor modelines - http://www.wireshark.org/tools/modelines.html
630 * indent-tabs-mode: nil
633 * vi: set shiftwidth=4 tabstop=8 expandtab:
634 * :indentSize=4:tabSize=8:noTabs=true: