systemd-units improvements (#16333)
* fix memory leak * add more information about systemd units
Costa Tsaousis committed
Nov 4, 2023 at 12:11 UTC
9e1dc361855c1c79726905fec3aaefbf3a528868
3 files changed
+602
-9
collectors/systemd-journal.plugin/systemd-main.c
+7
@@ -44,6 +44,13 @@ int main(int argc __maybe_unused, char **argv __maybe_unused) {
44
// function_systemd_units("123", "systemd-units", 600, &cancelled);
45
exit(1);
46
}
47
+#ifdef ENABLE_SYSTEMD_DBUS
48
+ if(argc == 2 && strcmp(argv[1], "debug-units") == 0) {
49
+ bool cancelled = false;
50
+ function_systemd_units("123", "systemd-units", 600, &cancelled);
51
+ exit(1);
52
+ }
53
+#endif
54
55
// ------------------------------------------------------------------------
56
// the event loop for functions
collectors/systemd-journal.plugin/systemd-units.c
+583
-8
@@ -50,7 +50,7 @@ static inline bool streq_ptr(const sd_char *a, const sd_char *b) {
50
}
51
52
ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
53
- if (!key)
53
+ if (!key || !*key)
54
return -EINVAL;
55
56
for (size_t i = 0; i < len; ++i)
@@ -264,6 +264,15 @@ typedef enum TimerState {
264
_TIMER_STATE_INVALID = -EINVAL,
265
} TimerState;
266
267
+typedef enum FreezerState {
268
+ FREEZER_RUNNING,
269
+ FREEZER_FREEZING,
270
+ FREEZER_FROZEN,
271
+ FREEZER_THAWING,
272
+ _FREEZER_STATE_MAX,
273
+ _FREEZER_STATE_INVALID = -EINVAL,
274
+} FreezerState;
275
+
276
// ----------------------------------------------------------------------------
277
// copied from systemd: unit-def.c
278
@@ -449,6 +458,177 @@ static const char* const timer_state_table[_TIMER_STATE_MAX] = {
458
459
DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
460
461
+static const char* const freezer_state_table[_FREEZER_STATE_MAX] = {
462
+ [FREEZER_RUNNING] = "running",
463
+ [FREEZER_FREEZING] = "freezing",
464
+ [FREEZER_FROZEN] = "frozen",
465
+ [FREEZER_THAWING] = "thawing",
466
+};
467
+
468
+DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState);
469
+
470
+// ----------------------------------------------------------------------------
471
+// our code
472
+
473
+typedef struct UnitAttribute {
474
+ union {
475
+ int boolean;
476
+ char *str;
477
+ uint64_t uint64;
478
+ int64_t int64;
479
+ uint32_t uint32;
480
+ int32_t int32;
481
+ double dbl;
482
+ };
483
+} UnitAttribute;
484
+
485
+struct UnitInfo;
486
+typedef void (*attribute_handler_t)(struct UnitInfo *u, UnitAttribute *ua);
487
+
488
+static void update_freezer_state(struct UnitInfo *u, UnitAttribute *ua);
489
+
490
+struct {
491
+ const char *member;
492
+ char value_type;
493
+
494
+ const char *show_as;
495
+ const char *info;
496
+ RRDF_FIELD_OPTIONS options;
497
+ RRDF_FIELD_FILTER filter;
498
+
499
+ attribute_handler_t handler;
500
+} unit_attributes[] = {
501
+ {
502
+ .member = "Type",
503
+ .value_type = SD_BUS_TYPE_STRING,
504
+ .show_as = "ServiceType",
505
+ .info = "Service Type",
506
+ .options = RRDF_FIELD_OPTS_VISIBLE,
507
+ .filter = RRDF_FIELD_FILTER_MULTISELECT,
508
+ }, {
509
+ .member = "Result",
510
+ .value_type = SD_BUS_TYPE_STRING,
511
+ .show_as = "Result",
512
+ .info = "Result",
513
+ .options = RRDF_FIELD_OPTS_VISIBLE,
514
+ .filter = RRDF_FIELD_FILTER_MULTISELECT,
515
+ }, {
516
+ .member = "UnitFileState",
517
+ .value_type = SD_BUS_TYPE_STRING,
518
+ .show_as = "Enabled",
519
+ .info = "Unit File State",
520
+ .options = RRDF_FIELD_OPTS_VISIBLE,
521
+ .filter = RRDF_FIELD_FILTER_MULTISELECT,
522
+ }, {
523
+ .member = "UnitFilePreset",
524
+ .value_type = SD_BUS_TYPE_STRING,
525
+ .show_as = "Preset",
526
+ .info = "Unit File Preset",
527
+ .options = RRDF_FIELD_OPTS_VISIBLE,
528
+ .filter = RRDF_FIELD_FILTER_MULTISELECT,
529
+ }, {
530
+ .member = "FreezerState",
531
+ .value_type = SD_BUS_TYPE_STRING,
532
+ .show_as = "FreezerState",
533
+ .info = "Freezer State",
534
+ .options = RRDF_FIELD_OPTS_NONE,
535
+ .filter = RRDF_FIELD_FILTER_MULTISELECT,
536
+ .handler = update_freezer_state,
537
+ },
538
+// { .member = "Id", .signature = "s", },
539
+// { .member = "LoadState", .signature = "s", },
540
+// { .member = "ActiveState", .signature = "s", },
541
+// { .member = "SubState", .signature = "s", },
542
+// { .member = "Description", .signature = "s", },
543
+// { .member = "Following", .signature = "s", },
544
+// { .member = "Documentation", .signature = "as", },
545
+// { .member = "FragmentPath", .signature = "s", },
546
+// { .member = "SourcePath", .signature = "s", },
547
+// { .member = "ControlGroup", .signature = "s", },
548
+// { .member = "DropInPaths", .signature = "as", },
549
+// { .member = "LoadError", .signature = "(ss)", },
550
+// { .member = "TriggeredBy", .signature = "as", },
551
+// { .member = "Triggers", .signature = "as", },
552
+// { .member = "InactiveExitTimestamp", .signature = "t", },
553
+// { .member = "InactiveExitTimestampMonotonic", .signature = "t", },
554
+// { .member = "ActiveEnterTimestamp", .signature = "t", },
555
+// { .member = "ActiveExitTimestamp", .signature = "t", },
556
+// { .member = "RuntimeMaxUSec", .signature = "t", },
557
+// { .member = "InactiveEnterTimestamp", .signature = "t", },
558
+// { .member = "NeedDaemonReload", .signature = "b", },
559
+// { .member = "Transient", .signature = "b", },
560
+// { .member = "ExecMainPID", .signature = "u", },
561
+// { .member = "MainPID", .signature = "u", },
562
+// { .member = "ControlPID", .signature = "u", },
563
+// { .member = "StatusText", .signature = "s", },
564
+// { .member = "PIDFile", .signature = "s", },
565
+// { .member = "StatusErrno", .signature = "i", },
566
+// { .member = "FileDescriptorStoreMax", .signature = "u", },
567
+// { .member = "NFileDescriptorStore", .signature = "u", },
568
+// { .member = "ExecMainStartTimestamp", .signature = "t", },
569
+// { .member = "ExecMainExitTimestamp", .signature = "t", },
570
+// { .member = "ExecMainCode", .signature = "i", },
571
+// { .member = "ExecMainStatus", .signature = "i", },
572
+// { .member = "LogNamespace", .signature = "s", },
573
+// { .member = "ConditionTimestamp", .signature = "t", },
574
+// { .member = "ConditionResult", .signature = "b", },
575
+// { .member = "Conditions", .signature = "a(sbbsi)", },
576
+// { .member = "AssertTimestamp", .signature = "t", },
577
+// { .member = "AssertResult", .signature = "b", },
578
+// { .member = "Asserts", .signature = "a(sbbsi)", },
579
+// { .member = "NextElapseUSecRealtime", .signature = "t", },
580
+// { .member = "NextElapseUSecMonotonic", .signature = "t", },
581
+// { .member = "NAccepted", .signature = "u", },
582
+// { .member = "NConnections", .signature = "u", },
583
+// { .member = "NRefused", .signature = "u", },
584
+// { .member = "Accept", .signature = "b", },
585
+// { .member = "Listen", .signature = "a(ss)", },
586
+// { .member = "SysFSPath", .signature = "s", },
587
+// { .member = "Where", .signature = "s", },
588
+// { .member = "What", .signature = "s", },
589
+// { .member = "MemoryCurrent", .signature = "t", },
590
+// { .member = "MemoryAvailable", .signature = "t", },
591
+// { .member = "DefaultMemoryMin", .signature = "t", },
592
+// { .member = "DefaultMemoryLow", .signature = "t", },
593
+// { .member = "DefaultStartupMemoryLow", .signature = "t", },
594
+// { .member = "MemoryMin", .signature = "t", },
595
+// { .member = "MemoryLow", .signature = "t", },
596
+// { .member = "StartupMemoryLow", .signature = "t", },
597
+// { .member = "MemoryHigh", .signature = "t", },
598
+// { .member = "StartupMemoryHigh", .signature = "t", },
599
+// { .member = "MemoryMax", .signature = "t", },
600
+// { .member = "StartupMemoryMax", .signature = "t", },
601
+// { .member = "MemorySwapMax", .signature = "t", },
602
+// { .member = "StartupMemorySwapMax", .signature = "t", },
603
+// { .member = "MemoryZSwapMax", .signature = "t", },
604
+// { .member = "StartupMemoryZSwapMax", .signature = "t", },
605
+// { .member = "MemoryLimit", .signature = "t", },
606
+// { .member = "CPUUsageNSec", .signature = "t", },
607
+// { .member = "TasksCurrent", .signature = "t", },
608
+// { .member = "TasksMax", .signature = "t", },
609
+// { .member = "IPIngressBytes", .signature = "t", },
610
+// { .member = "IPEgressBytes", .signature = "t", },
611
+// { .member = "IOReadBytes", .signature = "t", },
612
+// { .member = "IOWriteBytes", .signature = "t", },
613
+// { .member = "ExecCondition", .signature = "a(sasbttttuii)", },
614
+// { .member = "ExecConditionEx", .signature = "a(sasasttttuii)", },
615
+// { .member = "ExecStartPre", .signature = "a(sasbttttuii)", },
616
+// { .member = "ExecStartPreEx", .signature = "a(sasasttttuii)", },
617
+// { .member = "ExecStart", .signature = "a(sasbttttuii)", },
618
+// { .member = "ExecStartEx", .signature = "a(sasasttttuii)", },
619
+// { .member = "ExecStartPost", .signature = "a(sasbttttuii)", },
620
+// { .member = "ExecStartPostEx", .signature = "a(sasasttttuii)", },
621
+// { .member = "ExecReload", .signature = "a(sasbttttuii)", },
622
+// { .member = "ExecReloadEx", .signature = "a(sasasttttuii)", },
623
+// { .member = "ExecStopPre", .signature = "a(sasbttttuii)", },
624
+// { .member = "ExecStop", .signature = "a(sasbttttuii)", },
625
+// { .member = "ExecStopEx", .signature = "a(sasasttttuii)", },
626
+// { .member = "ExecStopPost", .signature = "a(sasbttttuii)", },
627
+// { .member = "ExecStopPostEx", .signature = "a(sasasttttuii)", },
628
+};
629
+
630
+#define _UNIT_ATTRIBUTE_MAX (sizeof(unit_attributes) / sizeof(unit_attributes[0]))
631
+
632
typedef struct UnitInfo {
633
char *id;
634
char *type;
@@ -465,6 +645,7 @@ typedef struct UnitInfo {
645
UnitType UnitType;
646
UnitLoadState UnitLoadState;
647
UnitActiveState UnitActiveState;
648
+ FreezerState FreezerState;
649
650
union {
651
AutomountState AutomountState;
@@ -480,12 +661,263 @@ typedef struct UnitInfo {
661
TimerState TimerState;
662
};
663
664
+ struct UnitAttribute attributes[_UNIT_ATTRIBUTE_MAX];
665
+
666
FACET_ROW_SEVERITY severity;
667
uint32_t prio;
668
669
struct UnitInfo *prev, *next;
670
} UnitInfo;
671
672
+static void update_freezer_state(UnitInfo *u, UnitAttribute *ua) {
673
+ u->FreezerState = freezer_state_from_string(ua->str);
674
+}
675
+
676
+// ----------------------------------------------------------------------------
677
+// common helpers
678
+
679
+#define _cleanup_(x) __attribute__((__cleanup__(x)))
680
+
681
+static void log_dbus_error(int r, const char *msg) {
682
+ netdata_log_error("SYSTEMD_UNITS: %s failed with error %d (%s)", msg, r, strerror(-r));
683
+}
684
+
685
+// ----------------------------------------------------------------------------
686
+// attributes management
687
+
688
+static inline ssize_t unit_property_slot_from_string(const char *s) {
689
+ if(!s || !*s)
690
+ return -EINVAL;
691
+
692
+ for(size_t i = 0; i < _UNIT_ATTRIBUTE_MAX ;i++)
693
+ if(streq_ptr(unit_attributes[i].member, s))
694
+ return (ssize_t)i;
695
+
696
+ return -EINVAL;
697
+}
698
+
699
+static inline const char *unit_property_name_to_string_from_slot(ssize_t i) {
700
+ if(i >= 0 && i < (ssize_t)_UNIT_ATTRIBUTE_MAX)
701
+ return unit_attributes[i].member;
702
+
703
+ return NULL;
704
+}
705
+
706
+static inline void systemd_unit_free_property(char type, struct UnitAttribute *at) {
707
+ switch(type) {
708
+ case SD_BUS_TYPE_STRING:
709
+ case SD_BUS_TYPE_OBJECT_PATH:
710
+ freez(at->str);
711
+ at->str = NULL;
712
+ break;
713
+
714
+ default:
715
+ break;
716
+ }
717
+}
718
+
719
+static int systemd_unit_get_property(sd_bus_message *m, UnitInfo *u, const char *name) {
720
+ int r;
721
+ char type;
722
+
723
+ r = sd_bus_message_peek_type(m, &type, NULL);
724
+ if(r < 0) {
725
+ log_dbus_error(r, "sd_bus_message_peek_type()");
726
+ return r;
727
+ }
728
+
729
+ ssize_t slot = unit_property_slot_from_string(name);
730
+ if(slot < 0) {
731
+ // internal_error(true, "unused attribute '%s' for unit '%s'", name, u->id);
732
+ sd_bus_message_skip(m, NULL);
733
+ return 0;
734
+ }
735
+
736
+ systemd_unit_free_property(unit_attributes[slot].value_type, &u->attributes[slot]);
737
+
738
+ if(unit_attributes[slot].value_type != type) {
739
+ netdata_log_error("Type of field '%s' expected to be '%c' but found '%c'. Ignoring field.",
740
+ unit_attributes[slot].member, unit_attributes[slot].value_type, type);
741
+ sd_bus_message_skip(m, NULL);
742
+ return 0;
743
+ }
744
+
745
+ switch (type) {
746
+ case SD_BUS_TYPE_OBJECT_PATH:
747
+ case SD_BUS_TYPE_STRING: {
748
+ char *s;
749
+
750
+ r = sd_bus_message_read_basic(m, type, &s);
751
+ if(r < 0) {
752
+ log_dbus_error(r, "sd_bus_message_read_basic()");
753
+ return r;
754
+ }
755
+
756
+ if(s && *s)
757
+ u->attributes[slot].str = strdupz(s);
758
+ }
759
+ break;
760
+
761
+ case SD_BUS_TYPE_BOOLEAN: {
762
+ r = sd_bus_message_read_basic(m, type, &u->attributes[slot].boolean);
763
+ if(r < 0) {
764
+ log_dbus_error(r, "sd_bus_message_read_basic()");
765
+ return r;
766
+ }
767
+ }
768
+ break;
769
+
770
+ case SD_BUS_TYPE_UINT64: {
771
+ r = sd_bus_message_read_basic(m, type, &u->attributes[slot].uint64);
772
+ if(r < 0) {
773
+ log_dbus_error(r, "sd_bus_message_read_basic()");
774
+ return r;
775
+ }
776
+ }
777
+ break;
778
+
779
+ case SD_BUS_TYPE_INT64: {
780
+ r = sd_bus_message_read_basic(m, type, &u->attributes[slot].int64);
781
+ if(r < 0) {
782
+ log_dbus_error(r, "sd_bus_message_read_basic()");
783
+ return r;
784
+ }
785
+ }
786
+ break;
787
+
788
+ case SD_BUS_TYPE_UINT32: {
789
+ r = sd_bus_message_read_basic(m, type, &u->attributes[slot].uint32);
790
+ if(r < 0) {
791
+ log_dbus_error(r, "sd_bus_message_read_basic()");
792
+ return r;
793
+ }
794
+ }
795
+ break;
796
+
797
+ case SD_BUS_TYPE_INT32: {
798
+ r = sd_bus_message_read_basic(m, type, &u->attributes[slot].int32);
799
+ if(r < 0) {
800
+ log_dbus_error(r, "sd_bus_message_read_basic()");
801
+ return r;
802
+ }
803
+ }
804
+ break;
805
+
806
+ case SD_BUS_TYPE_DOUBLE: {
807
+ r = sd_bus_message_read_basic(m, type, &u->attributes[slot].dbl);
808
+ if(r < 0) {
809
+ log_dbus_error(r, "sd_bus_message_read_basic()");
810
+ return r;
811
+ }
812
+ }
813
+ break;
814
+
815
+ case SD_BUS_TYPE_ARRAY: {
816
+ internal_error(true, "member '%s' is an array", name);
817
+ sd_bus_message_skip(m, NULL);
818
+ return 0;
819
+ }
820
+ break;
821
+
822
+ default: {
823
+ internal_error(true, "unknown field type '%c' for key '%s'", type, name);
824
+ sd_bus_message_skip(m, NULL);
825
+ return 0;
826
+ }
827
+ break;
828
+ }
829
+
830
+ if(unit_attributes[slot].handler)
831
+ unit_attributes[slot].handler(u, &u->attributes[slot]);
832
+
833
+ return 0;
834
+}
835
+
836
+static int systemd_unit_get_all_properties(sd_bus *bus, UnitInfo *u) {
837
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
838
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
839
+ int r;
840
+
841
+ r = sd_bus_call_method(bus,
842
+ "org.freedesktop.systemd1",
843
+ u->unit_path,
844
+ "org.freedesktop.DBus.Properties",
845
+ "GetAll",
846
+ &error,
847
+ &m,
848
+ "s", "");
849
+ if (r < 0) {
850
+ log_dbus_error(r, "sd_bus_call_method(p1)");
851
+ return r;
852
+ }
853
+
854
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
855
+ if (r < 0) {
856
+ log_dbus_error(r, "sd_bus_message_enter_container(p2)");
857
+ return r;
858
+ }
859
+
860
+ int c = 0;
861
+ while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
862
+ const char *member, *contents;
863
+ c++;
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
+ for(UnitInfo *u = base ; u ;u = u->next)
913
+ systemd_unit_get_all_properties(bus, u);
914
+}
915
+
916
+
917
+
918
+// ----------------------------------------------------------------------------
919
+// main unit info
920
+
921
int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
922
assert(message);
923
assert(u);
@@ -571,10 +1003,6 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
1003
return r;
1004
}
1005
574
-static void log_dbus_error(int r, const char *msg) {
575
- netdata_log_error("SYSTEMD_UNITS: %s failed with error %d", msg, r);
576
-}
577
-
1006
static int hex_to_int(char c) {
1007
if (c >= '0' && c <= '9') return c - '0';
1008
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
@@ -604,9 +1032,10 @@ static void txt_decode(char *txt) {
1032
}
1033
1034
static UnitInfo *systemd_units_get_all(void) {
607
- sd_bus *bus = NULL;
608
- sd_bus_error error = SD_BUS_ERROR_NULL;
609
- sd_bus_message *reply = NULL;
1035
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
1036
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1037
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1038
+
1039
UnitInfo *base = NULL;
1040
int r;
1041
@@ -673,6 +1102,8 @@ static UnitInfo *systemd_units_get_all(void) {
1102
return base;
1103
}
1104
1105
+ systemd_units_get_all_properties(bus, base);
1106
+
1107
return base;
1108
}
1109
@@ -690,10 +1121,16 @@ void systemd_units_free_all(UnitInfo *base) {
1121
freez((void *)u->unit_path);
1122
freez((void *)u->job_type);
1123
freez((void *)u->job_path);
1124
+
1125
+ for(int i = 0; i < (ssize_t)_UNIT_ATTRIBUTE_MAX ;i++)
1126
+ systemd_unit_free_property(unit_attributes[i].value_type, &u->attributes[i]);
1127
+
1128
freez(u);
1129
}
1130
}
1131
1132
+// ----------------------------------------------------------------------------
1133
+
1134
static void netdata_systemd_units_function_help(const char *transaction) {
1135
BUFFER *wb = buffer_create(0, NULL);
1136
buffer_sprintf(wb,
@@ -740,6 +1177,8 @@ static void netdata_systemd_units_function_info(const char *transaction) {
1177
buffer_free(wb);
1178
}
1179
1180
+// ----------------------------------------------------------------------------
1181
+
1182
static void systemd_unit_priority(UnitInfo *u, size_t units) {
1183
uint32_t prio;
1184
@@ -841,6 +1280,18 @@ FACET_ROW_SEVERITY system_unit_severity(UnitInfo *u) {
1280
break;
1281
}
1282
1283
+ switch(u->FreezerState) {
1284
+ default:
1285
+ case FREEZER_FROZEN:
1286
+ case FREEZER_FREEZING:
1287
+ case FREEZER_THAWING:
1288
+ severity = if_less(severity, max_severity, FACET_ROW_SEVERITY_WARNING);
1289
+ break;
1290
+
1291
+ case FREEZER_RUNNING:
1292
+ break;
1293
+ }
1294
+
1295
switch(u->UnitType) {
1296
case UNIT_SERVICE:
1297
switch(u->ServiceState) {
@@ -1176,6 +1627,9 @@ void function_systemd_units(const char *transaction, char *function, int timeout
1627
buffer_json_member_add_string(wb, "help", SYSTEMD_UNITS_FUNCTION_DESCRIPTION);
1628
buffer_json_member_add_array(wb, "data");
1629
1630
+ size_t count[_UNIT_ATTRIBUTE_MAX] = { 0 };
1631
+ struct UnitAttribute max[_UNIT_ATTRIBUTE_MAX];
1632
+
1633
for(UnitInfo *u = base; u ;u = u->next) {
1634
buffer_json_add_array_item_array(wb);
1635
{
@@ -1197,6 +1651,53 @@ void function_systemd_units(const char *transaction, char *function, int timeout
1651
buffer_json_add_array_item_uint64(wb, u->job_id);
1652
buffer_json_add_array_item_string(wb, u->job_type);
1653
buffer_json_add_array_item_string(wb, u->job_path);
1654
+
1655
+ for(ssize_t i = 0; i < (ssize_t)_UNIT_ATTRIBUTE_MAX ;i++) {
1656
+ switch(unit_attributes[i].value_type) {
1657
+ case SD_BUS_TYPE_OBJECT_PATH:
1658
+ case SD_BUS_TYPE_STRING:
1659
+ buffer_json_add_array_item_string(wb, u->attributes[i].str && *u->attributes[i].str ? u->attributes[i].str : "-");
1660
+ break;
1661
+
1662
+ case SD_BUS_TYPE_UINT64:
1663
+ buffer_json_add_array_item_uint64(wb, u->attributes[i].uint64);
1664
+ if(!count[i]++) max[i].uint64 = 0;
1665
+ max[i].uint64 = MAX(max[i].uint64, u->attributes[i].uint64);
1666
+ break;
1667
+
1668
+ case SD_BUS_TYPE_UINT32:
1669
+ buffer_json_add_array_item_uint64(wb, u->attributes[i].uint32);
1670
+ if(!count[i]++) max[i].uint32 = 0;
1671
+ max[i].uint32 = MAX(max[i].uint32, u->attributes[i].uint32);
1672
+ break;
1673
+
1674
+ case SD_BUS_TYPE_INT64:
1675
+ buffer_json_add_array_item_uint64(wb, u->attributes[i].int64);
1676
+ if(!count[i]++) max[i].uint64 = 0;
1677
+ max[i].int64 = MAX(max[i].int64, u->attributes[i].int64);
1678
+ break;
1679
+
1680
+ case SD_BUS_TYPE_INT32:
1681
+ buffer_json_add_array_item_uint64(wb, u->attributes[i].int32);
1682
+ if(!count[i]++) max[i].int32 = 0;
1683
+ max[i].int32 = MAX(max[i].int32, u->attributes[i].int32);
1684
+ break;
1685
+
1686
+ case SD_BUS_TYPE_DOUBLE:
1687
+ buffer_json_add_array_item_double(wb, u->attributes[i].dbl);
1688
+ if(!count[i]++) max[i].dbl = 0.0;
1689
+ max[i].dbl = MAX(max[i].dbl, u->attributes[i].dbl);
1690
+ break;
1691
+
1692
+ case SD_BUS_TYPE_BOOLEAN:
1693
+ buffer_json_add_array_item_boolean(wb, u->attributes[i].boolean);
1694
+ break;
1695
+
1696
+ default:
1697
+ break;
1698
+ }
1699
+ }
1700
+
1701
buffer_json_add_array_item_uint64(wb, u->prio);
1702
buffer_json_add_array_item_uint64(wb, 1); // count
1703
}
@@ -1299,6 +1800,80 @@ void function_systemd_units(const char *transaction, char *function, int timeout
1800
RRDF_FIELD_OPTS_WRAP | RRDF_FIELD_OPTS_FULL_WIDTH,
1801
NULL);
1802
1803
+ for(ssize_t i = 0; i < (ssize_t)_UNIT_ATTRIBUTE_MAX ;i++) {
1804
+ char key[256], name[256];
1805
+
1806
+ if(unit_attributes[i].show_as)
1807
+ snprintfz(key, sizeof(key), "%s", unit_attributes[i].show_as);
1808
+ else
1809
+ snprintfz(key, sizeof(key), "attribute%s", unit_property_name_to_string_from_slot(i));
1810
+
1811
+ if(unit_attributes[i].info)
1812
+ snprintfz(name, sizeof(name), "%s", unit_attributes[i].info);
1813
+ else
1814
+ snprintfz(name, sizeof(name), "Attribute %s", unit_property_name_to_string_from_slot(i));
1815
+
1816
+ RRDF_FIELD_OPTIONS options = unit_attributes[i].options;
1817
+ RRDF_FIELD_FILTER filter = unit_attributes[i].filter;
1818
+
1819
+ switch(unit_attributes[i].value_type) {
1820
+ case SD_BUS_TYPE_OBJECT_PATH:
1821
+ case SD_BUS_TYPE_STRING:
1822
+ buffer_rrdf_table_add_field(wb, field_id++, key, name,
1823
+ RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1824
+ 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1825
+ RRDF_FIELD_SUMMARY_COUNT, filter,
1826
+ RRDF_FIELD_OPTS_WRAP | options,
1827
+ NULL);
1828
+ break;
1829
+
1830
+ case SD_BUS_TYPE_INT32:
1831
+ case SD_BUS_TYPE_UINT32:
1832
+ case SD_BUS_TYPE_INT64:
1833
+ case SD_BUS_TYPE_UINT64: {
1834
+ double m;
1835
+ if(unit_attributes[i].value_type == SD_BUS_TYPE_UINT64)
1836
+ m = (double)max[i].uint64;
1837
+ else if(unit_attributes[i].value_type == SD_BUS_TYPE_INT64)
1838
+ m = (double)max[i].int64;
1839
+ else if(unit_attributes[i].value_type == SD_BUS_TYPE_UINT32)
1840
+ m = (double)max[i].uint32;
1841
+ else if(unit_attributes[i].value_type == SD_BUS_TYPE_INT32)
1842
+ m = (double)max[i].int32;
1843
+
1844
+ buffer_rrdf_table_add_field(wb, field_id++, key, name,
1845
+ RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1846
+ 0, NULL, m, RRDF_FIELD_SORT_ASCENDING, NULL,
1847
+ RRDF_FIELD_SUMMARY_SUM, filter,
1848
+ RRDF_FIELD_OPTS_WRAP | options,
1849
+ NULL);
1850
+ }
1851
+ break;
1852
+
1853
+ case SD_BUS_TYPE_DOUBLE:
1854
+ buffer_rrdf_table_add_field(wb, field_id++, key, name,
1855
+ RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1856
+ 2, NULL, max[i].dbl, RRDF_FIELD_SORT_ASCENDING, NULL,
1857
+ RRDF_FIELD_SUMMARY_SUM, filter,
1858
+ RRDF_FIELD_OPTS_WRAP | options,
1859
+ NULL);
1860
+ break;
1861
+
1862
+ case SD_BUS_TYPE_BOOLEAN:
1863
+ buffer_rrdf_table_add_field(wb, field_id++, key, name,
1864
+ RRDF_FIELD_TYPE_BOOLEAN, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1865
+ 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
1866
+ RRDF_FIELD_SUMMARY_COUNT, filter,
1867
+ RRDF_FIELD_OPTS_WRAP | options,
1868
+ NULL);
1869
+ break;
1870
+
1871
+ default:
1872
+ break;
1873
+ }
1874
+
1875
+ }
1876
+
1877
buffer_rrdf_table_add_field(wb, field_id++, "priority", "Priority",
1878
RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
1879
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
libnetdata/buffer/buffer.h
+12
-1
@@ -860,6 +860,13 @@ static inline void buffer_json_add_array_item_uint64(BUFFER *wb, uint64_t value)
860
wb->json.stack[wb->json.depth].count++;
861
}
862
863
+static inline void buffer_json_add_array_item_boolean(BUFFER *wb, bool value) {
864
+ buffer_print_json_comma_newline_spacing(wb);
865
+
866
+ buffer_strcat(wb, value ? "true" : "false");
867
+ wb->json.stack[wb->json.depth].count++;
868
+}
869
+
870
static inline void buffer_json_add_array_item_time_t(BUFFER *wb, time_t value) {
871
buffer_print_json_comma_newline_spacing(wb);
872
@@ -966,6 +973,7 @@ typedef enum __attribute__((packed)) {
973
typedef enum __attribute__((packed)) {
974
RRDF_FIELD_TYPE_NONE,
975
RRDF_FIELD_TYPE_INTEGER,
976
+ RRDF_FIELD_TYPE_BOOLEAN,
977
RRDF_FIELD_TYPE_STRING,
978
RRDF_FIELD_TYPE_DETAIL_STRING,
979
RRDF_FIELD_TYPE_BAR_WITH_INTEGER,
@@ -983,6 +991,9 @@ static inline const char *rrdf_field_type_to_string(RRDF_FIELD_TYPE type) {
991
case RRDF_FIELD_TYPE_INTEGER:
992
return "integer";
993
994
+ case RRDF_FIELD_TYPE_BOOLEAN:
995
+ return "boolean";
996
+
997
case RRDF_FIELD_TYPE_STRING:
998
return "string";
999
@@ -1113,7 +1124,7 @@ static inline const char *rrdf_field_summary_to_string(RRDF_FIELD_SUMMARY summar
1124
}
1125
1126
typedef enum __attribute__((packed)) {
1116
- RRDF_FIELD_FILTER_NONE,
1127
+ RRDF_FIELD_FILTER_NONE = 0,
1128
RRDF_FIELD_FILTER_RANGE,
1129
RRDF_FIELD_FILTER_MULTISELECT,
1130
RRDF_FIELD_FILTER_FACET,