master
c 2,266 lines 75.9 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "collectors/all.h"
4 #include "libnetdata/libnetdata.h"
5
6 #include <linux/capability.h>
7 #include <syslog.h>
8 #include <systemd/sd-bus.h>
9
10 #define ND_SD_JOURNAL_WORKER_THREADS 2
11 #define ND_SD_UNITS_FUNCTION_DESCRIPTION "View the status of systemd units"
12 #define ND_SD_UNITS_FUNCTION_NAME "systemd-list-units"
13 #define ND_SD_UNITS_DEFAULT_TIMEOUT 30
14
15 #define ND_SD_UNITS_MAX_PARAMS 10
16 #define ND_SD_UNITS_DBUS_TYPES "(ssssssouso)"
17
18 netdata_mutex_t stdout_mutex;
19
20 static void __attribute__((constructor)) init_mutex(void) {
21 netdata_mutex_init(&stdout_mutex);
22 }
23
24 static void __attribute__((destructor)) destroy_mutex(void) {
25 netdata_mutex_destroy(&stdout_mutex);
26 }
27
28 // ----------------------------------------------------------------------------
29 // copied from systemd: string-table.h
30
31 typedef char sd_char;
32 #define XCONCATENATE(x, y) x##y
33 #define CONCATENATE(x, y) XCONCATENATE(x, y)
34
35 #ifndef __COVERITY__
36 #define VOID_0 ((void)0)
37 #else
38 #define VOID_0 ((void *)0)
39 #endif
40
41 #define ELEMENTSOF(x) \
42 (__builtin_choose_expr(!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), sizeof(x) / sizeof((x)[0]), VOID_0))
43
44 #define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
45 #define UNIQ __COUNTER__
46 #define __CMP(aq, a, bq, b) \
47 ({ \
48 const typeof(a) UNIQ_T(A, aq) = (a); \
49 const typeof(b) UNIQ_T(B, bq) = (b); \
50 UNIQ_T(A, aq)<UNIQ_T(B, bq) ? -1 : UNIQ_T(A, aq)> UNIQ_T(B, bq) ? 1 : 0; \
51 })
52 #define CMP(a, b) __CMP(UNIQ, (a), UNIQ, (b))
53
54 static inline int strcmp_ptr(const sd_char *a, const sd_char *b)
55 {
56 if (a && b)
57 return strcmp(a, b);
58
59 return CMP(a, b);
60 }
61
62 static inline bool streq_ptr(const sd_char *a, const sd_char *b)
63 {
64 return strcmp_ptr(a, b) == 0;
65 }
66
67 static ssize_t string_table_lookup(const char *const *table, size_t len, const char *key)
68 {
69 if (!key || !*key)
70 return -EINVAL;
71
72 for (size_t i = 0; i < len; ++i)
73 if (streq_ptr(table[i], key))
74 return (ssize_t)i;
75
76 return -EINVAL;
77 }
78
79 /* For basic lookup tables with strictly enumerated entries */
80 #define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name, type, scope) \
81 scope const char *name##_to_string(type i) \
82 { \
83 if (i < 0 || i >= (type)ELEMENTSOF(name##_table)) \
84 return NULL; \
85 return name##_table[i]; \
86 }
87
88 #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type, scope) \
89 scope type name##_from_string(const char *s) \
90 { \
91 return (type)string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
92 }
93
94 #define _DEFINE_STRING_TABLE_LOOKUP(name, type, scope) \
95 _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name, type, scope) \
96 _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type, scope)
97
98 #define DEFINE_STRING_TABLE_LOOKUP(name, type) _DEFINE_STRING_TABLE_LOOKUP(name, type, )
99
100 // ----------------------------------------------------------------------------
101 // copied from systemd: unit-def.h
102
103 typedef enum UnitType {
104 UNIT_SERVICE,
105 UNIT_MOUNT,
106 UNIT_SWAP,
107 UNIT_SOCKET,
108 UNIT_TARGET,
109 UNIT_DEVICE,
110 UNIT_AUTOMOUNT,
111 UNIT_TIMER,
112 UNIT_PATH,
113 UNIT_SLICE,
114 UNIT_SCOPE,
115 _UNIT_TYPE_MAX,
116 _UNIT_TYPE_INVALID = -EINVAL,
117 } UnitType;
118
119 typedef enum UnitLoadState {
120 UNIT_STUB,
121 UNIT_LOADED,
122 UNIT_NOT_FOUND, /* error condition #1: unit file not found */
123 UNIT_BAD_SETTING, /* error condition #2: we couldn't parse some essential unit file setting */
124 UNIT_ERROR, /* error condition #3: other "system" error, catchall for the rest */
125 UNIT_MERGED,
126 UNIT_MASKED,
127 _UNIT_LOAD_STATE_MAX,
128 _UNIT_LOAD_STATE_INVALID = -EINVAL,
129 } UnitLoadState;
130
131 typedef enum UnitActiveState {
132 UNIT_ACTIVE,
133 UNIT_RELOADING,
134 UNIT_INACTIVE,
135 UNIT_FAILED,
136 UNIT_ACTIVATING,
137 UNIT_DEACTIVATING,
138 UNIT_MAINTENANCE,
139 _UNIT_ACTIVE_STATE_MAX,
140 _UNIT_ACTIVE_STATE_INVALID = -EINVAL,
141 } UnitActiveState;
142
143 typedef enum AutomountState {
144 AUTOMOUNT_DEAD,
145 AUTOMOUNT_WAITING,
146 AUTOMOUNT_RUNNING,
147 AUTOMOUNT_FAILED,
148 _AUTOMOUNT_STATE_MAX,
149 _AUTOMOUNT_STATE_INVALID = -EINVAL,
150 } AutomountState;
151
152 typedef enum DeviceState {
153 DEVICE_DEAD,
154 DEVICE_TENTATIVE, /* mounted or swapped, but not (yet) announced by udev */
155 DEVICE_PLUGGED, /* announced by udev */
156 _DEVICE_STATE_MAX,
157 _DEVICE_STATE_INVALID = -EINVAL,
158 } DeviceState;
159
160 typedef enum MountState {
161 MOUNT_DEAD,
162 MOUNT_MOUNTING, /* /usr/bin/mount is running, but the mount is not done yet. */
163 MOUNT_MOUNTING_DONE, /* /usr/bin/mount is running, and the mount is done. */
164 MOUNT_MOUNTED,
165 MOUNT_REMOUNTING,
166 MOUNT_UNMOUNTING,
167 MOUNT_REMOUNTING_SIGTERM,
168 MOUNT_REMOUNTING_SIGKILL,
169 MOUNT_UNMOUNTING_SIGTERM,
170 MOUNT_UNMOUNTING_SIGKILL,
171 MOUNT_FAILED,
172 MOUNT_CLEANING,
173 _MOUNT_STATE_MAX,
174 _MOUNT_STATE_INVALID = -EINVAL,
175 } MountState;
176
177 typedef enum PathState {
178 PATH_DEAD,
179 PATH_WAITING,
180 PATH_RUNNING,
181 PATH_FAILED,
182 _PATH_STATE_MAX,
183 _PATH_STATE_INVALID = -EINVAL,
184 } PathState;
185
186 typedef enum ScopeState {
187 SCOPE_DEAD,
188 SCOPE_START_CHOWN,
189 SCOPE_RUNNING,
190 SCOPE_ABANDONED,
191 SCOPE_STOP_SIGTERM,
192 SCOPE_STOP_SIGKILL,
193 SCOPE_FAILED,
194 _SCOPE_STATE_MAX,
195 _SCOPE_STATE_INVALID = -EINVAL,
196 } ScopeState;
197
198 typedef enum ServiceState {
199 SERVICE_DEAD,
200 SERVICE_CONDITION,
201 SERVICE_START_PRE,
202 SERVICE_START,
203 SERVICE_START_POST,
204 SERVICE_RUNNING,
205 SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
206 SERVICE_RELOAD, /* Reloading via ExecReload= */
207 SERVICE_RELOAD_SIGNAL, /* Reloading via SIGHUP requested */
208 SERVICE_RELOAD_NOTIFY, /* Waiting for READY=1 after RELOADING=1 notify */
209 SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */
210 SERVICE_STOP_WATCHDOG,
211 SERVICE_STOP_SIGTERM,
212 SERVICE_STOP_SIGKILL,
213 SERVICE_STOP_POST,
214 SERVICE_FINAL_WATCHDOG, /* In case the STOP_POST executable needs to be aborted. */
215 SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */
216 SERVICE_FINAL_SIGKILL,
217 SERVICE_FAILED,
218 SERVICE_DEAD_BEFORE_AUTO_RESTART,
219 SERVICE_FAILED_BEFORE_AUTO_RESTART,
220 SERVICE_DEAD_RESOURCES_PINNED, /* Like SERVICE_DEAD, but with pinned resources */
221 SERVICE_AUTO_RESTART,
222 SERVICE_AUTO_RESTART_QUEUED,
223 SERVICE_CLEANING,
224 _SERVICE_STATE_MAX,
225 _SERVICE_STATE_INVALID = -EINVAL,
226 } ServiceState;
227
228 typedef enum SliceState {
229 SLICE_DEAD,
230 SLICE_ACTIVE,
231 _SLICE_STATE_MAX,
232 _SLICE_STATE_INVALID = -EINVAL,
233 } SliceState;
234
235 typedef enum SocketState {
236 SOCKET_DEAD,
237 SOCKET_START_PRE,
238 SOCKET_START_CHOWN,
239 SOCKET_START_POST,
240 SOCKET_LISTENING,
241 SOCKET_RUNNING,
242 SOCKET_STOP_PRE,
243 SOCKET_STOP_PRE_SIGTERM,
244 SOCKET_STOP_PRE_SIGKILL,
245 SOCKET_STOP_POST,
246 SOCKET_FINAL_SIGTERM,
247 SOCKET_FINAL_SIGKILL,
248 SOCKET_FAILED,
249 SOCKET_CLEANING,
250 _SOCKET_STATE_MAX,
251 _SOCKET_STATE_INVALID = -EINVAL,
252 } SocketState;
253
254 typedef enum SwapState {
255 SWAP_DEAD,
256 SWAP_ACTIVATING, /* /sbin/swapon is running, but the swap not yet enabled. */
257 SWAP_ACTIVATING_DONE, /* /sbin/swapon is running, and the swap is done. */
258 SWAP_ACTIVE,
259 SWAP_DEACTIVATING,
260 SWAP_DEACTIVATING_SIGTERM,
261 SWAP_DEACTIVATING_SIGKILL,
262 SWAP_FAILED,
263 SWAP_CLEANING,
264 _SWAP_STATE_MAX,
265 _SWAP_STATE_INVALID = -EINVAL,
266 } SwapState;
267
268 typedef enum TargetState {
269 TARGET_DEAD,
270 TARGET_ACTIVE,
271 _TARGET_STATE_MAX,
272 _TARGET_STATE_INVALID = -EINVAL,
273 } TargetState;
274
275 typedef enum TimerState {
276 TIMER_DEAD,
277 TIMER_WAITING,
278 TIMER_RUNNING,
279 TIMER_ELAPSED,
280 TIMER_FAILED,
281 _TIMER_STATE_MAX,
282 _TIMER_STATE_INVALID = -EINVAL,
283 } TimerState;
284
285 typedef enum FreezerState {
286 FREEZER_RUNNING,
287 FREEZER_FREEZING,
288 FREEZER_FROZEN,
289 FREEZER_THAWING,
290 _FREEZER_STATE_MAX,
291 _FREEZER_STATE_INVALID = -EINVAL,
292 } FreezerState;
293
294 // ----------------------------------------------------------------------------
295 // copied from systemd: unit-def.c
296
297 static const char *const unit_type_table[_UNIT_TYPE_MAX] = {
298 [UNIT_SERVICE] = "service",
299 [UNIT_SOCKET] = "socket",
300 [UNIT_TARGET] = "target",
301 [UNIT_DEVICE] = "device",
302 [UNIT_MOUNT] = "mount",
303 [UNIT_AUTOMOUNT] = "automount",
304 [UNIT_SWAP] = "swap",
305 [UNIT_TIMER] = "timer",
306 [UNIT_PATH] = "path",
307 [UNIT_SLICE] = "slice",
308 [UNIT_SCOPE] = "scope",
309 };
310
311 DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
312
313 static const char *const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
314 [UNIT_STUB] = "stub",
315 [UNIT_LOADED] = "loaded",
316 [UNIT_NOT_FOUND] = "not-found",
317 [UNIT_BAD_SETTING] = "bad-setting",
318 [UNIT_ERROR] = "error",
319 [UNIT_MERGED] = "merged",
320 [UNIT_MASKED] = "masked"};
321
322 DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState);
323
324 static const char *const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
325 [UNIT_ACTIVE] = "active",
326 [UNIT_RELOADING] = "reloading",
327 [UNIT_INACTIVE] = "inactive",
328 [UNIT_FAILED] = "failed",
329 [UNIT_ACTIVATING] = "activating",
330 [UNIT_DEACTIVATING] = "deactivating",
331 [UNIT_MAINTENANCE] = "maintenance",
332 };
333
334 DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
335
336 static const char *const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
337 [AUTOMOUNT_DEAD] = "dead",
338 [AUTOMOUNT_WAITING] = "waiting",
339 [AUTOMOUNT_RUNNING] = "running",
340 [AUTOMOUNT_FAILED] = "failed"};
341
342 DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
343
344 static const char *const device_state_table[_DEVICE_STATE_MAX] = {
345 [DEVICE_DEAD] = "dead",
346 [DEVICE_TENTATIVE] = "tentative",
347 [DEVICE_PLUGGED] = "plugged",
348 };
349
350 DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
351
352 static const char *const mount_state_table[_MOUNT_STATE_MAX] = {
353 [MOUNT_DEAD] = "dead",
354 [MOUNT_MOUNTING] = "mounting",
355 [MOUNT_MOUNTING_DONE] = "mounting-done",
356 [MOUNT_MOUNTED] = "mounted",
357 [MOUNT_REMOUNTING] = "remounting",
358 [MOUNT_UNMOUNTING] = "unmounting",
359 [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
360 [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
361 [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
362 [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
363 [MOUNT_FAILED] = "failed",
364 [MOUNT_CLEANING] = "cleaning",
365 };
366
367 DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
368
369 static const char *const path_state_table[_PATH_STATE_MAX] =
370 {[PATH_DEAD] = "dead", [PATH_WAITING] = "waiting", [PATH_RUNNING] = "running", [PATH_FAILED] = "failed"};
371
372 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
373
374 static const char *const scope_state_table[_SCOPE_STATE_MAX] = {
375 [SCOPE_DEAD] = "dead",
376 [SCOPE_START_CHOWN] = "start-chown",
377 [SCOPE_RUNNING] = "running",
378 [SCOPE_ABANDONED] = "abandoned",
379 [SCOPE_STOP_SIGTERM] = "stop-sigterm",
380 [SCOPE_STOP_SIGKILL] = "stop-sigkill",
381 [SCOPE_FAILED] = "failed",
382 };
383
384 DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
385
386 static const char *const service_state_table[_SERVICE_STATE_MAX] = {
387 [SERVICE_DEAD] = "dead",
388 [SERVICE_CONDITION] = "condition",
389 [SERVICE_START_PRE] = "start-pre",
390 [SERVICE_START] = "start",
391 [SERVICE_START_POST] = "start-post",
392 [SERVICE_RUNNING] = "running",
393 [SERVICE_EXITED] = "exited",
394 [SERVICE_RELOAD] = "reload",
395 [SERVICE_RELOAD_SIGNAL] = "reload-signal",
396 [SERVICE_RELOAD_NOTIFY] = "reload-notify",
397 [SERVICE_STOP] = "stop",
398 [SERVICE_STOP_WATCHDOG] = "stop-watchdog",
399 [SERVICE_STOP_SIGTERM] = "stop-sigterm",
400 [SERVICE_STOP_SIGKILL] = "stop-sigkill",
401 [SERVICE_STOP_POST] = "stop-post",
402 [SERVICE_FINAL_WATCHDOG] = "final-watchdog",
403 [SERVICE_FINAL_SIGTERM] = "final-sigterm",
404 [SERVICE_FINAL_SIGKILL] = "final-sigkill",
405 [SERVICE_FAILED] = "failed",
406 [SERVICE_DEAD_BEFORE_AUTO_RESTART] = "dead-before-auto-restart",
407 [SERVICE_FAILED_BEFORE_AUTO_RESTART] = "failed-before-auto-restart",
408 [SERVICE_DEAD_RESOURCES_PINNED] = "dead-resources-pinned",
409 [SERVICE_AUTO_RESTART] = "auto-restart",
410 [SERVICE_AUTO_RESTART_QUEUED] = "auto-restart-queued",
411 [SERVICE_CLEANING] = "cleaning",
412 };
413
414 DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
415
416 static const char *const slice_state_table[_SLICE_STATE_MAX] = {[SLICE_DEAD] = "dead", [SLICE_ACTIVE] = "active"};
417
418 DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
419
420 static const char *const socket_state_table[_SOCKET_STATE_MAX] = {
421 [SOCKET_DEAD] = "dead",
422 [SOCKET_START_PRE] = "start-pre",
423 [SOCKET_START_CHOWN] = "start-chown",
424 [SOCKET_START_POST] = "start-post",
425 [SOCKET_LISTENING] = "listening",
426 [SOCKET_RUNNING] = "running",
427 [SOCKET_STOP_PRE] = "stop-pre",
428 [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
429 [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
430 [SOCKET_STOP_POST] = "stop-post",
431 [SOCKET_FINAL_SIGTERM] = "final-sigterm",
432 [SOCKET_FINAL_SIGKILL] = "final-sigkill",
433 [SOCKET_FAILED] = "failed",
434 [SOCKET_CLEANING] = "cleaning",
435 };
436
437 DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
438
439 static const char *const swap_state_table[_SWAP_STATE_MAX] = {
440 [SWAP_DEAD] = "dead",
441 [SWAP_ACTIVATING] = "activating",
442 [SWAP_ACTIVATING_DONE] = "activating-done",
443 [SWAP_ACTIVE] = "active",
444 [SWAP_DEACTIVATING] = "deactivating",
445 [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
446 [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
447 [SWAP_FAILED] = "failed",
448 [SWAP_CLEANING] = "cleaning",
449 };
450
451 DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
452
453 static const char *const target_state_table[_TARGET_STATE_MAX] = {[TARGET_DEAD] = "dead", [TARGET_ACTIVE] = "active"};
454
455 DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
456
457 static const char *const timer_state_table[_TIMER_STATE_MAX] = {
458 [TIMER_DEAD] = "dead",
459 [TIMER_WAITING] = "waiting",
460 [TIMER_RUNNING] = "running",
461 [TIMER_ELAPSED] = "elapsed",
462 [TIMER_FAILED] = "failed"};
463
464 DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
465
466 static const char *const freezer_state_table[_FREEZER_STATE_MAX] = {
467 [FREEZER_RUNNING] = "running",
468 [FREEZER_FREEZING] = "freezing",
469 [FREEZER_FROZEN] = "frozen",
470 [FREEZER_THAWING] = "thawing",
471 };
472
473 DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState);
474
475 // ----------------------------------------------------------------------------
476 // our code
477
478 typedef struct UnitAttribute {
479 union {
480 int boolean;
481 char *str;
482 uint64_t uint64;
483 int64_t int64;
484 uint32_t uint32;
485 int32_t int32;
486 double dbl;
487 };
488 } UnitAttribute;
489
490 struct UnitInfo;
491 typedef void (*attribute_handler_t)(struct UnitInfo *u, UnitAttribute *ua);
492
493 static void update_freezer_state(struct UnitInfo *u, UnitAttribute *ua);
494
495 static const struct {
496 const char *member;
497 char value_type;
498
499 const char *show_as;
500 const char *info;
501 RRDF_FIELD_OPTIONS options;
502 RRDF_FIELD_FILTER filter;
503
504 attribute_handler_t handler;
505 } unit_attributes[] = {
506 {
507 .member = "Type",
508 .value_type = SD_BUS_TYPE_STRING,
509 .show_as = "ServiceType",
510 .info = "Service Type",
511 .options = RRDF_FIELD_OPTS_VISIBLE,
512 .filter = RRDF_FIELD_FILTER_MULTISELECT,
513 },
514 {
515 .member = "Result",
516 .value_type = SD_BUS_TYPE_STRING,
517 .show_as = "Result",
518 .info = "Result",
519 .options = RRDF_FIELD_OPTS_VISIBLE,
520 .filter = RRDF_FIELD_FILTER_MULTISELECT,
521 },
522 {
523 .member = "UnitFileState",
524 .value_type = SD_BUS_TYPE_STRING,
525 .show_as = "Enabled",
526 .info = "Unit File State",
527 .options = RRDF_FIELD_OPTS_NONE,
528 .filter = RRDF_FIELD_FILTER_MULTISELECT,
529 },
530 {
531 .member = "UnitFilePreset",
532 .value_type = SD_BUS_TYPE_STRING,
533 .show_as = "Preset",
534 .info = "Unit File Preset",
535 .options = RRDF_FIELD_OPTS_NONE,
536 .filter = RRDF_FIELD_FILTER_MULTISELECT,
537 },
538 {
539 .member = "FreezerState",
540 .value_type = SD_BUS_TYPE_STRING,
541 .show_as = "FreezerState",
542 .info = "Freezer State",
543 .options = RRDF_FIELD_OPTS_NONE,
544 .filter = RRDF_FIELD_FILTER_MULTISELECT,
545 .handler = update_freezer_state,
546 },
547 // { .member = "Id", .signature = "s", },
548 // { .member = "LoadState", .signature = "s", },
549 // { .member = "ActiveState", .signature = "s", },
550 // { .member = "SubState", .signature = "s", },
551 // { .member = "Description", .signature = "s", },
552 // { .member = "Following", .signature = "s", },
553 // { .member = "Documentation", .signature = "as", },
554 // { .member = "FragmentPath", .signature = "s", },
555 // { .member = "SourcePath", .signature = "s", },
556 // { .member = "ControlGroup", .signature = "s", },
557 // { .member = "DropInPaths", .signature = "as", },
558 // { .member = "LoadError", .signature = "(ss)", },
559 // { .member = "TriggeredBy", .signature = "as", },
560 // { .member = "Triggers", .signature = "as", },
561 // { .member = "InactiveExitTimestamp", .signature = "t", },
562 // { .member = "InactiveExitTimestampMonotonic", .signature = "t", },
563 // { .member = "ActiveEnterTimestamp", .signature = "t", },
564 // { .member = "ActiveExitTimestamp", .signature = "t", },
565 // { .member = "RuntimeMaxUSec", .signature = "t", },
566 // { .member = "InactiveEnterTimestamp", .signature = "t", },
567 // { .member = "NeedDaemonReload", .signature = "b", },
568 // { .member = "Transient", .signature = "b", },
569 // { .member = "ExecMainPID", .signature = "u", },
570 // { .member = "MainPID", .signature = "u", },
571 // { .member = "ControlPID", .signature = "u", },
572 // { .member = "StatusText", .signature = "s", },
573 // { .member = "PIDFile", .signature = "s", },
574 // { .member = "StatusErrno", .signature = "i", },
575 // { .member = "FileDescriptorStoreMax", .signature = "u", },
576 // { .member = "NFileDescriptorStore", .signature = "u", },
577 // { .member = "ExecMainStartTimestamp", .signature = "t", },
578 // { .member = "ExecMainExitTimestamp", .signature = "t", },
579 // { .member = "ExecMainCode", .signature = "i", },
580 // { .member = "ExecMainStatus", .signature = "i", },
581 // { .member = "LogNamespace", .signature = "s", },
582 // { .member = "ConditionTimestamp", .signature = "t", },
583 // { .member = "ConditionResult", .signature = "b", },
584 // { .member = "Conditions", .signature = "a(sbbsi)", },
585 // { .member = "AssertTimestamp", .signature = "t", },
586 // { .member = "AssertResult", .signature = "b", },
587 // { .member = "Asserts", .signature = "a(sbbsi)", },
588 // { .member = "NextElapseUSecRealtime", .signature = "t", },
589 // { .member = "NextElapseUSecMonotonic", .signature = "t", },
590 // { .member = "NAccepted", .signature = "u", },
591 // { .member = "NConnections", .signature = "u", },
592 // { .member = "NRefused", .signature = "u", },
593 // { .member = "Accept", .signature = "b", },
594 // { .member = "Listen", .signature = "a(ss)", },
595 // { .member = "SysFSPath", .signature = "s", },
596 // { .member = "Where", .signature = "s", },
597 // { .member = "What", .signature = "s", },
598 // { .member = "MemoryCurrent", .signature = "t", },
599 // { .member = "MemoryAvailable", .signature = "t", },
600 // { .member = "DefaultMemoryMin", .signature = "t", },
601 // { .member = "DefaultMemoryLow", .signature = "t", },
602 // { .member = "DefaultStartupMemoryLow", .signature = "t", },
603 // { .member = "MemoryMin", .signature = "t", },
604 // { .member = "MemoryLow", .signature = "t", },
605 // { .member = "StartupMemoryLow", .signature = "t", },
606 // { .member = "MemoryHigh", .signature = "t", },
607 // { .member = "StartupMemoryHigh", .signature = "t", },
608 // { .member = "MemoryMax", .signature = "t", },
609 // { .member = "StartupMemoryMax", .signature = "t", },
610 // { .member = "MemorySwapMax", .signature = "t", },
611 // { .member = "StartupMemorySwapMax", .signature = "t", },
612 // { .member = "MemoryZSwapMax", .signature = "t", },
613 // { .member = "StartupMemoryZSwapMax", .signature = "t", },
614 // { .member = "MemoryLimit", .signature = "t", },
615 // { .member = "CPUUsageNSec", .signature = "t", },
616 // { .member = "TasksCurrent", .signature = "t", },
617 // { .member = "TasksMax", .signature = "t", },
618 // { .member = "IPIngressBytes", .signature = "t", },
619 // { .member = "IPEgressBytes", .signature = "t", },
620 // { .member = "IOReadBytes", .signature = "t", },
621 // { .member = "IOWriteBytes", .signature = "t", },
622 // { .member = "ExecCondition", .signature = "a(sasbttttuii)", },
623 // { .member = "ExecConditionEx", .signature = "a(sasasttttuii)", },
624 // { .member = "ExecStartPre", .signature = "a(sasbttttuii)", },
625 // { .member = "ExecStartPreEx", .signature = "a(sasasttttuii)", },
626 // { .member = "ExecStart", .signature = "a(sasbttttuii)", },
627 // { .member = "ExecStartEx", .signature = "a(sasasttttuii)", },
628 // { .member = "ExecStartPost", .signature = "a(sasbttttuii)", },
629 // { .member = "ExecStartPostEx", .signature = "a(sasasttttuii)", },
630 // { .member = "ExecReload", .signature = "a(sasbttttuii)", },
631 // { .member = "ExecReloadEx", .signature = "a(sasasttttuii)", },
632 // { .member = "ExecStopPre", .signature = "a(sasbttttuii)", },
633 // { .member = "ExecStop", .signature = "a(sasbttttuii)", },
634 // { .member = "ExecStopEx", .signature = "a(sasasttttuii)", },
635 // { .member = "ExecStopPost", .signature = "a(sasbttttuii)", },
636 // { .member = "ExecStopPostEx", .signature = "a(sasasttttuii)", },
637 };
638
639 #define _UNIT_ATTRIBUTE_MAX (sizeof(unit_attributes) / sizeof(unit_attributes[0]))
640
641 typedef struct UnitInfo {
642 char *id;
643 char *type;
644 char *description;
645 char *load_state;
646 char *active_state;
647 char *sub_state;
648 char *following;
649 char *unit_path;
650 uint32_t job_id;
651 char *job_type;
652 char *job_path;
653
654 UnitType UnitType;
655 UnitLoadState UnitLoadState;
656 UnitActiveState UnitActiveState;
657 FreezerState FreezerState;
658
659 union {
660 AutomountState AutomountState;
661 DeviceState DeviceState;
662 MountState MountState;
663 PathState PathState;
664 ScopeState ScopeState;
665 ServiceState ServiceState;
666 SliceState SliceState;
667 SocketState SocketState;
668 SwapState SwapState;
669 TargetState TargetState;
670 TimerState TimerState;
671 };
672
673 struct UnitAttribute attributes[_UNIT_ATTRIBUTE_MAX];
674
675 FACET_ROW_SEVERITY severity;
676 uint32_t prio;
677
678 struct UnitInfo *prev, *next;
679 } UnitInfo;
680
681 static void update_freezer_state(UnitInfo *u, UnitAttribute *ua)
682 {
683 u->FreezerState = freezer_state_from_string(ua->str);
684 }
685
686 // ----------------------------------------------------------------------------
687 // common helpers
688
689 static void log_dbus_error(int r, const char *msg)
690 {
691 netdata_log_error("ND_SD_UNITS: %s failed with error %d (%s)", msg, r, strerror(-r));
692 }
693
694 // ----------------------------------------------------------------------------
695 // attributes management
696
697 static inline ssize_t unit_property_slot_from_string(const char *s)
698 {
699 if (!s || !*s)
700 return -EINVAL;
701
702 for (size_t i = 0; i < _UNIT_ATTRIBUTE_MAX; i++)
703 if (streq_ptr(unit_attributes[i].member, s))
704 return (ssize_t)i;
705
706 return -EINVAL;
707 }
708
709 static inline const char *unit_property_name_to_string_from_slot(ssize_t i)
710 {
711 if (i >= 0 && i < (ssize_t)_UNIT_ATTRIBUTE_MAX)
712 return unit_attributes[i].member;
713
714 return NULL;
715 }
716
717 static inline void systemd_unit_free_property(char type, struct UnitAttribute *at)
718 {
719 switch (type) {
720 case SD_BUS_TYPE_STRING:
721 case SD_BUS_TYPE_OBJECT_PATH:
722 freez(at->str);
723 at->str = NULL;
724 break;
725
726 default:
727 break;
728 }
729 }
730
731 static int systemd_unit_get_property(sd_bus_message *m, UnitInfo *u, const char *name)
732 {
733 int r;
734 char type;
735
736 r = sd_bus_message_peek_type(m, &type, NULL);
737 if (r < 0) {
738 log_dbus_error(r, "sd_bus_message_peek_type()");
739 return r;
740 }
741
742 ssize_t slot = unit_property_slot_from_string(name);
743 if (slot < 0) {
744 // internal_error(true, "unused attribute '%s' for unit '%s'", name, u->id);
745 sd_bus_message_skip(m, NULL);
746 return 0;
747 }
748
749 systemd_unit_free_property(unit_attributes[slot].value_type, &u->attributes[slot]);
750
751 if (unit_attributes[slot].value_type != type) {
752 netdata_log_error(
753 "Type of field '%s' expected to be '%c' but found '%c'. Ignoring field.",
754 unit_attributes[slot].member,
755 unit_attributes[slot].value_type,
756 type);
757 sd_bus_message_skip(m, NULL);
758 return 0;
759 }
760
761 switch (type) {
762 case SD_BUS_TYPE_OBJECT_PATH:
763 case SD_BUS_TYPE_STRING: {
764 char *s;
765
766 r = sd_bus_message_read_basic(m, type, &s);
767 if (r < 0) {
768 log_dbus_error(r, "sd_bus_message_read_basic()");
769 return r;
770 }
771
772 if (s && *s)
773 u->attributes[slot].str = strdupz(s);
774 } break;
775
776 case SD_BUS_TYPE_BOOLEAN: {
777 r = sd_bus_message_read_basic(m, type, &u->attributes[slot].boolean);
778 if (r < 0) {
779 log_dbus_error(r, "sd_bus_message_read_basic()");
780 return r;
781 }
782 } break;
783
784 case SD_BUS_TYPE_UINT64: {
785 r = sd_bus_message_read_basic(m, type, &u->attributes[slot].uint64);
786 if (r < 0) {
787 log_dbus_error(r, "sd_bus_message_read_basic()");
788 return r;
789 }
790 } break;
791
792 case SD_BUS_TYPE_INT64: {
793 r = sd_bus_message_read_basic(m, type, &u->attributes[slot].int64);
794 if (r < 0) {
795 log_dbus_error(r, "sd_bus_message_read_basic()");
796 return r;
797 }
798 } break;
799
800 case SD_BUS_TYPE_UINT32: {
801 r = sd_bus_message_read_basic(m, type, &u->attributes[slot].uint32);
802 if (r < 0) {
803 log_dbus_error(r, "sd_bus_message_read_basic()");
804 return r;
805 }
806 } break;
807
808 case SD_BUS_TYPE_INT32: {
809 r = sd_bus_message_read_basic(m, type, &u->attributes[slot].int32);
810 if (r < 0) {
811 log_dbus_error(r, "sd_bus_message_read_basic()");
812 return r;
813 }
814 } break;
815
816 case SD_BUS_TYPE_DOUBLE: {
817 r = sd_bus_message_read_basic(m, type, &u->attributes[slot].dbl);
818 if (r < 0) {
819 log_dbus_error(r, "sd_bus_message_read_basic()");
820 return r;
821 }
822 } break;
823
824 case SD_BUS_TYPE_ARRAY: {
825 internal_error(true, "member '%s' is an array", name);
826 sd_bus_message_skip(m, NULL);
827 return 0;
828 } break;
829
830 default: {
831 internal_error(true, "unknown field type '%c' for key '%s'", type, name);
832 sd_bus_message_skip(m, NULL);
833 return 0;
834 } break;
835 }
836
837 if (unit_attributes[slot].handler)
838 unit_attributes[slot].handler(u, &u->attributes[slot]);
839
840 return 0;
841 }
842
843 static int systemd_unit_get_all_properties(sd_bus *bus, UnitInfo *u)
844 {
845 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
846 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
847 int r;
848
849 r = sd_bus_call_method(
850 bus, "org.freedesktop.systemd1", u->unit_path, "org.freedesktop.DBus.Properties", "GetAll", &error, &m, "s", "");
851 if (r < 0) {
852 log_dbus_error(r, "sd_bus_call_method(p1)");
853 return r;
854 }
855
856 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
857 if (r < 0) {
858 log_dbus_error(r, "sd_bus_message_enter_container(p2)");
859 return r;
860 }
861
862 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
863 const char *member, *contents;
864
865 r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
866 if (r < 0) {
867 log_dbus_error(r, "sd_bus_message_read_basic(p3)");
868 return r;
869 }
870
871 r = sd_bus_message_peek_type(m, NULL, &contents);
872 if (r < 0) {
873 log_dbus_error(r, "sd_bus_message_peek_type(p4)");
874 return r;
875 }
876
877 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
878 if (r < 0) {
879 log_dbus_error(r, "sd_bus_message_enter_container(p5)");
880 return r;
881 }
882
883 systemd_unit_get_property(m, u, member);
884
885 r = sd_bus_message_exit_container(m);
886 if (r < 0) {
887 log_dbus_error(r, "sd_bus_message_exit_container(p6)");
888 return r;
889 }
890
891 r = sd_bus_message_exit_container(m);
892 if (r < 0) {
893 log_dbus_error(r, "sd_bus_message_exit_container(p7)");
894 return r;
895 }
896 }
897 if (r < 0) {
898 log_dbus_error(r, "sd_bus_message_enter_container(p8)");
899 return r;
900 }
901
902 r = sd_bus_message_exit_container(m);
903 if (r < 0) {
904 log_dbus_error(r, "sd_bus_message_exit_container(p9)");
905 return r;
906 }
907
908 return 0;
909 }
910
911 static void systemd_units_get_all_properties(sd_bus *bus, UnitInfo *base)
912 {
913 for (UnitInfo *u = base; u; u = u->next)
914 systemd_unit_get_all_properties(bus, u);
915 }
916
917 // ----------------------------------------------------------------------------
918 // main unit info
919
920 static int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u)
921 {
922 assert(message);
923 assert(u);
924
925 u->type = NULL;
926
927 int r = sd_bus_message_read(
928 message,
929 ND_SD_UNITS_DBUS_TYPES,
930 &u->id,
931 &u->description,
932 &u->load_state,
933 &u->active_state,
934 &u->sub_state,
935 &u->following,
936 &u->unit_path,
937 &u->job_id,
938 &u->job_type,
939 &u->job_path);
940
941 if (r <= 0)
942 return r;
943
944 char *dot;
945 if (u->id && (dot = strrchr(u->id, '.')) != NULL)
946 u->type = &dot[1];
947 else
948 u->type = "unknown";
949
950 u->UnitType = unit_type_from_string(u->type);
951 u->UnitLoadState = unit_load_state_from_string(u->load_state);
952 u->UnitActiveState = unit_active_state_from_string(u->active_state);
953
954 switch (u->UnitType) {
955 case UNIT_SERVICE:
956 u->ServiceState = service_state_from_string(u->sub_state);
957 break;
958
959 case UNIT_MOUNT:
960 u->MountState = mount_state_from_string(u->sub_state);
961 break;
962
963 case UNIT_SWAP:
964 u->SwapState = swap_state_from_string(u->sub_state);
965 break;
966
967 case UNIT_SOCKET:
968 u->SocketState = socket_state_from_string(u->sub_state);
969 break;
970
971 case UNIT_TARGET:
972 u->TargetState = target_state_from_string(u->sub_state);
973 break;
974
975 case UNIT_DEVICE:
976 u->DeviceState = device_state_from_string(u->sub_state);
977 break;
978
979 case UNIT_AUTOMOUNT:
980 u->AutomountState = automount_state_from_string(u->sub_state);
981 break;
982
983 case UNIT_TIMER:
984 u->TimerState = timer_state_from_string(u->sub_state);
985 break;
986
987 case UNIT_PATH:
988 u->PathState = path_state_from_string(u->sub_state);
989 break;
990
991 case UNIT_SLICE:
992 u->SliceState = slice_state_from_string(u->sub_state);
993 break;
994
995 case UNIT_SCOPE:
996 u->ScopeState = scope_state_from_string(u->sub_state);
997 break;
998
999 default:
1000 break;
1001 }
1002
1003 return r;
1004 }
1005
1006 static int hex_to_int(char c)
1007 {
1008 if (c >= '0' && c <= '9')
1009 return c - '0';
1010 if (c >= 'a' && c <= 'f')
1011 return c - 'a' + 10;
1012 if (c >= 'A' && c <= 'F')
1013 return c - 'A' + 10;
1014 return 0;
1015 }
1016
1017 // un-escape hex sequences (\xNN) in id
1018 static void txt_decode(char *txt)
1019 {
1020 if (!txt || !*txt)
1021 return;
1022
1023 char *src = txt, *dst = txt;
1024
1025 size_t id_len = strlen(src);
1026 size_t s = 0, d = 0;
1027 for (; s < id_len; s++) {
1028 if (src[s] == '\\' && src[s + 1] == 'x' && isxdigit(src[s + 2]) && isxdigit(src[s + 3])) {
1029 int value = (hex_to_int(src[s + 2]) << 4) + hex_to_int(src[s + 3]);
1030 dst[d++] = (char)value;
1031 s += 3;
1032 } else
1033 dst[d++] = src[s];
1034 }
1035 dst[d] = '\0';
1036 }
1037
1038 static UnitInfo *systemd_units_get_all(void)
1039 {
1040 _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
1041 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1042 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1043
1044 UnitInfo *base = NULL;
1045 int r;
1046
1047 r = sd_bus_default_system(&bus);
1048 if (r < 0) {
1049 log_dbus_error(r, "sd_bus_default_system()");
1050 return base;
1051 }
1052
1053 // This calls the ListUnits method of the org.freedesktop.systemd1.Manager interface
1054 // Replace "ListUnits" with "ListUnitsFiltered" to get specific units based on filters
1055 r = sd_bus_call_method(
1056 bus,
1057 "org.freedesktop.systemd1", /* service to contact */
1058 "/org/freedesktop/systemd1", /* object path */
1059 "org.freedesktop.systemd1.Manager", /* interface name */
1060 "ListUnits", /* method name */
1061 &error, /* object to return error in */
1062 &reply, /* return message on success */
1063 NULL); /* input signature */
1064 if (r < 0) {
1065 log_dbus_error(r, "sd_bus_call_method()");
1066 return base;
1067 }
1068
1069 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, ND_SD_UNITS_DBUS_TYPES);
1070 if (r < 0) {
1071 log_dbus_error(r, "sd_bus_message_enter_container()");
1072 return base;
1073 }
1074
1075 UnitInfo u;
1076 memset(&u, 0, sizeof(u));
1077 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1078 UnitInfo *i = callocz(1, sizeof(u));
1079 *i = u;
1080
1081 i->id = strdupz(u.id && *u.id ? u.id : "-");
1082 txt_decode(i->id);
1083
1084 i->type = strdupz(u.type && *u.type ? u.type : "-");
1085 i->description = strdupz(u.description && *u.description ? u.description : "-");
1086 txt_decode(i->description);
1087
1088 i->load_state = strdupz(u.load_state && *u.load_state ? u.load_state : "-");
1089 i->active_state = strdupz(u.active_state && *u.active_state ? u.active_state : "-");
1090 i->sub_state = strdupz(u.sub_state && *u.sub_state ? u.sub_state : "-");
1091 i->following = strdupz(u.following && *u.following ? u.following : "-");
1092 i->unit_path = strdupz(u.unit_path && *u.unit_path ? u.unit_path : "-");
1093 i->job_type = strdupz(u.job_type && *u.job_type ? u.job_type : "-");
1094 i->job_path = strdupz(u.job_path && *u.job_path ? u.job_path : "-");
1095 i->job_id = u.job_id;
1096
1097 DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(base, i, prev, next);
1098 memset(&u, 0, sizeof(u));
1099 }
1100 if (r < 0) {
1101 log_dbus_error(r, "sd_bus_message_read()");
1102 return base;
1103 }
1104
1105 r = sd_bus_message_exit_container(reply);
1106 if (r < 0) {
1107 log_dbus_error(r, "sd_bus_message_exit_container()");
1108 return base;
1109 }
1110
1111 systemd_units_get_all_properties(bus, base);
1112
1113 return base;
1114 }
1115
1116 static void systemd_units_free_all(UnitInfo *base)
1117 {
1118 while (base) {
1119 UnitInfo *u = base;
1120 DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(base, u, prev, next);
1121 freez((void *)u->id);
1122 freez((void *)u->type);
1123 freez((void *)u->description);
1124 freez((void *)u->load_state);
1125 freez((void *)u->active_state);
1126 freez((void *)u->sub_state);
1127 freez((void *)u->following);
1128 freez((void *)u->unit_path);
1129 freez((void *)u->job_type);
1130 freez((void *)u->job_path);
1131
1132 for (int i = 0; i < (ssize_t)_UNIT_ATTRIBUTE_MAX; i++)
1133 systemd_unit_free_property(unit_attributes[i].value_type, &u->attributes[i]);
1134
1135 freez(u);
1136 }
1137 }
1138
1139 // ----------------------------------------------------------------------------
1140
1141 static void netdata_systemd_units_function_help(const char *transaction)
1142 {
1143 BUFFER *wb = buffer_create(0, NULL);
1144 buffer_sprintf(
1145 wb,
1146 "%s / %s\n"
1147 "\n"
1148 "%s\n"
1149 "\n"
1150 "The following parameters are supported:\n"
1151 "\n"
1152 " help\n"
1153 " Shows this help message.\n"
1154 "\n"
1155 " info\n"
1156 " Request initial configuration information about the plugin.\n"
1157 " The key entity returned is the required_params array, which includes\n"
1158 " all the available systemd journal sources.\n"
1159 " When `info` is requested, all other parameters are ignored.\n"
1160 "\n",
1161 program_name,
1162 ND_SD_UNITS_FUNCTION_NAME,
1163 ND_SD_UNITS_FUNCTION_DESCRIPTION);
1164
1165 netdata_mutex_lock(&stdout_mutex);
1166 wb->response_code = HTTP_RESP_OK;
1167 wb->content_type = CT_TEXT_PLAIN;
1168 wb->expires = now_realtime_sec() + 3600;
1169 pluginsd_function_result_to_stdout(transaction, wb);
1170 netdata_mutex_unlock(&stdout_mutex);
1171
1172 buffer_free(wb);
1173 }
1174
1175 static void netdata_systemd_units_function_info(const char *transaction)
1176 {
1177 BUFFER *wb = buffer_create(0, NULL);
1178 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
1179
1180 buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
1181 buffer_json_member_add_string(wb, "type", "table");
1182 buffer_json_member_add_boolean(wb, "has_history", false);
1183 buffer_json_member_add_string(wb, "help", ND_SD_UNITS_FUNCTION_DESCRIPTION);
1184
1185 buffer_json_finalize(wb);
1186 netdata_mutex_lock(&stdout_mutex);
1187 wb->response_code = HTTP_RESP_OK;
1188 wb->content_type = CT_APPLICATION_JSON;
1189 wb->expires = now_realtime_sec() + 3600;
1190 pluginsd_function_result_to_stdout(transaction, wb);
1191 netdata_mutex_unlock(&stdout_mutex);
1192
1193 buffer_free(wb);
1194 }
1195
1196 // ----------------------------------------------------------------------------
1197
1198 static void systemd_unit_priority(UnitInfo *u, size_t units)
1199 {
1200 uint32_t prio;
1201
1202 switch (u->severity) {
1203 case FACET_ROW_SEVERITY_CRITICAL:
1204 prio = 0;
1205 break;
1206
1207 default:
1208 case FACET_ROW_SEVERITY_WARNING:
1209 prio = 1;
1210 break;
1211
1212 case FACET_ROW_SEVERITY_NOTICE:
1213 prio = 2;
1214 break;
1215
1216 case FACET_ROW_SEVERITY_NORMAL:
1217 prio = 3;
1218 break;
1219
1220 case FACET_ROW_SEVERITY_DEBUG:
1221 prio = 4;
1222 break;
1223 }
1224
1225 prio = prio * (uint32_t)(_UNIT_TYPE_MAX + 1) + (uint32_t)u->UnitType;
1226 u->prio = (prio * units) + u->prio;
1227 }
1228
1229 static inline FACET_ROW_SEVERITY if_less(FACET_ROW_SEVERITY current, FACET_ROW_SEVERITY max, FACET_ROW_SEVERITY target)
1230 {
1231 FACET_ROW_SEVERITY wanted = current;
1232 if (current < target)
1233 wanted = target > max ? max : target;
1234 return wanted;
1235 }
1236
1237 static inline FACET_ROW_SEVERITY
1238 if_normal(FACET_ROW_SEVERITY current, FACET_ROW_SEVERITY max, FACET_ROW_SEVERITY target)
1239 {
1240 FACET_ROW_SEVERITY wanted = current;
1241 if (current == FACET_ROW_SEVERITY_NORMAL)
1242 wanted = target > max ? max : target;
1243 return wanted;
1244 }
1245
1246 static FACET_ROW_SEVERITY system_unit_severity(UnitInfo *u)
1247 {
1248 FACET_ROW_SEVERITY severity, max_severity;
1249
1250 switch (u->UnitLoadState) {
1251 case UNIT_ERROR:
1252 case UNIT_BAD_SETTING:
1253 severity = FACET_ROW_SEVERITY_CRITICAL;
1254 max_severity = FACET_ROW_SEVERITY_CRITICAL;
1255 break;
1256
1257 default:
1258 severity = FACET_ROW_SEVERITY_WARNING;
1259 max_severity = FACET_ROW_SEVERITY_CRITICAL;
1260 break;
1261
1262 case UNIT_NOT_FOUND:
1263 severity = FACET_ROW_SEVERITY_NOTICE;
1264 max_severity = FACET_ROW_SEVERITY_NOTICE;
1265 break;
1266
1267 case UNIT_LOADED:
1268 severity = FACET_ROW_SEVERITY_NORMAL;
1269 max_severity = FACET_ROW_SEVERITY_CRITICAL;
1270 break;
1271
1272 case UNIT_MERGED:
1273 case UNIT_MASKED:
1274 case UNIT_STUB:
1275 severity = FACET_ROW_SEVERITY_DEBUG;
1276 max_severity = FACET_ROW_SEVERITY_DEBUG;
1277 break;
1278 }
1279
1280 switch (u->UnitActiveState) {
1281 case UNIT_FAILED:
1282 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_CRITICAL);
1283 break;
1284
1285 default:
1286 case UNIT_RELOADING:
1287 case UNIT_ACTIVATING:
1288 case UNIT_DEACTIVATING:
1289 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1290 break;
1291
1292 case UNIT_MAINTENANCE:
1293 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_NOTICE);
1294 break;
1295
1296 case UNIT_ACTIVE:
1297 break;
1298
1299 case UNIT_INACTIVE:
1300 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1301 break;
1302 }
1303
1304 switch (u->FreezerState) {
1305 default:
1306 case FREEZER_FROZEN:
1307 case FREEZER_FREEZING:
1308 case FREEZER_THAWING:
1309 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1310 break;
1311
1312 case FREEZER_RUNNING:
1313 break;
1314 }
1315
1316 switch (u->UnitType) {
1317 case UNIT_SERVICE:
1318 switch (u->ServiceState) {
1319 case SERVICE_FAILED:
1320 case SERVICE_FAILED_BEFORE_AUTO_RESTART:
1321 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_CRITICAL);
1322 break;
1323
1324 default:
1325 case SERVICE_STOP:
1326 case SERVICE_STOP_WATCHDOG:
1327 case SERVICE_STOP_SIGTERM:
1328 case SERVICE_STOP_SIGKILL:
1329 case SERVICE_STOP_POST:
1330 case SERVICE_FINAL_WATCHDOG:
1331 case SERVICE_FINAL_SIGTERM:
1332 case SERVICE_FINAL_SIGKILL:
1333 case SERVICE_AUTO_RESTART:
1334 case SERVICE_AUTO_RESTART_QUEUED:
1335 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1336 break;
1337
1338 case SERVICE_CONDITION:
1339 case SERVICE_START_PRE:
1340 case SERVICE_START:
1341 case SERVICE_START_POST:
1342 case SERVICE_RELOAD:
1343 case SERVICE_RELOAD_SIGNAL:
1344 case SERVICE_RELOAD_NOTIFY:
1345 case SERVICE_DEAD_RESOURCES_PINNED:
1346 case SERVICE_CLEANING:
1347 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_NOTICE);
1348 break;
1349
1350 case SERVICE_EXITED:
1351 case SERVICE_RUNNING:
1352 break;
1353
1354 case SERVICE_DEAD:
1355 case SERVICE_DEAD_BEFORE_AUTO_RESTART:
1356 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1357 break;
1358 }
1359 break;
1360
1361 case UNIT_MOUNT:
1362 switch (u->MountState) {
1363 case MOUNT_FAILED:
1364 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_CRITICAL);
1365 break;
1366
1367 default:
1368 case MOUNT_REMOUNTING_SIGTERM:
1369 case MOUNT_REMOUNTING_SIGKILL:
1370 case MOUNT_UNMOUNTING_SIGTERM:
1371 case MOUNT_UNMOUNTING_SIGKILL:
1372 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1373 break;
1374
1375 case MOUNT_MOUNTING:
1376 case MOUNT_MOUNTING_DONE:
1377 case MOUNT_REMOUNTING:
1378 case MOUNT_UNMOUNTING:
1379 case MOUNT_CLEANING:
1380 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_NOTICE);
1381 break;
1382
1383 case MOUNT_MOUNTED:
1384 break;
1385
1386 case MOUNT_DEAD:
1387 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1388 break;
1389 }
1390 break;
1391
1392 case UNIT_SWAP:
1393 switch (u->SwapState) {
1394 case SWAP_FAILED:
1395 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_CRITICAL);
1396 break;
1397
1398 default:
1399 case SWAP_DEACTIVATING_SIGTERM:
1400 case SWAP_DEACTIVATING_SIGKILL:
1401 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1402 break;
1403
1404 case SWAP_ACTIVATING:
1405 case SWAP_ACTIVATING_DONE:
1406 case SWAP_DEACTIVATING:
1407 case SWAP_CLEANING:
1408 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_NOTICE);
1409 break;
1410
1411 case SWAP_ACTIVE:
1412 break;
1413
1414 case SWAP_DEAD:
1415 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1416 break;
1417 }
1418 break;
1419
1420 case UNIT_SOCKET:
1421 switch (u->SocketState) {
1422 case SOCKET_FAILED:
1423 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_CRITICAL);
1424 break;
1425
1426 default:
1427 case SOCKET_STOP_PRE_SIGTERM:
1428 case SOCKET_STOP_PRE_SIGKILL:
1429 case SOCKET_FINAL_SIGTERM:
1430 case SOCKET_FINAL_SIGKILL:
1431 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1432 break;
1433
1434 case SOCKET_START_PRE:
1435 case SOCKET_START_CHOWN:
1436 case SOCKET_START_POST:
1437 case SOCKET_STOP_PRE:
1438 case SOCKET_STOP_POST:
1439 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_NOTICE);
1440 break;
1441
1442 case SOCKET_RUNNING:
1443 case SOCKET_LISTENING:
1444 break;
1445
1446 case SOCKET_DEAD:
1447 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1448 break;
1449 }
1450 break;
1451
1452 case UNIT_TARGET:
1453 switch (u->TargetState) {
1454 default:
1455 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1456 break;
1457
1458 case TARGET_ACTIVE:
1459 break;
1460
1461 case TARGET_DEAD:
1462 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1463 break;
1464 }
1465 break;
1466
1467 case UNIT_DEVICE:
1468 switch (u->DeviceState) {
1469 default:
1470 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1471 break;
1472
1473 case DEVICE_TENTATIVE:
1474 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_NOTICE);
1475 break;
1476
1477 case DEVICE_PLUGGED:
1478 break;
1479
1480 case DEVICE_DEAD:
1481 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1482 break;
1483 }
1484 break;
1485
1486 case UNIT_AUTOMOUNT:
1487 switch (u->AutomountState) {
1488 case AUTOMOUNT_FAILED:
1489 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_CRITICAL);
1490 break;
1491
1492 default:
1493 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1494 break;
1495
1496 case AUTOMOUNT_WAITING:
1497 case AUTOMOUNT_RUNNING:
1498 break;
1499
1500 case AUTOMOUNT_DEAD:
1501 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1502 break;
1503 }
1504 break;
1505
1506 case UNIT_TIMER:
1507 switch (u->TimerState) {
1508 case TIMER_FAILED:
1509 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_CRITICAL);
1510 break;
1511
1512 default:
1513 case TIMER_ELAPSED:
1514 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1515 break;
1516
1517 case TIMER_WAITING:
1518 case TIMER_RUNNING:
1519 break;
1520
1521 case TIMER_DEAD:
1522 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1523 break;
1524 }
1525 break;
1526
1527 case UNIT_PATH:
1528 switch (u->PathState) {
1529 case PATH_FAILED:
1530 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_CRITICAL);
1531 break;
1532
1533 default:
1534 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1535 break;
1536
1537 case PATH_WAITING:
1538 case PATH_RUNNING:
1539 break;
1540
1541 case PATH_DEAD:
1542 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1543 break;
1544 }
1545 break;
1546
1547 case UNIT_SLICE:
1548 switch (u->SliceState) {
1549 default:
1550 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1551 break;
1552
1553 case SLICE_ACTIVE:
1554 break;
1555
1556 case SLICE_DEAD:
1557 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1558 break;
1559 }
1560 break;
1561
1562 case UNIT_SCOPE:
1563 switch (u->ScopeState) {
1564 case SCOPE_FAILED:
1565 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_CRITICAL);
1566 break;
1567
1568 default:
1569 case SCOPE_STOP_SIGTERM:
1570 case SCOPE_STOP_SIGKILL:
1571 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1572 break;
1573
1574 case SCOPE_ABANDONED:
1575 case SCOPE_START_CHOWN:
1576 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_NOTICE);
1577 break;
1578
1579 case SCOPE_RUNNING:
1580 break;
1581
1582 case SCOPE_DEAD:
1583 severity = if_normal(severity, max_severity, FACET_ROW_SEVERITY_DEBUG);
1584 break;
1585 }
1586 break;
1587
1588 default:
1589 severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1590 break;
1591 }
1592
1593 u->severity = severity;
1594 return severity;
1595 }
1596
1597 static int unit_info_compar(const void *a, const void *b)
1598 {
1599 UnitInfo *u1 = *((UnitInfo **)a);
1600 UnitInfo *u2 = *((UnitInfo **)b);
1601
1602 return strcasecmp(u1->id, u2->id);
1603 }
1604
1605 static void systemd_units_assign_priority(UnitInfo *base)
1606 {
1607 size_t units = 0, c = 0, prio = 0;
1608 for (UnitInfo *u = base; u; u = u->next)
1609 units++;
1610
1611 UnitInfo *array[units];
1612 for (UnitInfo *u = base; u; u = u->next)
1613 array[c++] = u;
1614
1615 qsort(array, units, sizeof(UnitInfo *), unit_info_compar);
1616
1617 for (c = 0; c < units; c++) {
1618 array[c]->prio = prio++;
1619 system_unit_severity(array[c]);
1620 systemd_unit_priority(array[c], units);
1621 }
1622 }
1623
1624 void function_systemd_units(
1625 const char *transaction,
1626 char *function,
1627 usec_t *stop_monotonic_ut __maybe_unused,
1628 bool *cancelled __maybe_unused,
1629 BUFFER *payload __maybe_unused,
1630 HTTP_ACCESS access __maybe_unused,
1631 const char *source __maybe_unused,
1632 void *data __maybe_unused)
1633 {
1634 char *words[ND_SD_UNITS_MAX_PARAMS] = {NULL};
1635 size_t num_words = quoted_strings_splitter_whitespace(function, words, ND_SD_UNITS_MAX_PARAMS);
1636 for (int i = 1; i < ND_SD_UNITS_MAX_PARAMS; i++) {
1637 char *keyword = get_word(words, num_words, i);
1638 if (!keyword)
1639 break;
1640
1641 if (strcmp(keyword, "info") == 0) {
1642 netdata_systemd_units_function_info(transaction);
1643 return;
1644 } else if (strcmp(keyword, "help") == 0) {
1645 netdata_systemd_units_function_help(transaction);
1646 return;
1647 }
1648 }
1649
1650 UnitInfo *base = systemd_units_get_all();
1651 systemd_units_assign_priority(base);
1652
1653 BUFFER *wb = buffer_create(0, NULL);
1654 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
1655
1656 buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
1657 buffer_json_member_add_string(wb, "type", "table");
1658 buffer_json_member_add_boolean(wb, "has_history", false);
1659 buffer_json_member_add_time_t(wb, "update_every", 10);
1660 buffer_json_member_add_string(wb, "help", ND_SD_UNITS_FUNCTION_DESCRIPTION);
1661 buffer_json_member_add_array(wb, "data");
1662
1663 size_t count[_UNIT_ATTRIBUTE_MAX] = {0};
1664 struct UnitAttribute max[_UNIT_ATTRIBUTE_MAX];
1665
1666 for (UnitInfo *u = base; u; u = u->next) {
1667 buffer_json_add_array_item_array(wb);
1668 {
1669 buffer_json_add_array_item_string(wb, u->id);
1670
1671 buffer_json_add_array_item_object(wb);
1672 {
1673 buffer_json_member_add_string(wb, "severity", facets_severity_to_string(u->severity));
1674 }
1675 buffer_json_object_close(wb);
1676
1677 buffer_json_add_array_item_string(wb, u->type);
1678 buffer_json_add_array_item_string(wb, u->description);
1679 buffer_json_add_array_item_string(wb, u->load_state);
1680 buffer_json_add_array_item_string(wb, u->active_state);
1681 buffer_json_add_array_item_string(wb, u->sub_state);
1682 buffer_json_add_array_item_string(wb, u->following);
1683 buffer_json_add_array_item_string(wb, u->unit_path);
1684 buffer_json_add_array_item_uint64(wb, u->job_id);
1685 buffer_json_add_array_item_string(wb, u->job_type);
1686 buffer_json_add_array_item_string(wb, u->job_path);
1687
1688 for (ssize_t i = 0; i < (ssize_t)_UNIT_ATTRIBUTE_MAX; i++) {
1689 switch (unit_attributes[i].value_type) {
1690 case SD_BUS_TYPE_OBJECT_PATH:
1691 case SD_BUS_TYPE_STRING:
1692 buffer_json_add_array_item_string(
1693 wb, u->attributes[i].str && *u->attributes[i].str ? u->attributes[i].str : "-");
1694 break;
1695
1696 case SD_BUS_TYPE_UINT64:
1697 buffer_json_add_array_item_uint64(wb, u->attributes[i].uint64);
1698 if (!count[i]++)
1699 max[i].uint64 = 0;
1700 max[i].uint64 = MAX(max[i].uint64, u->attributes[i].uint64);
1701 break;
1702
1703 case SD_BUS_TYPE_UINT32:
1704 buffer_json_add_array_item_uint64(wb, u->attributes[i].uint32);
1705 if (!count[i]++)
1706 max[i].uint32 = 0;
1707 max[i].uint32 = MAX(max[i].uint32, u->attributes[i].uint32);
1708 break;
1709
1710 case SD_BUS_TYPE_INT64:
1711 buffer_json_add_array_item_uint64(wb, u->attributes[i].int64);
1712 if (!count[i]++)
1713 max[i].uint64 = 0;
1714 max[i].int64 = MAX(max[i].int64, u->attributes[i].int64);
1715 break;
1716
1717 case SD_BUS_TYPE_INT32:
1718 buffer_json_add_array_item_uint64(wb, u->attributes[i].int32);
1719 if (!count[i]++)
1720 max[i].int32 = 0;
1721 max[i].int32 = MAX(max[i].int32, u->attributes[i].int32);
1722 break;
1723
1724 case SD_BUS_TYPE_DOUBLE:
1725 buffer_json_add_array_item_double(wb, u->attributes[i].dbl);
1726 if (!count[i]++)
1727 max[i].dbl = 0.0;
1728 max[i].dbl = MAX(max[i].dbl, u->attributes[i].dbl);
1729 break;
1730
1731 case SD_BUS_TYPE_BOOLEAN:
1732 buffer_json_add_array_item_boolean(wb, u->attributes[i].boolean);
1733 break;
1734
1735 default:
1736 break;
1737 }
1738 }
1739
1740 buffer_json_add_array_item_uint64(wb, u->prio);
1741 buffer_json_add_array_item_uint64(wb, 1); // count
1742 }
1743 buffer_json_array_close(wb);
1744 }
1745
1746 buffer_json_array_close(wb); // data
1747
1748 buffer_json_member_add_object(wb, "columns");
1749 {
1750 size_t field_id = 0;
1751
1752 buffer_rrdf_table_add_field(
1753 wb,
1754 field_id++,
1755 "id",
1756 "Unit ID",
1757 RRDF_FIELD_TYPE_STRING,
1758 RRDF_FIELD_VISUAL_VALUE,
1759 RRDF_FIELD_TRANSFORM_NONE,
1760 0,
1761 NULL,
1762 NAN,
1763 RRDF_FIELD_SORT_ASCENDING,
1764 NULL,
1765 RRDF_FIELD_SUMMARY_COUNT,
1766 RRDF_FIELD_FILTER_NONE,
1767 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_WRAP | RRDF_FIELD_OPTS_FULL_WIDTH,
1768 NULL);
1769
1770 buffer_rrdf_table_add_field(
1771 wb,
1772 field_id++,
1773 "rowOptions",
1774 "rowOptions",
1775 RRDF_FIELD_TYPE_NONE,
1776 RRDR_FIELD_VISUAL_ROW_OPTIONS,
1777 RRDF_FIELD_TRANSFORM_NONE,
1778 0,
1779 NULL,
1780 NAN,
1781 RRDF_FIELD_SORT_FIXED,
1782 NULL,
1783 RRDF_FIELD_SUMMARY_COUNT,
1784 RRDF_FIELD_FILTER_NONE,
1785 RRDF_FIELD_OPTS_DUMMY,
1786 NULL);
1787
1788 buffer_rrdf_table_add_field(
1789 wb,
1790 field_id++,
1791 "type",
1792 "Unit Type",
1793 RRDF_FIELD_TYPE_STRING,
1794 RRDF_FIELD_VISUAL_VALUE,
1795 RRDF_FIELD_TRANSFORM_NONE,
1796 0,
1797 NULL,
1798 NAN,
1799 RRDF_FIELD_SORT_ASCENDING,
1800 NULL,
1801 RRDF_FIELD_SUMMARY_COUNT,
1802 RRDF_FIELD_FILTER_MULTISELECT,
1803 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_EXPANDED_FILTER,
1804 NULL);
1805
1806 buffer_rrdf_table_add_field(
1807 wb,
1808 field_id++,
1809 "description",
1810 "Unit Description",
1811 RRDF_FIELD_TYPE_STRING,
1812 RRDF_FIELD_VISUAL_VALUE,
1813 RRDF_FIELD_TRANSFORM_NONE,
1814 0,
1815 NULL,
1816 NAN,
1817 RRDF_FIELD_SORT_ASCENDING,
1818 NULL,
1819 RRDF_FIELD_SUMMARY_COUNT,
1820 RRDF_FIELD_FILTER_NONE,
1821 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_WRAP | RRDF_FIELD_OPTS_FULL_WIDTH,
1822 NULL);
1823
1824 buffer_rrdf_table_add_field(
1825 wb,
1826 field_id++,
1827 "loadState",
1828 "Unit Load State",
1829 RRDF_FIELD_TYPE_STRING,
1830 RRDF_FIELD_VISUAL_VALUE,
1831 RRDF_FIELD_TRANSFORM_NONE,
1832 0,
1833 NULL,
1834 NAN,
1835 RRDF_FIELD_SORT_ASCENDING,
1836 NULL,
1837 RRDF_FIELD_SUMMARY_COUNT,
1838 RRDF_FIELD_FILTER_MULTISELECT,
1839 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_EXPANDED_FILTER,
1840 NULL);
1841
1842 buffer_rrdf_table_add_field(
1843 wb,
1844 field_id++,
1845 "activeState",
1846 "Unit Active State",
1847 RRDF_FIELD_TYPE_STRING,
1848 RRDF_FIELD_VISUAL_VALUE,
1849 RRDF_FIELD_TRANSFORM_NONE,
1850 0,
1851 NULL,
1852 NAN,
1853 RRDF_FIELD_SORT_ASCENDING,
1854 NULL,
1855 RRDF_FIELD_SUMMARY_COUNT,
1856 RRDF_FIELD_FILTER_MULTISELECT,
1857 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_EXPANDED_FILTER,
1858 NULL);
1859
1860 buffer_rrdf_table_add_field(
1861 wb,
1862 field_id++,
1863 "subState",
1864 "Unit Sub State",
1865 RRDF_FIELD_TYPE_STRING,
1866 RRDF_FIELD_VISUAL_VALUE,
1867 RRDF_FIELD_TRANSFORM_NONE,
1868 0,
1869 NULL,
1870 NAN,
1871 RRDF_FIELD_SORT_ASCENDING,
1872 NULL,
1873 RRDF_FIELD_SUMMARY_COUNT,
1874 RRDF_FIELD_FILTER_MULTISELECT,
1875 RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_EXPANDED_FILTER,
1876 NULL);
1877
1878 buffer_rrdf_table_add_field(
1879 wb,
1880 field_id++,
1881 "following",
1882 "Unit Following",
1883 RRDF_FIELD_TYPE_STRING,
1884 RRDF_FIELD_VISUAL_VALUE,
1885 RRDF_FIELD_TRANSFORM_NONE,
1886 0,
1887 NULL,
1888 NAN,
1889 RRDF_FIELD_SORT_ASCENDING,
1890 NULL,
1891 RRDF_FIELD_SUMMARY_COUNT,
1892 RRDF_FIELD_FILTER_NONE,
1893 RRDF_FIELD_OPTS_WRAP,
1894 NULL);
1895
1896 buffer_rrdf_table_add_field(
1897 wb,
1898 field_id++,
1899 "path",
1900 "Unit Path",
1901 RRDF_FIELD_TYPE_STRING,
1902 RRDF_FIELD_VISUAL_VALUE,
1903 RRDF_FIELD_TRANSFORM_NONE,
1904 0,
1905 NULL,
1906 NAN,
1907 RRDF_FIELD_SORT_ASCENDING,
1908 NULL,
1909 RRDF_FIELD_SUMMARY_COUNT,
1910 RRDF_FIELD_FILTER_NONE,
1911 RRDF_FIELD_OPTS_WRAP | RRDF_FIELD_OPTS_FULL_WIDTH,
1912 NULL);
1913
1914 buffer_rrdf_table_add_field(
1915 wb,
1916 field_id++,
1917 "jobId",
1918 "Unit Job ID",
1919 RRDF_FIELD_TYPE_INTEGER,
1920 RRDF_FIELD_VISUAL_VALUE,
1921 RRDF_FIELD_TRANSFORM_NONE,
1922 0,
1923 NULL,
1924 NAN,
1925 RRDF_FIELD_SORT_ASCENDING,
1926 NULL,
1927 RRDF_FIELD_SUMMARY_COUNT,
1928 RRDF_FIELD_FILTER_NONE,
1929 RRDF_FIELD_OPTS_NONE,
1930 NULL);
1931
1932 buffer_rrdf_table_add_field(
1933 wb,
1934 field_id++,
1935 "jobType",
1936 "Unit Job Type",
1937 RRDF_FIELD_TYPE_STRING,
1938 RRDF_FIELD_VISUAL_VALUE,
1939 RRDF_FIELD_TRANSFORM_NONE,
1940 0,
1941 NULL,
1942 NAN,
1943 RRDF_FIELD_SORT_ASCENDING,
1944 NULL,
1945 RRDF_FIELD_SUMMARY_COUNT,
1946 RRDF_FIELD_FILTER_MULTISELECT,
1947 RRDF_FIELD_OPTS_NONE,
1948 NULL);
1949
1950 buffer_rrdf_table_add_field(
1951 wb,
1952 field_id++,
1953 "jobPath",
1954 "Unit Job Path",
1955 RRDF_FIELD_TYPE_STRING,
1956 RRDF_FIELD_VISUAL_VALUE,
1957 RRDF_FIELD_TRANSFORM_NONE,
1958 0,
1959 NULL,
1960 NAN,
1961 RRDF_FIELD_SORT_ASCENDING,
1962 NULL,
1963 RRDF_FIELD_SUMMARY_COUNT,
1964 RRDF_FIELD_FILTER_NONE,
1965 RRDF_FIELD_OPTS_WRAP | RRDF_FIELD_OPTS_FULL_WIDTH,
1966 NULL);
1967
1968 for (ssize_t i = 0; i < (ssize_t)_UNIT_ATTRIBUTE_MAX; i++) {
1969 char key[256], name[256];
1970
1971 if (unit_attributes[i].show_as)
1972 snprintfz(key, sizeof(key), "%s", unit_attributes[i].show_as);
1973 else
1974 snprintfz(key, sizeof(key), "attribute%s", unit_property_name_to_string_from_slot(i));
1975
1976 if (unit_attributes[i].info)
1977 snprintfz(name, sizeof(name), "%s", unit_attributes[i].info);
1978 else
1979 snprintfz(name, sizeof(name), "Attribute %s", unit_property_name_to_string_from_slot(i));
1980
1981 RRDF_FIELD_OPTIONS options = unit_attributes[i].options;
1982 RRDF_FIELD_FILTER filter = unit_attributes[i].filter;
1983
1984 switch (unit_attributes[i].value_type) {
1985 case SD_BUS_TYPE_OBJECT_PATH:
1986 case SD_BUS_TYPE_STRING:
1987 buffer_rrdf_table_add_field(
1988 wb,
1989 field_id++,
1990 key,
1991 name,
1992 RRDF_FIELD_TYPE_STRING,
1993 RRDF_FIELD_VISUAL_VALUE,
1994 RRDF_FIELD_TRANSFORM_NONE,
1995 0,
1996 NULL,
1997 NAN,
1998 RRDF_FIELD_SORT_ASCENDING,
1999 NULL,
2000 RRDF_FIELD_SUMMARY_COUNT,
2001 filter,
2002 RRDF_FIELD_OPTS_WRAP | options,
2003 NULL);
2004 break;
2005
2006 case SD_BUS_TYPE_INT32:
2007 case SD_BUS_TYPE_UINT32:
2008 case SD_BUS_TYPE_INT64:
2009 case SD_BUS_TYPE_UINT64: {
2010 double m = 0.0;
2011 if (unit_attributes[i].value_type == SD_BUS_TYPE_UINT64)
2012 m = (double)max[i].uint64;
2013 else if (unit_attributes[i].value_type == SD_BUS_TYPE_INT64)
2014 m = (double)max[i].int64;
2015 else if (unit_attributes[i].value_type == SD_BUS_TYPE_UINT32)
2016 m = (double)max[i].uint32;
2017 else if (unit_attributes[i].value_type == SD_BUS_TYPE_INT32)
2018 m = (double)max[i].int32;
2019
2020 buffer_rrdf_table_add_field(
2021 wb,
2022 field_id++,
2023 key,
2024 name,
2025 RRDF_FIELD_TYPE_INTEGER,
2026 RRDF_FIELD_VISUAL_VALUE,
2027 RRDF_FIELD_TRANSFORM_NONE,
2028 0,
2029 NULL,
2030 m,
2031 RRDF_FIELD_SORT_ASCENDING,
2032 NULL,
2033 RRDF_FIELD_SUMMARY_SUM,
2034 filter,
2035 RRDF_FIELD_OPTS_WRAP | options,
2036 NULL);
2037 } break;
2038
2039 case SD_BUS_TYPE_DOUBLE:
2040 buffer_rrdf_table_add_field(
2041 wb,
2042 field_id++,
2043 key,
2044 name,
2045 RRDF_FIELD_TYPE_INTEGER,
2046 RRDF_FIELD_VISUAL_VALUE,
2047 RRDF_FIELD_TRANSFORM_NONE,
2048 2,
2049 NULL,
2050 max[i].dbl,
2051 RRDF_FIELD_SORT_ASCENDING,
2052 NULL,
2053 RRDF_FIELD_SUMMARY_SUM,
2054 filter,
2055 RRDF_FIELD_OPTS_WRAP | options,
2056 NULL);
2057 break;
2058
2059 case SD_BUS_TYPE_BOOLEAN:
2060 buffer_rrdf_table_add_field(
2061 wb,
2062 field_id++,
2063 key,
2064 name,
2065 RRDF_FIELD_TYPE_BOOLEAN,
2066 RRDF_FIELD_VISUAL_VALUE,
2067 RRDF_FIELD_TRANSFORM_NONE,
2068 0,
2069 NULL,
2070 NAN,
2071 RRDF_FIELD_SORT_ASCENDING,
2072 NULL,
2073 RRDF_FIELD_SUMMARY_COUNT,
2074 filter,
2075 RRDF_FIELD_OPTS_WRAP | options,
2076 NULL);
2077 break;
2078
2079 default:
2080 break;
2081 }
2082 }
2083
2084 buffer_rrdf_table_add_field(
2085 wb,
2086 field_id++,
2087 "priority",
2088 "Priority",
2089 RRDF_FIELD_TYPE_INTEGER,
2090 RRDF_FIELD_VISUAL_VALUE,
2091 RRDF_FIELD_TRANSFORM_NONE,
2092 0,
2093 NULL,
2094 NAN,
2095 RRDF_FIELD_SORT_ASCENDING,
2096 NULL,
2097 RRDF_FIELD_SUMMARY_COUNT,
2098 RRDF_FIELD_FILTER_NONE,
2099 RRDF_FIELD_OPTS_NONE,
2100 NULL);
2101
2102 buffer_rrdf_table_add_field(
2103 wb,
2104 field_id++,
2105 "count",
2106 "Count",
2107 RRDF_FIELD_TYPE_INTEGER,
2108 RRDF_FIELD_VISUAL_VALUE,
2109 RRDF_FIELD_TRANSFORM_NONE,
2110 0,
2111 NULL,
2112 NAN,
2113 RRDF_FIELD_SORT_ASCENDING,
2114 NULL,
2115 RRDF_FIELD_SUMMARY_COUNT,
2116 RRDF_FIELD_FILTER_NONE,
2117 RRDF_FIELD_OPTS_NONE,
2118 NULL);
2119 }
2120
2121 buffer_json_object_close(wb); // columns
2122 buffer_json_member_add_string(wb, "default_sort_column", "priority");
2123
2124 buffer_json_member_add_object(wb, "charts");
2125 {
2126 buffer_json_member_add_object(wb, "count");
2127 {
2128 buffer_json_member_add_string(wb, "name", "count");
2129 buffer_json_member_add_string(wb, "type", "stacked-bar");
2130 buffer_json_member_add_array(wb, "columns");
2131 {
2132 buffer_json_add_array_item_string(wb, "count");
2133 }
2134 buffer_json_array_close(wb);
2135 }
2136 buffer_json_object_close(wb);
2137 }
2138 buffer_json_object_close(wb); // charts
2139
2140 buffer_json_member_add_array(wb, "default_charts");
2141 {
2142 buffer_json_add_array_item_array(wb);
2143 buffer_json_add_array_item_string(wb, "count");
2144 buffer_json_add_array_item_string(wb, "activeState");
2145 buffer_json_array_close(wb);
2146 buffer_json_add_array_item_array(wb);
2147 buffer_json_add_array_item_string(wb, "count");
2148 buffer_json_add_array_item_string(wb, "subState");
2149 buffer_json_array_close(wb);
2150 }
2151 buffer_json_array_close(wb);
2152
2153 buffer_json_member_add_object(wb, "group_by");
2154 {
2155 buffer_json_member_add_object(wb, "type");
2156 {
2157 buffer_json_member_add_string(wb, "name", "Top Down Tree");
2158 buffer_json_member_add_array(wb, "columns");
2159 {
2160 buffer_json_add_array_item_string(wb, "type");
2161 buffer_json_add_array_item_string(wb, "loadState");
2162 buffer_json_add_array_item_string(wb, "activeState");
2163 buffer_json_add_array_item_string(wb, "subState");
2164 }
2165 buffer_json_array_close(wb);
2166 }
2167 buffer_json_object_close(wb);
2168
2169 buffer_json_member_add_object(wb, "subState");
2170 {
2171 buffer_json_member_add_string(wb, "name", "Bottom Up Tree");
2172 buffer_json_member_add_array(wb, "columns");
2173 {
2174 buffer_json_add_array_item_string(wb, "subState");
2175 buffer_json_add_array_item_string(wb, "activeState");
2176 buffer_json_add_array_item_string(wb, "loadState");
2177 buffer_json_add_array_item_string(wb, "type");
2178 }
2179 buffer_json_array_close(wb);
2180 }
2181 buffer_json_object_close(wb);
2182 }
2183 buffer_json_object_close(wb); // group_by
2184
2185 buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1);
2186 buffer_json_finalize(wb);
2187
2188 netdata_mutex_lock(&stdout_mutex);
2189 wb->response_code = HTTP_RESP_OK;
2190 wb->content_type = CT_APPLICATION_JSON;
2191 wb->expires = now_realtime_sec() + 1;
2192 pluginsd_function_result_to_stdout(transaction, wb);
2193 netdata_mutex_unlock(&stdout_mutex);
2194
2195 buffer_free(wb);
2196 systemd_units_free_all(base);
2197 }
2198
2199 static bool plugin_should_exit = false;
2200
2201 int main(int argc __maybe_unused, char **argv __maybe_unused)
2202 {
2203 nd_thread_tag_set("sd-unit.plugin");
2204 nd_log_initialize_for_external_plugins("systemd-units.plugin");
2205 netdata_threads_init_for_external_plugins(0);
2206
2207 netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
2208 if (verify_netdata_host_prefix(true) == -1)
2209 exit(1);
2210
2211 // ------------------------------------------------------------------------
2212 // debug
2213
2214 if (argc == 2 && strcmp(argv[1], "debug-units") == 0) {
2215 bool cancelled = false;
2216 usec_t stop_monotonic_ut = now_monotonic_usec() + 600 * USEC_PER_SEC;
2217 function_systemd_units(
2218 "123", "systemd-units", &stop_monotonic_ut, &cancelled, NULL, HTTP_ACCESS_ALL, NULL, NULL);
2219 exit(1);
2220 }
2221
2222 // ------------------------------------------------------------------------
2223 // the event loop for functions
2224
2225 struct functions_evloop_globals *wg =
2226 functions_evloop_init(ND_SD_JOURNAL_WORKER_THREADS, "SDU", &stdout_mutex, &plugin_should_exit, NULL);
2227
2228 functions_evloop_add_function(
2229 wg, ND_SD_UNITS_FUNCTION_NAME, function_systemd_units, ND_SD_UNITS_DEFAULT_TIMEOUT, NULL);
2230
2231 // ------------------------------------------------------------------------
2232 // register functions to netdata
2233
2234 netdata_mutex_lock(&stdout_mutex);
2235
2236 fprintf(
2237 stdout,
2238 PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"top\" " HTTP_ACCESS_FORMAT " %d\n",
2239 ND_SD_UNITS_FUNCTION_NAME,
2240 ND_SD_UNITS_DEFAULT_TIMEOUT,
2241 ND_SD_UNITS_FUNCTION_DESCRIPTION,
2242 (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA),
2243 RRDFUNCTIONS_PRIORITY_DEFAULT);
2244
2245 fflush(stdout);
2246 netdata_mutex_unlock(&stdout_mutex);
2247
2248 // ------------------------------------------------------------------------
2249
2250 usec_t send_newline_ut = 0;
2251 const bool tty = isatty(fileno(stdout)) == 1;
2252
2253 heartbeat_t hb;
2254 heartbeat_init(&hb, USEC_PER_SEC);
2255 while (!__atomic_load_n(&plugin_should_exit, __ATOMIC_ACQUIRE)) {
2256 usec_t dt_ut = heartbeat_next(&hb);
2257 send_newline_ut += dt_ut;
2258
2259 if (!tty && send_newline_ut > USEC_PER_SEC) {
2260 send_newline_and_flush(&stdout_mutex);
2261 send_newline_ut = 0;
2262 }
2263 }
2264
2265 exit(0);
2266 }