systemd-journal.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. /*
  3. * netdata systemd-journal.plugin
  4. * Copyright (C) 2023 Netdata Inc.
  5. * GPL v3+
  6. */
  7. // TODO - 1) MARKDOC
  8. #include "collectors/all.h"
  9. #include "libnetdata/libnetdata.h"
  10. #include "libnetdata/required_dummies.h"
  11. #ifndef SD_JOURNAL_ALL_NAMESPACES
  12. #define JOURNAL_NAMESPACE SD_JOURNAL_LOCAL_ONLY
  13. #else
  14. #define JOURNAL_NAMESPACE SD_JOURNAL_ALL_NAMESPACES
  15. #endif
  16. #include <systemd/sd-journal.h>
  17. #include <syslog.h>
  18. #define FACET_MAX_VALUE_LENGTH 8192
  19. #define SYSTEMD_JOURNAL_FUNCTION_DESCRIPTION "View, search and analyze systemd journal entries."
  20. #define SYSTEMD_JOURNAL_FUNCTION_NAME "systemd-journal"
  21. #define SYSTEMD_JOURNAL_DEFAULT_TIMEOUT 30
  22. #define SYSTEMD_JOURNAL_MAX_PARAMS 100
  23. #define SYSTEMD_JOURNAL_DEFAULT_QUERY_DURATION (3 * 3600)
  24. #define SYSTEMD_JOURNAL_DEFAULT_ITEMS_PER_QUERY 200
  25. #define JOURNAL_PARAMETER_HELP "help"
  26. #define JOURNAL_PARAMETER_AFTER "after"
  27. #define JOURNAL_PARAMETER_BEFORE "before"
  28. #define JOURNAL_PARAMETER_ANCHOR "anchor"
  29. #define JOURNAL_PARAMETER_LAST "last"
  30. #define JOURNAL_PARAMETER_QUERY "query"
  31. #define SYSTEMD_ALWAYS_VISIBLE_KEYS NULL
  32. #define SYSTEMD_KEYS_EXCLUDED_FROM_FACETS NULL
  33. #define SYSTEMD_KEYS_INCLUDED_IN_FACETS \
  34. "_TRANSPORT" \
  35. "|SYSLOG_IDENTIFIER" \
  36. "|SYSLOG_FACILITY" \
  37. "|PRIORITY" \
  38. "|_HOSTNAME" \
  39. "|_RUNTIME_SCOPE" \
  40. "|_PID" \
  41. "|_UID" \
  42. "|_GID" \
  43. "|_SYSTEMD_UNIT" \
  44. "|_SYSTEMD_SLICE" \
  45. "|_SYSTEMD_USER_SLICE" \
  46. "|_COMM" \
  47. "|_EXE" \
  48. "|_SYSTEMD_CGROUP" \
  49. "|_SYSTEMD_USER_UNIT" \
  50. "|USER_UNIT" \
  51. "|UNIT" \
  52. ""
  53. static netdata_mutex_t mutex = NETDATA_MUTEX_INITIALIZER;
  54. static bool plugin_should_exit = false;
  55. DICTIONARY *uids = NULL;
  56. DICTIONARY *gids = NULL;
  57. // ----------------------------------------------------------------------------
  58. int systemd_journal_query(BUFFER *wb, FACETS *facets, usec_t after_ut, usec_t before_ut, usec_t stop_monotonic_ut) {
  59. sd_journal *j;
  60. int r;
  61. // Open the system journal for reading
  62. r = sd_journal_open(&j, JOURNAL_NAMESPACE);
  63. if (r < 0)
  64. return HTTP_RESP_INTERNAL_SERVER_ERROR;
  65. facets_rows_begin(facets);
  66. bool timed_out = false;
  67. size_t row_counter = 0;
  68. sd_journal_seek_realtime_usec(j, before_ut);
  69. SD_JOURNAL_FOREACH_BACKWARDS(j) {
  70. row_counter++;
  71. uint64_t msg_ut;
  72. sd_journal_get_realtime_usec(j, &msg_ut);
  73. if (msg_ut < after_ut)
  74. break;
  75. const void *data;
  76. size_t length;
  77. SD_JOURNAL_FOREACH_DATA(j, data, length) {
  78. const char *key = data;
  79. const char *equal = strchr(key, '=');
  80. if(unlikely(!equal))
  81. continue;
  82. const char *value = ++equal;
  83. size_t key_length = value - key; // including '\0'
  84. char key_copy[key_length];
  85. memcpy(key_copy, key, key_length - 1);
  86. key_copy[key_length - 1] = '\0';
  87. size_t value_length = length - key_length; // without '\0'
  88. facets_add_key_value_length(facets, key_copy, value, value_length <= FACET_MAX_VALUE_LENGTH ? value_length : FACET_MAX_VALUE_LENGTH);
  89. }
  90. facets_row_finished(facets, msg_ut);
  91. if((row_counter % 100) == 0 && now_monotonic_usec() > stop_monotonic_ut) {
  92. timed_out = true;
  93. break;
  94. }
  95. }
  96. sd_journal_close(j);
  97. buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
  98. buffer_json_member_add_boolean(wb, "partial", timed_out);
  99. buffer_json_member_add_string(wb, "type", "table");
  100. buffer_json_member_add_time_t(wb, "update_every", 1);
  101. buffer_json_member_add_string(wb, "help", SYSTEMD_JOURNAL_FUNCTION_DESCRIPTION);
  102. facets_report(facets, wb);
  103. buffer_json_member_add_time_t(wb, "expires", now_realtime_sec());
  104. buffer_json_finalize(wb);
  105. return HTTP_RESP_OK;
  106. }
  107. static void systemd_journal_function_help(const char *transaction) {
  108. pluginsd_function_result_begin_to_stdout(transaction, HTTP_RESP_OK, "text/plain", now_realtime_sec() + 3600);
  109. fprintf(stdout,
  110. "%s / %s\n"
  111. "\n"
  112. "%s\n"
  113. "\n"
  114. "The following filters are supported:\n"
  115. "\n"
  116. " help\n"
  117. " Shows this help message.\n"
  118. "\n"
  119. " before:TIMESTAMP\n"
  120. " Absolute or relative (to now) timestamp in seconds, to start the query.\n"
  121. " The query is always executed from the most recent to the oldest log entry.\n"
  122. " If not given the default is: now.\n"
  123. "\n"
  124. " after:TIMESTAMP\n"
  125. " Absolute or relative (to `before`) timestamp in seconds, to end the query.\n"
  126. " If not given, the default is %d.\n"
  127. "\n"
  128. " last:ITEMS\n"
  129. " The number of items to return.\n"
  130. " The default is %d.\n"
  131. "\n"
  132. " anchor:NUMBER\n"
  133. " The `timestamp` of the item last received, to return log entries after that.\n"
  134. " If not given, the query will return the top `ITEMS` from the most recent.\n"
  135. "\n"
  136. " facet_id:value_id1,value_id2,value_id3,...\n"
  137. " Apply filters to the query, based on the facet IDs returned.\n"
  138. " Each `facet_id` can be given once, but multiple `facet_ids` can be given.\n"
  139. "\n"
  140. "Filters can be combined. Each filter can be given only one time.\n"
  141. , program_name
  142. , SYSTEMD_JOURNAL_FUNCTION_NAME
  143. , SYSTEMD_JOURNAL_FUNCTION_DESCRIPTION
  144. , -SYSTEMD_JOURNAL_DEFAULT_QUERY_DURATION
  145. , SYSTEMD_JOURNAL_DEFAULT_ITEMS_PER_QUERY
  146. );
  147. pluginsd_function_result_end_to_stdout();
  148. }
  149. static const char *syslog_facility_to_name(int facility) {
  150. switch (facility) {
  151. case LOG_FAC(LOG_KERN): return "kern";
  152. case LOG_FAC(LOG_USER): return "user";
  153. case LOG_FAC(LOG_MAIL): return "mail";
  154. case LOG_FAC(LOG_DAEMON): return "daemon";
  155. case LOG_FAC(LOG_AUTH): return "auth";
  156. case LOG_FAC(LOG_SYSLOG): return "syslog";
  157. case LOG_FAC(LOG_LPR): return "lpr";
  158. case LOG_FAC(LOG_NEWS): return "news";
  159. case LOG_FAC(LOG_UUCP): return "uucp";
  160. case LOG_FAC(LOG_CRON): return "cron";
  161. case LOG_FAC(LOG_AUTHPRIV): return "authpriv";
  162. case LOG_FAC(LOG_FTP): return "ftp";
  163. case LOG_FAC(LOG_LOCAL0): return "local0";
  164. case LOG_FAC(LOG_LOCAL1): return "local1";
  165. case LOG_FAC(LOG_LOCAL2): return "local2";
  166. case LOG_FAC(LOG_LOCAL3): return "local3";
  167. case LOG_FAC(LOG_LOCAL4): return "local4";
  168. case LOG_FAC(LOG_LOCAL5): return "local5";
  169. case LOG_FAC(LOG_LOCAL6): return "local6";
  170. case LOG_FAC(LOG_LOCAL7): return "local7";
  171. default: return NULL;
  172. }
  173. }
  174. static const char *syslog_priority_to_name(int priority) {
  175. switch (priority) {
  176. case LOG_ALERT: return "alert";
  177. case LOG_CRIT: return "critical";
  178. case LOG_DEBUG: return "debug";
  179. case LOG_EMERG: return "panic";
  180. case LOG_ERR: return "error";
  181. case LOG_INFO: return "info";
  182. case LOG_NOTICE: return "notice";
  183. case LOG_WARNING: return "warning";
  184. default: return NULL;
  185. }
  186. }
  187. static char *uid_to_username(uid_t uid, char *buffer, size_t buffer_size) {
  188. struct passwd pw, *result;
  189. char tmp[1024 + 1];
  190. if (getpwuid_r(uid, &pw, tmp, 1024, &result) != 0 || result == NULL)
  191. return NULL;
  192. strncpy(buffer, pw.pw_name, buffer_size - 1);
  193. buffer[buffer_size - 1] = '\0'; // Null-terminate just in case
  194. return buffer;
  195. }
  196. static char *gid_to_groupname(gid_t gid, char* buffer, size_t buffer_size) {
  197. struct group grp, *result;
  198. char tmp[1024 + 1];
  199. if (getgrgid_r(gid, &grp, tmp, 1024, &result) != 0 || result == NULL)
  200. return NULL;
  201. strncpy(buffer, grp.gr_name, buffer_size - 1);
  202. buffer[buffer_size - 1] = '\0'; // Null-terminate just in case
  203. return buffer;
  204. }
  205. static void systemd_journal_transform_syslog_facility(FACETS *facets __maybe_unused, BUFFER *wb, void *data __maybe_unused) {
  206. const char *v = buffer_tostring(wb);
  207. if(*v && isdigit(*v)) {
  208. int facility = str2i(buffer_tostring(wb));
  209. const char *name = syslog_facility_to_name(facility);
  210. if (name) {
  211. buffer_flush(wb);
  212. buffer_strcat(wb, name);
  213. }
  214. }
  215. }
  216. static void systemd_journal_transform_priority(FACETS *facets __maybe_unused, BUFFER *wb, void *data __maybe_unused) {
  217. const char *v = buffer_tostring(wb);
  218. if(*v && isdigit(*v)) {
  219. int priority = str2i(buffer_tostring(wb));
  220. const char *name = syslog_priority_to_name(priority);
  221. if (name) {
  222. buffer_flush(wb);
  223. buffer_strcat(wb, name);
  224. }
  225. }
  226. }
  227. static void systemd_journal_transform_uid(FACETS *facets __maybe_unused, BUFFER *wb, void *data) {
  228. DICTIONARY *cache = data;
  229. const char *v = buffer_tostring(wb);
  230. if(*v && isdigit(*v)) {
  231. const char *sv = dictionary_get(cache, v);
  232. if(!sv) {
  233. char buf[1024 + 1];
  234. int uid = str2i(buffer_tostring(wb));
  235. const char *name = uid_to_username(uid, buf, 1024);
  236. if (!name)
  237. name = v;
  238. sv = dictionary_set(cache, v, (void *)name, strlen(name) + 1);
  239. }
  240. buffer_flush(wb);
  241. buffer_strcat(wb, sv);
  242. }
  243. }
  244. static void systemd_journal_transform_gid(FACETS *facets __maybe_unused, BUFFER *wb, void *data) {
  245. DICTIONARY *cache = data;
  246. const char *v = buffer_tostring(wb);
  247. if(*v && isdigit(*v)) {
  248. const char *sv = dictionary_get(cache, v);
  249. if(!sv) {
  250. char buf[1024 + 1];
  251. int gid = str2i(buffer_tostring(wb));
  252. const char *name = gid_to_groupname(gid, buf, 1024);
  253. if (!name)
  254. name = v;
  255. sv = dictionary_set(cache, v, (void *)name, strlen(name) + 1);
  256. }
  257. buffer_flush(wb);
  258. buffer_strcat(wb, sv);
  259. }
  260. }
  261. static void systemd_journal_dynamic_row_id(FACETS *facets __maybe_unused, BUFFER *json_array, FACET_ROW_KEY_VALUE *rkv, FACET_ROW *row, void *data __maybe_unused) {
  262. FACET_ROW_KEY_VALUE *pid_rkv = dictionary_get(row->dict, "_PID");
  263. const char *pid = pid_rkv ? buffer_tostring(pid_rkv->wb) : FACET_VALUE_UNSET;
  264. FACET_ROW_KEY_VALUE *syslog_identifier_rkv = dictionary_get(row->dict, "SYSLOG_IDENTIFIER");
  265. const char *identifier = syslog_identifier_rkv ? buffer_tostring(syslog_identifier_rkv->wb) : FACET_VALUE_UNSET;
  266. if(strcmp(identifier, FACET_VALUE_UNSET) == 0) {
  267. FACET_ROW_KEY_VALUE *comm_rkv = dictionary_get(row->dict, "_COMM");
  268. identifier = comm_rkv ? buffer_tostring(comm_rkv->wb) : FACET_VALUE_UNSET;
  269. }
  270. buffer_flush(rkv->wb);
  271. if(strcmp(pid, FACET_VALUE_UNSET) == 0)
  272. buffer_strcat(rkv->wb, identifier);
  273. else
  274. buffer_sprintf(rkv->wb, "%s[%s]", identifier, pid);
  275. buffer_json_add_array_item_string(json_array, buffer_tostring(rkv->wb));
  276. }
  277. static void function_systemd_journal(const char *transaction, char *function, char *line_buffer __maybe_unused, int line_max __maybe_unused, int timeout __maybe_unused) {
  278. char *words[SYSTEMD_JOURNAL_MAX_PARAMS] = { NULL };
  279. size_t num_words = quoted_strings_splitter_pluginsd(function, words, SYSTEMD_JOURNAL_MAX_PARAMS);
  280. BUFFER *wb = buffer_create(0, NULL);
  281. buffer_flush(wb);
  282. buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS);
  283. FACETS *facets = facets_create(50, 0, FACETS_OPTION_ALL_KEYS_FTS,
  284. SYSTEMD_ALWAYS_VISIBLE_KEYS,
  285. SYSTEMD_KEYS_INCLUDED_IN_FACETS,
  286. SYSTEMD_KEYS_EXCLUDED_FROM_FACETS);
  287. facets_accepted_param(facets, JOURNAL_PARAMETER_AFTER);
  288. facets_accepted_param(facets, JOURNAL_PARAMETER_BEFORE);
  289. facets_accepted_param(facets, JOURNAL_PARAMETER_ANCHOR);
  290. facets_accepted_param(facets, JOURNAL_PARAMETER_LAST);
  291. facets_accepted_param(facets, JOURNAL_PARAMETER_QUERY);
  292. // register the fields in the order you want them on the dashboard
  293. facets_register_dynamic_key(facets, "ND_JOURNAL_PROCESS", FACET_KEY_OPTION_NO_FACET|FACET_KEY_OPTION_VISIBLE|FACET_KEY_OPTION_FTS,
  294. systemd_journal_dynamic_row_id, NULL);
  295. facets_register_key(facets, "MESSAGE",
  296. FACET_KEY_OPTION_NO_FACET|FACET_KEY_OPTION_MAIN_TEXT|FACET_KEY_OPTION_VISIBLE|FACET_KEY_OPTION_FTS);
  297. facets_register_key_transformation(facets, "PRIORITY", FACET_KEY_OPTION_FACET|FACET_KEY_OPTION_FTS,
  298. systemd_journal_transform_priority, NULL);
  299. facets_register_key_transformation(facets, "SYSLOG_FACILITY", FACET_KEY_OPTION_FACET|FACET_KEY_OPTION_FTS,
  300. systemd_journal_transform_syslog_facility, NULL);
  301. facets_register_key(facets, "SYSLOG_IDENTIFIER", FACET_KEY_OPTION_FACET|FACET_KEY_OPTION_FTS);
  302. facets_register_key(facets, "UNIT", FACET_KEY_OPTION_FACET|FACET_KEY_OPTION_FTS);
  303. facets_register_key(facets, "USER_UNIT", FACET_KEY_OPTION_FACET|FACET_KEY_OPTION_FTS);
  304. facets_register_key_transformation(facets, "_UID", FACET_KEY_OPTION_FACET|FACET_KEY_OPTION_FTS,
  305. systemd_journal_transform_uid, uids);
  306. facets_register_key_transformation(facets, "_GID", FACET_KEY_OPTION_FACET|FACET_KEY_OPTION_FTS,
  307. systemd_journal_transform_gid, gids);
  308. time_t after_s = 0, before_s = 0;
  309. usec_t anchor = 0;
  310. size_t last = 0;
  311. const char *query = NULL;
  312. buffer_json_member_add_object(wb, "request");
  313. buffer_json_member_add_object(wb, "filters");
  314. for(int i = 1; i < SYSTEMD_JOURNAL_MAX_PARAMS ;i++) {
  315. const char *keyword = get_word(words, num_words, i);
  316. if(!keyword) break;
  317. if(strcmp(keyword, JOURNAL_PARAMETER_HELP) == 0) {
  318. systemd_journal_function_help(transaction);
  319. goto cleanup;
  320. }
  321. else if(strncmp(keyword, JOURNAL_PARAMETER_AFTER ":", strlen(JOURNAL_PARAMETER_AFTER ":")) == 0) {
  322. after_s = str2l(&keyword[strlen(JOURNAL_PARAMETER_AFTER ":")]);
  323. }
  324. else if(strncmp(keyword, JOURNAL_PARAMETER_BEFORE ":", strlen(JOURNAL_PARAMETER_BEFORE ":")) == 0) {
  325. before_s = str2l(&keyword[strlen(JOURNAL_PARAMETER_BEFORE ":")]);
  326. }
  327. else if(strncmp(keyword, JOURNAL_PARAMETER_ANCHOR ":", strlen(JOURNAL_PARAMETER_ANCHOR ":")) == 0) {
  328. anchor = str2ull(&keyword[strlen(JOURNAL_PARAMETER_ANCHOR ":")], NULL);
  329. }
  330. else if(strncmp(keyword, JOURNAL_PARAMETER_LAST ":", strlen(JOURNAL_PARAMETER_LAST ":")) == 0) {
  331. last = str2ul(&keyword[strlen(JOURNAL_PARAMETER_LAST ":")]);
  332. }
  333. else if(strncmp(keyword, JOURNAL_PARAMETER_QUERY ":", strlen(JOURNAL_PARAMETER_QUERY ":")) == 0) {
  334. query= &keyword[strlen(JOURNAL_PARAMETER_QUERY ":")];
  335. }
  336. else {
  337. char *value = strchr(keyword, ':');
  338. if(value) {
  339. *value++ = '\0';
  340. buffer_json_member_add_array(wb, keyword);
  341. while(value) {
  342. char *sep = strchr(value, ',');
  343. if(sep)
  344. *sep++ = '\0';
  345. facets_register_facet_filter(facets, keyword, value, FACET_KEY_OPTION_REORDER);
  346. buffer_json_add_array_item_string(wb, value);
  347. value = sep;
  348. }
  349. buffer_json_array_close(wb); // keyword
  350. }
  351. }
  352. }
  353. buffer_json_object_close(wb); // filters
  354. time_t expires = now_realtime_sec() + 1;
  355. time_t now_s;
  356. if(!after_s && !before_s) {
  357. now_s = now_realtime_sec();
  358. before_s = now_s;
  359. after_s = before_s - SYSTEMD_JOURNAL_DEFAULT_QUERY_DURATION;
  360. }
  361. else
  362. rrdr_relative_window_to_absolute(&after_s, &before_s, &now_s, false);
  363. if(after_s > before_s) {
  364. time_t tmp = after_s;
  365. after_s = before_s;
  366. before_s = tmp;
  367. }
  368. if(after_s == before_s)
  369. after_s = before_s - SYSTEMD_JOURNAL_DEFAULT_QUERY_DURATION;
  370. if(!last)
  371. last = SYSTEMD_JOURNAL_DEFAULT_ITEMS_PER_QUERY;
  372. buffer_json_member_add_time_t(wb, "after", after_s);
  373. buffer_json_member_add_time_t(wb, "before", before_s);
  374. buffer_json_member_add_uint64(wb, "anchor", anchor);
  375. buffer_json_member_add_uint64(wb, "last", last);
  376. buffer_json_member_add_string(wb, "query", query);
  377. buffer_json_member_add_time_t(wb, "timeout", timeout);
  378. buffer_json_object_close(wb); // request
  379. facets_set_items(facets, last);
  380. facets_set_anchor(facets, anchor);
  381. facets_set_query(facets, query);
  382. int response = systemd_journal_query(wb, facets, after_s * USEC_PER_SEC, before_s * USEC_PER_SEC,
  383. now_monotonic_usec() + (timeout - 1) * USEC_PER_SEC);
  384. if(response != HTTP_RESP_OK) {
  385. pluginsd_function_json_error(transaction, response, "failed");
  386. goto cleanup;
  387. }
  388. pluginsd_function_result_begin_to_stdout(transaction, HTTP_RESP_OK, "application/json", expires);
  389. fwrite(buffer_tostring(wb), buffer_strlen(wb), 1, stdout);
  390. pluginsd_function_result_end_to_stdout();
  391. cleanup:
  392. facets_destroy(facets);
  393. buffer_free(wb);
  394. }
  395. static void *reader_main(void *arg __maybe_unused) {
  396. char buffer[PLUGINSD_LINE_MAX + 1];
  397. char *s = NULL;
  398. while(!plugin_should_exit && (s = fgets(buffer, PLUGINSD_LINE_MAX, stdin))) {
  399. char *words[PLUGINSD_MAX_WORDS] = { NULL };
  400. size_t num_words = quoted_strings_splitter_pluginsd(buffer, words, PLUGINSD_MAX_WORDS);
  401. const char *keyword = get_word(words, num_words, 0);
  402. if(keyword && strcmp(keyword, PLUGINSD_KEYWORD_FUNCTION) == 0) {
  403. char *transaction = get_word(words, num_words, 1);
  404. char *timeout_s = get_word(words, num_words, 2);
  405. char *function = get_word(words, num_words, 3);
  406. if(!transaction || !*transaction || !timeout_s || !*timeout_s || !function || !*function) {
  407. netdata_log_error("Received incomplete %s (transaction = '%s', timeout = '%s', function = '%s'). Ignoring it.",
  408. keyword,
  409. transaction?transaction:"(unset)",
  410. timeout_s?timeout_s:"(unset)",
  411. function?function:"(unset)");
  412. }
  413. else {
  414. int timeout = str2i(timeout_s);
  415. if(timeout <= 0) timeout = SYSTEMD_JOURNAL_DEFAULT_TIMEOUT;
  416. netdata_mutex_lock(&mutex);
  417. if(strncmp(function, SYSTEMD_JOURNAL_FUNCTION_NAME, strlen(SYSTEMD_JOURNAL_FUNCTION_NAME)) == 0)
  418. function_systemd_journal(transaction, function, buffer, PLUGINSD_LINE_MAX + 1, timeout);
  419. else
  420. pluginsd_function_json_error(transaction, HTTP_RESP_NOT_FOUND, "No function with this name found in systemd-journal.plugin.");
  421. fflush(stdout);
  422. netdata_mutex_unlock(&mutex);
  423. }
  424. }
  425. else
  426. netdata_log_error("Received unknown command: %s", keyword?keyword:"(unset)");
  427. }
  428. if(!s || feof(stdin) || ferror(stdin)) {
  429. plugin_should_exit = true;
  430. netdata_log_error("Received error on stdin.");
  431. }
  432. exit(1);
  433. }
  434. int main(int argc __maybe_unused, char **argv __maybe_unused) {
  435. stderror = stderr;
  436. clocks_init();
  437. program_name = "systemd-journal.plugin";
  438. // disable syslog
  439. error_log_syslog = 0;
  440. // set errors flood protection to 100 logs per hour
  441. error_log_errors_per_period = 100;
  442. error_log_throttle_period = 3600;
  443. // initialize the threads
  444. netdata_threads_init_for_external_plugins(0); // set the default threads stack size here
  445. uids = dictionary_create(0);
  446. gids = dictionary_create(0);
  447. // ------------------------------------------------------------------------
  448. // debug
  449. if(argc == 2 && strcmp(argv[1], "debug") == 0) {
  450. char buf[] = "systemd-journal after:-86400 before:0 last:500";
  451. function_systemd_journal("123", buf, "", 0, 30);
  452. exit(1);
  453. }
  454. // ------------------------------------------------------------------------
  455. netdata_thread_t reader_thread;
  456. netdata_thread_create(&reader_thread, "SDJ_READER", NETDATA_THREAD_OPTION_DONT_LOG, reader_main, NULL);
  457. // ------------------------------------------------------------------------
  458. time_t started_t = now_monotonic_sec();
  459. size_t iteration;
  460. usec_t step = 1000 * USEC_PER_MS;
  461. bool tty = isatty(fileno(stderr)) == 1;
  462. netdata_mutex_lock(&mutex);
  463. fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\"\n",
  464. SYSTEMD_JOURNAL_FUNCTION_NAME, SYSTEMD_JOURNAL_DEFAULT_TIMEOUT, SYSTEMD_JOURNAL_FUNCTION_DESCRIPTION);
  465. heartbeat_t hb;
  466. heartbeat_init(&hb);
  467. for(iteration = 0; 1 ; iteration++) {
  468. netdata_mutex_unlock(&mutex);
  469. heartbeat_next(&hb, step);
  470. netdata_mutex_lock(&mutex);
  471. if(!tty)
  472. fprintf(stdout, "\n");
  473. fflush(stdout);
  474. time_t now = now_monotonic_sec();
  475. if(now - started_t > 86400)
  476. break;
  477. }
  478. dictionary_destroy(uids);
  479. dictionary_destroy(gids);
  480. exit(0);
  481. }