@cryptotaxi247 / netdata-1 / commits / 9e1dc3618

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,