Put the common code for reading Logcat packets into a routine.
[metze/wireshark/wip.git] / wiretap / logcat.c
1 /* logcat.c
2  *
3  * Copyright 2014, Michal Labedzki for Tieto Corporation
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #include "config.h"
21
22 #include <string.h>
23 #include <time.h>
24
25 #include "wtap-int.h"
26 #include "file_wrappers.h"
27 #include "buffer.h"
28
29 #include "logcat.h"
30
31 enum dump_type_t {
32     DUMP_BINARY,
33     DUMP_BRIEF,
34     DUMP_PROCESS,
35     DUMP_TAG,
36     DUMP_TIME,
37     DUMP_THREAD,
38     DUMP_THREADTIME,
39     DUMP_LONG
40 };
41
42 struct dumper_t {
43     enum dump_type_t type;
44 };
45
46 static gchar get_priority(const guint8 *priority) {
47     static gchar priorities[] = "??VDIWEFS";
48
49     if (*priority >= (guint8) sizeof(priorities))
50         return '?';
51
52     return priorities[(int) *priority];
53 }
54
55 static gchar *logcat_log(const struct dumper_t *dumper, guint32 seconds,
56         gint microseconds, gint pid, gint tid, gchar priority, const gchar *tag,
57         const gchar *log)
58 {
59     gchar  time_buffer[15];
60     time_t datetime;
61
62     datetime = (time_t) seconds;
63
64     switch (dumper->type) {
65         case DUMP_BRIEF:
66             return g_strdup_printf("%c/%s(%5i): %s\n",
67                     priority, tag, pid, log);
68         case DUMP_PROCESS:
69             return g_strdup_printf("%c(%5i) %s  (%s)\n",
70                     priority, pid, log, tag);
71         case DUMP_TAG:
72             return g_strdup_printf("%c/%s: %s\n",
73                    priority, tag, log);
74         case DUMP_THREAD:
75             return g_strdup_printf("%c(%5i:%5i) %s\n",
76                     priority, pid, tid, log);
77         case DUMP_TIME:
78             strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
79                     gmtime(&datetime));
80             return g_strdup_printf("%s.%03i %c/%s(%5i): %s\n",
81                     time_buffer, microseconds, priority, tag, pid, log);
82         case DUMP_THREADTIME:
83             strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
84                     gmtime(&datetime));
85             return g_strdup_printf("%s.%03i %5i:%5i %c %s: %s\n",
86                     time_buffer, microseconds, pid, tid, priority, tag, log);
87         case DUMP_LONG:
88             strftime(time_buffer, sizeof(time_buffer), "%m-%d %H:%M:%S",
89                     gmtime(&datetime));
90             return g_strdup_printf("[ %s.%03i %5i:%5i %c/%s ]\n%s\n\n",
91                     time_buffer, microseconds, pid, tid, priority, tag, log);
92         default:
93             return NULL;
94     }
95
96 }
97
98 static gint detect_version(wtap *wth, int *err, gchar **err_info)
99 {
100     gint     bytes_read;
101     guint16  payload_length;
102     guint16  try_header_size;
103     guint8  *buffer;
104     gint64   file_offset;
105     guint32  log_length;
106     guint32  tag_length;
107     guint16  tmp;
108
109     file_offset = file_tell(wth->fh);
110
111     bytes_read = file_read(&tmp, 2, wth->fh);
112     if (bytes_read != 2) {
113         *err = file_error(wth->fh, err_info);
114         if (*err == 0 && bytes_read != 0)
115             *err = WTAP_ERR_SHORT_READ;
116         return -1;
117     }
118     payload_length = pletoh16(&tmp);
119
120     bytes_read = file_read(&tmp, 2, wth->fh);
121     if (bytes_read != 2) {
122         *err = file_error(wth->fh, err_info);
123         if (*err == 0 && bytes_read != 0)
124             *err = WTAP_ERR_SHORT_READ;
125         return -1;
126     }
127     try_header_size = pletoh16(&tmp);
128
129     buffer = (guint8 *) g_malloc(5 * 4 + payload_length);
130     bytes_read = file_read(buffer, 5 * 4 + payload_length, wth->fh);
131     if (bytes_read != 5 * 4 + payload_length) {
132         if (bytes_read != 4 * 4 + payload_length) {
133             *err = file_error(wth->fh, err_info);
134             if (*err == 0 && bytes_read != 0)
135                 *err = WTAP_ERR_SHORT_READ;
136             g_free(buffer);
137             return -1;
138         }
139     }
140
141     if (try_header_size == 24) {
142         tag_length = (guint32)strlen(buffer + 5 * 4 + 1) + 1;
143         log_length = (guint32)strlen(buffer + 5 * 4 + 1 + tag_length) + 1;
144         if (payload_length == 1 + tag_length + log_length) {
145             g_free(buffer);
146             return 2;
147         }
148     }
149
150     tag_length = (guint32)strlen(buffer + 4 * 4 + 1) + 1;
151     log_length = (guint32)strlen(buffer + 4 * 4 + 1 + tag_length) + 1;
152     if (payload_length == 1 + tag_length + log_length) {
153         if (file_seek(wth->fh, file_offset + 4 * 4 + 1 + tag_length + log_length, SEEK_SET, err) == -1) {
154             g_free(buffer);
155             return -1;
156         }
157         g_free(buffer);
158         return 1;
159     }
160
161     g_free(buffer);
162     return 0;
163 }
164
165 static gboolean logcat_read_packet(struct logcat_phdr *logcat, FILE_T fh,
166     struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
167 {
168     gint                 bytes_read;
169     gint                 packet_size;
170     guint16              payload_length;
171     guint                tmp[2];
172     guint8              *pd;
173
174     bytes_read = file_read(&tmp, 2, fh);
175     if (bytes_read != 2) {
176         *err = file_error(fh, err_info);
177         if (*err == 0 && bytes_read != 0)
178             *err = WTAP_ERR_SHORT_READ;
179         return FALSE;
180     }
181     payload_length = pletoh16(tmp);
182
183     if (logcat->version == 1) {
184         packet_size = 5 * 4 + payload_length;
185     } else if (logcat->version == 2) {
186         packet_size = 6 * 4 + payload_length;
187     } else {
188         return FALSE;
189     }
190
191     buffer_assure_space(buf, packet_size);
192     pd = buffer_start_ptr(buf);
193
194     /* Copy the first two bytes of the packet. */
195     memcpy(pd, tmp, 2);
196
197     /* Read the rest of the packet. */
198     bytes_read = file_read(pd + 2, packet_size - 2, fh);
199     if (bytes_read != packet_size - 2) {
200         *err = file_error(fh, err_info);
201         if (*err == 0)
202             *err = WTAP_ERR_SHORT_READ;
203         return FALSE;
204     }
205
206     phdr->presence_flags = WTAP_HAS_TS;
207     phdr->ts.secs = (time_t) pletoh32(pd + 12);
208     phdr->ts.nsecs = (int) pletoh32(pd + 16);
209     phdr->caplen = packet_size;
210     phdr->len = packet_size;
211
212     phdr->pseudo_header.logcat.version = logcat->version;
213
214     return TRUE;
215 }
216
217 static gboolean logcat_read(wtap *wth, int *err, gchar **err_info,
218     gint64 *data_offset)
219 {
220     *data_offset = file_tell(wth->fh);
221
222     return logcat_read_packet((struct logcat_phdr *) wth->priv, wth->fh,
223         &wth->phdr, wth->frame_buffer, err, err_info);
224 }
225
226 static gboolean logcat_seek_read(wtap *wth, gint64 seek_off,
227     struct wtap_pkthdr *phdr, Buffer *buf,
228     int *err, gchar **err_info)
229 {
230     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
231         return FALSE;
232
233     if (!logcat_read_packet((struct logcat_phdr *) wth->priv, wth->random_fh,
234          phdr, buf, err, err_info)) {
235         if (*err == 0)
236             *err = WTAP_ERR_SHORT_READ;
237         return FALSE;
238     }
239     return TRUE;
240 }
241
242 int logcat_open(wtap *wth, int *err, gchar **err_info _U_)
243 {
244     int                 local_err;
245     gchar              *local_err_info;
246     gint                version;
247     gint                tmp_version;
248     struct logcat_phdr *logcat;
249
250     /* check first 3 packets (or 2 or 1 if EOF) versions to check file format is correct */
251     version = detect_version(wth, &local_err, &local_err_info);
252     if (version <= 0)
253         return 0;
254
255     tmp_version = detect_version(wth, &local_err, &local_err_info);
256     if (tmp_version < 0 && !file_eof(wth->fh)) {
257         return 0;
258     } else if (tmp_version > 0) {
259         if (tmp_version != version)
260             return 0;
261
262         tmp_version = detect_version(wth, &local_err, &local_err_info);
263         if (tmp_version != version && !file_eof(wth->fh))
264             return 0;
265     }
266
267     if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
268         return -1;
269
270     logcat = (struct logcat_phdr *) g_malloc(sizeof(struct logcat_phdr));
271     logcat->version = version;
272
273     wth->priv = logcat;
274
275     wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOGCAT;
276     wth->file_encap = WTAP_ENCAP_LOGCAT;
277     wth->snapshot_length = 0;
278
279     wth->subtype_read = logcat_read;
280     wth->subtype_seek_read = logcat_seek_read;
281     wth->tsprecision = WTAP_FILE_TSPREC_USEC;
282
283     return 1;
284 }
285
286 int logcat_dump_can_write_encap(int encap)
287 {
288     if (encap == WTAP_ENCAP_PER_PACKET)
289         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
290
291     if (encap != WTAP_ENCAP_LOGCAT)
292         return WTAP_ERR_UNSUPPORTED_ENCAP;
293
294     return 0;
295 }
296
297 static gboolean logcat_binary_dump(wtap_dumper *wdh,
298     const struct wtap_pkthdr *phdr,
299     const guint8 *pd, int *err)
300 {
301     if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err))
302         return FALSE;
303
304     wdh->bytes_dumped += phdr->caplen;
305
306     return TRUE;
307 }
308
309 gboolean logcat_binary_dump_open(wtap_dumper *wdh, int *err)
310 {
311     wdh->subtype_write = logcat_binary_dump;
312     wdh->subtype_close = NULL;
313
314     switch (wdh->file_type_subtype) {
315         case WTAP_FILE_TYPE_SUBTYPE_LOGCAT:
316             wdh->tsprecision = WTAP_FILE_TSPREC_USEC;
317             break;
318
319         default:
320             *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
321             return FALSE;
322     }
323
324     return TRUE;
325 }
326
327 static gboolean logcat_dump_text(wtap_dumper *wdh,
328     const struct wtap_pkthdr *phdr,
329     const guint8 *pd, int *err)
330 {
331     gchar                          *buf;
332     gint                            length;
333     gchar                           priority;
334     const gchar                    *tag;
335     const gint                     *pid;
336     const gint                     *tid;
337     const gchar                    *log;
338     gchar                          *log_part;
339     const gchar                    *str_begin;
340     const gchar                    *str_end;
341     const guint32                  *datetime;
342     const guint32                  *nanoseconds;
343     const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
344     const struct dumper_t          *dumper        = (const struct dumper_t *) wdh->priv;
345
346     if (pseudo_header->logcat.version == 1) {
347         pid = (const gint *) (pd + 4);
348         tid = (const gint *) (pd + 2 * 4);
349         datetime = (const guint32 *) (pd + 3 * 4);
350         nanoseconds = (const guint32 *) (pd + 4 * 4);
351         priority = get_priority((const guint8 *) (pd + 5 * 4));
352         tag = (const gchar *) (pd + 5 * 4 + 1);
353         log = tag + strlen(tag) + 1;
354     } else if (pseudo_header->logcat.version == 2) {
355         pid = (const gint *) (pd + 4);
356         tid = (const gint *) (pd + 2 * 4);
357         datetime = (const guint32 *) (pd + 3 * 4);
358         nanoseconds = (const guint32 *) (pd + 4 * 4);
359         priority = get_priority((const guint8 *) (pd + 6 * 4));
360         tag = (const char *) (pd + 6 * 4 + 1);
361         log = tag + strlen(tag) + 1;
362     } else {
363         *err = WTAP_ERR_UNSUPPORTED;
364         return FALSE;
365     }
366
367     str_begin = str_end = log;
368     while (dumper->type != DUMP_LONG && (str_end = strchr(str_begin, '\n'))) {
369         log_part = (gchar *) g_malloc(str_end - str_begin + 1);
370         g_strlcpy(log_part, str_begin, str_end - str_begin);
371         log_part[str_end - str_begin] = '\0';
372         str_begin = str_end + 1;
373
374         buf = logcat_log(dumper, *datetime, *nanoseconds / 1000000, *pid, *tid,
375                 priority, tag, log_part);
376         if (!buf) {
377             g_free(log_part);
378             return FALSE;
379         }
380         g_free(log_part);
381         length = (guint32)strlen(buf);
382
383         if (!wtap_dump_file_write(wdh, buf, length, err)) {
384             g_free(buf);
385             return FALSE;
386         }
387
388         wdh->bytes_dumped += length;
389
390         g_free(buf);
391     }
392
393     if (*str_begin != '\0') {
394         log_part = (gchar *) g_malloc(strlen(str_begin) + 1);
395         g_strlcpy(log_part, str_begin, strlen(str_begin));
396         log_part[strlen(str_begin)] = '\0';
397
398         buf = logcat_log(dumper, *datetime, *nanoseconds / 1000000, *pid, *tid,
399                 priority, tag, log_part);
400         if (!buf) {
401             g_free(log_part);
402             return FALSE;
403         }
404         g_free(log_part);
405         length = (guint32)strlen(buf);
406
407         if (!wtap_dump_file_write(wdh, buf, length, err)) {
408             g_free(buf);
409             return FALSE;
410         }
411
412         wdh->bytes_dumped += length;
413         g_free(buf);
414     }
415
416     return TRUE;
417 }
418
419 gboolean logcat_text_brief_dump_open(wtap_dumper *wdh, int *err _U_)
420 {
421     struct dumper_t *dumper;
422
423     dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
424     dumper->type = DUMP_BRIEF;
425
426     wdh->priv = dumper;
427     wdh->subtype_write = logcat_dump_text;
428     wdh->subtype_close = NULL;
429
430     return TRUE;
431 }
432
433 gboolean logcat_text_process_dump_open(wtap_dumper *wdh, int *err _U_)
434 {
435     struct dumper_t *dumper;
436
437     dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
438     dumper->type = DUMP_PROCESS;
439
440     wdh->priv = dumper;
441     wdh->subtype_write = logcat_dump_text;
442     wdh->subtype_close = NULL;
443
444     return TRUE;
445 }
446
447 gboolean logcat_text_tag_dump_open(wtap_dumper *wdh, int *err _U_)
448 {
449     struct dumper_t *dumper;
450
451     dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
452     dumper->type = DUMP_TAG;
453
454     wdh->priv = dumper;
455     wdh->subtype_write = logcat_dump_text;
456     wdh->subtype_close = NULL;
457
458     return TRUE;
459 }
460
461 gboolean logcat_text_time_dump_open(wtap_dumper *wdh, int *err _U_)
462 {
463     struct dumper_t *dumper;
464
465     dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
466     dumper->type = DUMP_TIME;
467
468     wdh->priv = dumper;
469     wdh->subtype_write = logcat_dump_text;
470     wdh->subtype_close = NULL;
471
472     return TRUE;
473 }
474
475 gboolean logcat_text_thread_dump_open(wtap_dumper *wdh, int *err _U_)
476 {
477     struct dumper_t *dumper;
478
479     dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
480     dumper->type = DUMP_THREAD;
481
482     wdh->priv = dumper;
483     wdh->subtype_write = logcat_dump_text;
484     wdh->subtype_close = NULL;
485
486     return TRUE;
487 }
488
489 gboolean logcat_text_threadtime_dump_open(wtap_dumper *wdh, int *err _U_)
490 {
491     struct dumper_t *dumper;
492
493     dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
494     dumper->type = DUMP_THREADTIME;
495
496     wdh->priv = dumper;
497     wdh->subtype_write = logcat_dump_text;
498     wdh->subtype_close = NULL;
499
500     return TRUE;
501 }
502
503 gboolean logcat_text_long_dump_open(wtap_dumper *wdh, int *err _U_)
504 {
505     struct dumper_t *dumper;
506
507     dumper = (struct dumper_t *) g_malloc(sizeof(struct dumper_t));
508     dumper->type = DUMP_LONG;
509
510     wdh->priv = dumper;
511     wdh->subtype_write = logcat_dump_text;
512     wdh->subtype_close = NULL;
513
514     return TRUE;
515 }
516
517 /*
518  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
519  *
520  * Local variables:
521  * c-basic-offset: 4
522  * tab-width: 8
523  * indent-tabs-mode: nil
524  * End:
525  *
526  * vi: set shiftwidth=4 tabstop=8 expandtab:
527  * :indentSize=4:tabSize=8:noTabs=true:
528  */