automotive-dlt
dlt-system-journal.c
Go to the documentation of this file.
1 /*
2  * @licence app begin@
3  * SPDX license identifier: MPL-2.0
4  *
5  * Copyright (C) 2011-2015, BMW AG
6  *
7  * This file is part of GENIVI Project DLT - Diagnostic Log and Trace.
8  *
9  * This Source Code Form is subject to the terms of the
10  * Mozilla Public License (MPL), v. 2.0.
11  * If a copy of the MPL was not distributed with this file,
12  * You can obtain one at http://mozilla.org/MPL/2.0/.
13  *
14  * For further information see http://www.genivi.org/.
15  * @licence end@
16  */
17 
27 /*******************************************************************************
28 ** **
29 ** SRC-MODULE: dlt-system-journal.c **
30 ** **
31 ** TARGET : linux **
32 ** **
33 ** PROJECT : DLT **
34 ** **
35 ** AUTHOR : Alexander Wenzel Alexander.AW.Wenzel@bmw.de **
36 ** **
37 ** PURPOSE : **
38 ** **
39 ** REMARKS : **
40 ** **
41 ** PLATFORM DEPENDANT [yes/no]: yes **
42 ** **
43 ** TO BE CHANGED BY USER [yes/no]: no **
44 ** **
45 *******************************************************************************/
46 #if defined(DLT_SYSTEMD_JOURNAL_ENABLE)
47 
48 #include <pthread.h>
49 #include <unistd.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <strings.h>
53 #include <errno.h>
54 #include <stdlib.h>
55 
56 #include "dlt-system.h"
57 
58 #include <systemd/sd-journal.h>
59 #include <systemd/sd-id128.h>
60 #include <inttypes.h> /* for PRI formatting macro */
61 
63 
64 #define DLT_SYSTEM_JOURNAL_BUFFER_SIZE 256
65 #define DLT_SYSTEM_JOURNAL_BUFFER_SIZE_BIG 2048
66 
67 #define DLT_SYSTEM_JOURNAL_ASCII_FIRST_VISIBLE_CHARACTER 31
68 #define DLT_SYSTEM_JOURNAL_BOOT_ID_MAX_LENGTH 9+32+1
69 
70 typedef struct
71 {
72  char real[DLT_SYSTEM_JOURNAL_BUFFER_SIZE];
73  char monotonic[DLT_SYSTEM_JOURNAL_BUFFER_SIZE];
74 } MessageTimestamp;
75 
76 DLT_IMPORT_CONTEXT(dltsystem)
77 DLT_DECLARE_CONTEXT(journalContext)
78 
79 int journal_checkUserBufferForFreeSpace()
80 {
81  int total_size, used_size;
82 
83  dlt_user_check_buffer(&total_size, &used_size);
84 
85  if((total_size - used_size) < (total_size/2))
86  {
87  return -1;
88  }
89  return 1;
90 }
91 
92 int dlt_system_journal_get(sd_journal* j,char *target,const char *field,size_t max_size)
93 {
94  char *data;
95  size_t length;
96  int error_code;
97  size_t field_size;
98 
99  // pre check parameters
100  if(max_size<1 || target == 0 || field == 0 || j == 0)
101  return -1;
102 
103  // intialise empty target
104  target[0]=0;
105 
106  // get data from journal
107  error_code = sd_journal_get_data(j, field,(const void **) &data, &length);
108 
109  // check if an error
110  if(error_code)
111  return error_code;
112 
113  // calculate field size
114  field_size = strlen(field)+1;
115 
116  //check length
117  if(length<field_size)
118  return -1;
119 
120  // copy string
121  if(max_size<=(length-field_size))
122  {
123  // truncate
124  strncpy(target,data+field_size,max_size-1);
125  target[max_size-1]=0;
126  }
127  else
128  {
129  // full copy
130  strncpy(target,data+field_size,length-field_size);
131  target[length-field_size]=0;
132 
133  }
134 
135  // debug messages
136  //printf("%s = %s\n",field,target);
137 
138  // Success
139  return 0;
140 }
141 
142 void dlt_system_journal_get_timestamp(sd_journal *journal, MessageTimestamp *timestamp)
143 {
144  int ret = 0;
145  uint64_t time_secs = 0;
146  uint64_t time_usecs = 0;
147  struct tm * timeinfo = NULL;
148 
149  char buffer_realtime[DLT_SYSTEM_JOURNAL_BUFFER_SIZE] = { 0 };
150  char buffer_realtime_formatted[DLT_SYSTEM_JOURNAL_BUFFER_SIZE] = { 0 };
151  char buffer_monotime[DLT_SYSTEM_JOURNAL_BUFFER_SIZE] = { 0 };
152 
153  /* Try to get realtime from message source and if not successful try to get realtime from journal entry */
154  ret = dlt_system_journal_get(journal, buffer_realtime, "_SOURCE_REALTIME_TIMESTAMP", sizeof(buffer_realtime));
155  if (ret == 0 && strlen(buffer_realtime) > 0)
156  {
157  errno = 0;
158  time_usecs = strtoull(buffer_realtime, NULL, 10);
159  if (errno != 0)
160  time_usecs = 0;
161  }
162  else
163  {
164  if ((ret = sd_journal_get_realtime_usec(journal, &time_usecs)) < 0)
165  {
166  DLT_LOG(dltsystem, DLT_LOG_WARN,
167  DLT_STRING("dlt-system-journal failed to get realtime: "),
168  DLT_STRING(strerror(-ret)));
169 
170  /* just to be sure to have a defined value */
171  time_usecs = 0;
172  }
173  }
174 
175  time_secs = time_usecs / 1000000;
176  timeinfo = localtime((const time_t*) (&time_secs));
177  strftime(buffer_realtime_formatted, sizeof(buffer_realtime_formatted), "%Y/%m/%d %H:%M:%S", timeinfo);
178 
179  snprintf(timestamp->real, sizeof(timestamp->real), "%s.%06"PRIu64, buffer_realtime_formatted, time_usecs % 1000000);
180 
181  /* Try to get monotonic time from message source and if not successful try to get monotonic time from journal entry */
182  ret = dlt_system_journal_get(journal, buffer_monotime, "_SOURCE_MONOTONIC_TIMESTAMP", sizeof(buffer_monotime));
183  if (ret == 0 && strlen(buffer_monotime) > 0)
184  {
185  errno = 0;
186  time_usecs = strtoull(buffer_monotime, NULL, 10);
187  if (errno != 0)
188  time_usecs = 0;
189  }
190  else
191  {
192  if ((ret = sd_journal_get_monotonic_usec(journal, &time_usecs, NULL)) < 0)
193  {
194  DLT_LOG(dltsystem, DLT_LOG_WARN,
195  DLT_STRING("dlt-system-journal failed to get monotonic time: "),
196  DLT_STRING(strerror(-ret)));
197 
198  /* just to be sure to have a defined value */
199  time_usecs = 0;
200  }
201  }
202 
203  snprintf(timestamp->monotonic, sizeof(timestamp->monotonic), "%"PRId64".%06"PRIu64, time_usecs / 1000000, time_usecs % 1000000);
204 }
205 
206 void journal_thread(void *v_conf)
207 {
208  int r;
209  sd_journal *j;
210  char match[DLT_SYSTEM_JOURNAL_BOOT_ID_MAX_LENGTH] = "_BOOT_ID=";
211  sd_id128_t boot_id;
212 
213  char buffer_process[DLT_SYSTEM_JOURNAL_BUFFER_SIZE] = { 0 },
214  buffer_priority[DLT_SYSTEM_JOURNAL_BUFFER_SIZE] = { 0 },
215  buffer_pid[DLT_SYSTEM_JOURNAL_BUFFER_SIZE] = { 0 },
216  buffer_comm[DLT_SYSTEM_JOURNAL_BUFFER_SIZE] = { 0 },
217  buffer_message[DLT_SYSTEM_JOURNAL_BUFFER_SIZE_BIG] = { 0 },
218  buffer_transport[DLT_SYSTEM_JOURNAL_BUFFER_SIZE] = { 0 };
219 
220  MessageTimestamp timestamp;
221 
222  int loglevel,systemd_loglevel;
223  char* systemd_log_levels[] = { "Emergency","Alert","Critical","Error","Warning","Notice","Informational","Debug" };
224 
225  DLT_LOG(dltsystem, DLT_LOG_DEBUG,
226  DLT_STRING("dlt-system-journal, in thread."));
227 
229  DLT_REGISTER_CONTEXT(journalContext, conf->Journal.ContextId, "Journal Adapter");
230 
231  r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY/*SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_RUNTIME_ONLY*/);
232  printf("journal open return %d\n", r);
233  if (r < 0) {
234  DLT_LOG(dltsystem, DLT_LOG_ERROR,
235  DLT_STRING("dlt-system-journal, cannot open journal:"),DLT_STRING(strerror(-r)));
236  printf("journal open failed: %s\n", strerror(-r));
237  return;
238  }
239 
240  if(conf->Journal.CurrentBoot)
241  {
242  /* show only current boot entries */
243  r = sd_id128_get_boot(&boot_id);
244  if(r<0)
245  {
246  DLT_LOG(dltsystem, DLT_LOG_ERROR,
247  DLT_STRING("dlt-system-journal failed to get boot id:"),DLT_STRING(strerror(-r)));
248  sd_journal_close(j);
249  return;
250 
251  }
252  sd_id128_to_string(boot_id, match + 9);
253  r = sd_journal_add_match(j,match,strlen(match));
254  if(r<0)
255  {
256  DLT_LOG(dltsystem, DLT_LOG_ERROR,
257  DLT_STRING("dlt-system-journal failed to get match:"),DLT_STRING(strerror(-r)));
258  sd_journal_close(j);
259  return;
260 
261  }
262  }
263 
264  if(conf->Journal.Follow)
265  {
266  /* show only last 10 entries and follow */
267  r = sd_journal_seek_tail(j);
268  if(r<0)
269  {
270  DLT_LOG(dltsystem, DLT_LOG_ERROR,
271  DLT_STRING("dlt-system-journal failed to seek to tail:"),DLT_STRING(strerror(-r)));
272  sd_journal_close(j);
273  return;
274 
275  }
276  r = sd_journal_previous_skip(j, 10);
277  if(r<0)
278  {
279  DLT_LOG(dltsystem, DLT_LOG_ERROR,
280  DLT_STRING("dlt-system-journal failed to seek back 10 entries:"),DLT_STRING(strerror(-r)));
281  sd_journal_close(j);
282  return;
283 
284  }
285 
286  }
287 
288  while(!threads.shutdown)
289  {
290 
291  r = sd_journal_next(j);
292  if(r<0)
293  {
294  DLT_LOG(dltsystem, DLT_LOG_ERROR,
295  DLT_STRING("dlt-system-journal failed to get next entry:"),DLT_STRING(strerror(-r)));
296  sd_journal_close(j);
297  return;
298 
299  }
300  else if(r>0)
301  {
302  /* get all data from current journal entry */
303  dlt_system_journal_get_timestamp(j, &timestamp);
304 
305  /* get data from journal entry, empty string if invalid fields */
306  dlt_system_journal_get(j,buffer_comm,"_COMM",sizeof(buffer_comm));
307  dlt_system_journal_get(j,buffer_pid,"_PID",sizeof(buffer_pid));
308  dlt_system_journal_get(j,buffer_priority,"PRIORITY",sizeof(buffer_priority));
309  dlt_system_journal_get(j,buffer_message,"MESSAGE",sizeof(buffer_message));
310  dlt_system_journal_get(j,buffer_transport,"_TRANSPORT",sizeof(buffer_transport));
311 
312  /* prepare process string */
313  if(strcmp(buffer_transport,"kernel")==0)
314  snprintf(buffer_process,DLT_SYSTEM_JOURNAL_BUFFER_SIZE,"kernel:");
315  else
316  snprintf(buffer_process,DLT_SYSTEM_JOURNAL_BUFFER_SIZE,"%s[%s]:",buffer_comm,buffer_pid);
317 
318  /* map log level on demand */
319  loglevel = DLT_LOG_INFO;
320  systemd_loglevel = atoi(buffer_priority);
321  if(conf->Journal.MapLogLevels)
322  {
323  /* Map log levels from journal to DLT */
324  switch(systemd_loglevel)
325  {
326  case 0: /* Emergency */
327  case 1: /* Alert */
328  case 2: /* Critical */
329  loglevel = DLT_LOG_FATAL;
330  break;
331  case 3: /* Error */
332  loglevel = DLT_LOG_ERROR;
333  break;
334  case 4: /* Warning */
335  loglevel = DLT_LOG_WARN;
336  break;
337  case 5: /* Notice */
338  case 6: /* Informational */
339  loglevel = DLT_LOG_INFO;
340  break;
341  case 7: /* Debug */
342  loglevel = DLT_LOG_DEBUG;
343  break;
344  default:
345  loglevel = DLT_LOG_INFO;
346  break;
347  }
348  }
349  if(systemd_loglevel>=0 && systemd_loglevel<=7)
350  snprintf(buffer_priority,DLT_SYSTEM_JOURNAL_BUFFER_SIZE,"%s:",systemd_log_levels[systemd_loglevel]);
351  else
352  snprintf(buffer_priority,DLT_SYSTEM_JOURNAL_BUFFER_SIZE,"prio_unknown:");
353 
354  /* write log entry */
355  DLT_LOG(journalContext, loglevel,
356  DLT_STRING(timestamp.real),
357  DLT_STRING(timestamp.monotonic),
358  DLT_STRING(buffer_process),
359  DLT_STRING(buffer_priority),
360  DLT_STRING(buffer_message)
361  );
362  }
363  else
364  {
365  r = sd_journal_wait(j,1000000);
366  if(r<0)
367  {
368  DLT_LOG(dltsystem, DLT_LOG_ERROR,
369  DLT_STRING("dlt-system-journal failed to call sd_journal_get_realtime_usec(): "),DLT_STRING(strerror(-r)));
370  sd_journal_close(j);
371  return;
372 
373  }
374  }
375 
376  if(journal_checkUserBufferForFreeSpace()==-1)
377  {
378  // buffer is nearly full
379  // wait 500ms for writing next entry
380  struct timespec t;
381  t.tv_sec = 0;
382  t.tv_nsec = 1000000ul*500;
383  nanosleep(&t, NULL);
384  }
385 
386  }
387 
388  sd_journal_close(j);
389 
390  DLT_UNREGISTER_CONTEXT(journalContext);
391 
392 }
393 
394 void start_systemd_journal(DltSystemConfiguration *conf)
395 {
396  DLT_LOG(dltsystem, DLT_LOG_DEBUG,
397  DLT_STRING("dlt-system-journal, start journal"));
398  static pthread_attr_t t_attr;
399  static pthread_t pt;
400  pthread_create(&pt, &t_attr, (void *)journal_thread, conf);
401  threads.threads[threads.count++] = pt;
402 }
403 
404 #endif /* DLT_SYSTEMD_JOURNAL_ENABLE */
char * ContextId
Definition: dlt-system.h:108
static char data[kDataSize]
Definition: city-test.cc:40
JournalOptions Journal
Definition: dlt-system.h:156
#define DLT_DECLARE_CONTEXT(CONTEXT)
DltReturnValue dlt_user_check_buffer(int *total_size, int *used_size)
Definition: dlt_user.c:4512
#define DLT_STRING(TEXT)
#define DLT_REGISTER_CONTEXT(CONTEXT, CONTEXTID, DESCRIPTION)
#define DLT_UNREGISTER_CONTEXT(CONTEXT)
#define DLT_LOG(CONTEXT, LOGLEVEL, ARGS...)
DltSystemThreads threads
#define DLT_IMPORT_CONTEXT(CONTEXT)
pthread_t threads[MAX_THREADS]
Definition: dlt-system.h:163
#define NULL
Definition: dlt_common.h:232