Skip to content

Commit deeed86

Browse files
committed
[metrics] Add a basic CLI
systemd-report will list all the metrics.
1 parent 6c0ba4e commit deeed86

3 files changed

Lines changed: 239 additions & 0 deletions

File tree

src/report/meson.build

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-License-Identifier: LGPL-2.1-or-later
2+
3+
executables += [
4+
executable_template + {
5+
'name' : 'systemd-report',
6+
'public' : true,
7+
'sources' : files('report.c'),
8+
},
9+
]

src/report/report.c

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2+
3+
#include <getopt.h>
4+
5+
#include "alloc-util.h"
6+
#include "build.h"
7+
#include "dirent-util.h"
8+
#include "fd-util.h"
9+
#include "log.h"
10+
#include "main-func.h"
11+
#include "sd-varlink.h"
12+
#include "string-util.h"
13+
#include "sd-event.h"
14+
#include "time-util.h"
15+
16+
#define RUN_SYSTEMD_METRICS_PATH "/run/systemd/metrics/"
17+
#define MAX_CONCURRENT_METRICS_SOCKETS 20
18+
#define TIMEOUT_USEC (30*USEC_PER_SEC) /* 30 seconds */
19+
20+
typedef struct Context {
21+
int n_ref;
22+
} Context;
23+
24+
static int metrics_on_query_reply(
25+
sd_varlink *link,
26+
sd_json_variant *parameters,
27+
const char *error_id,
28+
sd_varlink_reply_flags_t flags,
29+
void *userdata) {
30+
assert(link);
31+
32+
Context* context = ASSERT_PTR(userdata);
33+
34+
if (error_id) {
35+
bool disconnect = streq(error_id, SD_VARLINK_ERROR_DISCONNECTED);
36+
if (disconnect)
37+
log_info("Disconnected.");
38+
else
39+
log_error("Varlink error: %s", error_id);
40+
41+
(void) sd_event_exit(ASSERT_PTR(sd_varlink_get_event(link)), disconnect ? EXIT_SUCCESS : EXIT_FAILURE);
42+
return 0;
43+
}
44+
45+
sd_json_variant_dump(parameters, SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO, stdout, NULL);
46+
47+
fflush(stdout);
48+
49+
if (FLAGS_SET(flags, SD_VARLINK_REPLY_ERROR)) {
50+
(void) sd_event_exit(ASSERT_PTR(sd_varlink_get_event(link)), EXIT_FAILURE);
51+
return 0;
52+
}
53+
54+
if ((flags == 0) && (--context->n_ref <= 0))
55+
(void) sd_event_exit(ASSERT_PTR(sd_varlink_get_event(link)), EXIT_SUCCESS);
56+
57+
return 0;
58+
}
59+
60+
static int metrics_call(const char *path, sd_event *event, sd_varlink *vl, Context *context) {
61+
int r;
62+
63+
assert(path);
64+
assert(event);
65+
assert(context);
66+
67+
r = sd_varlink_connect_address(&vl, path);
68+
if (r < 0)
69+
return log_debug_errno(r, "Unable to connect to %s: %m", path);
70+
71+
(void) sd_varlink_set_userdata(vl, context);
72+
73+
r = sd_varlink_attach_event(vl, event, SD_EVENT_PRIORITY_NORMAL);
74+
if (r < 0)
75+
return log_debug_errno(r, "Failed to attach varlink connection to event loop: %m");
76+
77+
r = sd_varlink_bind_reply(vl, metrics_on_query_reply);
78+
if (r < 0)
79+
return log_debug_errno(r, "Failed to bind reply callback: %m");
80+
81+
r = sd_varlink_observe(vl, "io.systemd.Metrics.List", /* parameter */ NULL);
82+
if (r < 0)
83+
return log_debug_errno(r, "Failed to issue io.systemd.Metrics.List call: %m");
84+
85+
return 0;
86+
}
87+
88+
static void sd_varlink_unref_many(sd_varlink **array, size_t n) {
89+
FOREACH_ARRAY(v, array, n)
90+
sd_varlink_unref(*v);
91+
92+
93+
free(array);
94+
}
95+
96+
static int timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
97+
return sd_event_exit(ASSERT_PTR(sd_event_source_get_event(es)), EXIT_FAILURE);
98+
}
99+
100+
static int metrics_start_query(void) {
101+
_cleanup_closedir_ DIR *d = NULL;
102+
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
103+
int r, c;
104+
105+
d = opendir(RUN_SYSTEMD_METRICS_PATH);
106+
if (!d) {
107+
if (errno == ENOENT)
108+
return -ESRCH;
109+
110+
return -errno;
111+
}
112+
113+
r = sd_event_default(&event);
114+
if (r < 0)
115+
return log_error_errno(r, "Failed to get event loop: %m");
116+
117+
r = sd_event_set_signal_exit(event, true);
118+
if (r < 0)
119+
return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m");
120+
121+
sd_varlink *varlinks[MAX_CONCURRENT_METRICS_SOCKETS] = {};
122+
size_t n_varlinks = MAX_CONCURRENT_METRICS_SOCKETS;
123+
Context context = {};
124+
int i = 0;
125+
126+
CLEANUP_ARRAY(varlinks, n_varlinks, sd_varlink_unref_many);
127+
128+
FOREACH_DIRENT(de, d, return -errno) {
129+
_cleanup_free_ char *p = NULL;
130+
131+
if (de->d_type != DT_SOCK && de->d_type != DT_UNKNOWN)
132+
continue;
133+
134+
p = path_join(RUN_SYSTEMD_METRICS_PATH, de->d_name);
135+
if (!p)
136+
return -ENOMEM;
137+
138+
context.n_ref++;
139+
140+
r = metrics_call(p, event, varlinks[i], &context);
141+
if (r < 0) {
142+
context.n_ref--;
143+
continue;
144+
}
145+
146+
if (++i >= MAX_CONCURRENT_METRICS_SOCKETS)
147+
break;
148+
}
149+
150+
r = sd_event_add_time_relative(event, NULL, CLOCK_MONOTONIC, 100, 0, timeout_callback, NULL);
151+
if (r < 0)
152+
return log_error_errno(r, "Failed to add timer event: %m");
153+
154+
r = sd_event_loop(event);
155+
if (r < 0)
156+
r = log_error_errno(r, "Failed to run event loop: %m");
157+
158+
r = sd_event_get_exit_code(event, &c);
159+
if (r < 0)
160+
r = log_error_errno(r, "Failed to get exit code: %m");
161+
162+
return c;
163+
}
164+
165+
static int help(void) {
166+
printf("%s [OPTIONS...]\n\n"
167+
"Print metrics for all systemd components.\n\n"
168+
" -h --help Show this help\n"
169+
" --version Show package version\n",
170+
program_invocation_short_name);
171+
172+
return 0;
173+
}
174+
175+
static int parse_argv(int argc, char *argv[]) {
176+
enum {
177+
ARG_VERSION = 0x100
178+
};
179+
180+
static const struct option options[] = {
181+
{ "help", no_argument, NULL, 'h' },
182+
{ "version", no_argument, NULL, ARG_VERSION },
183+
{}
184+
};
185+
186+
int c;
187+
188+
assert(argc >= 0);
189+
assert(argv);
190+
191+
while ((c = getopt_long(argc, argv, "hp", options, NULL)) >= 0)
192+
switch (c) {
193+
case 'h':
194+
return help();
195+
case ARG_VERSION:
196+
return version();
197+
case '?':
198+
return -EINVAL;
199+
default:
200+
assert_not_reached();
201+
}
202+
203+
if (optind < argc)
204+
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
205+
"%s takes no arguments.",
206+
program_invocation_short_name);
207+
208+
return 1;
209+
}
210+
211+
static int run(int argc, char *argv[]) {
212+
int r;
213+
214+
log_setup();
215+
216+
r = parse_argv(argc, argv);
217+
if (r <= 0)
218+
return r;
219+
220+
r = metrics_start_query();
221+
if (r < 0)
222+
return r;
223+
224+
return 0;
225+
}
226+
227+
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);

test/units/TEST-74-AUX-UTILS.varlinkctl.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,6 @@ systemd-run --wait --pipe --user --machine testuser@ \
219219
# test io.systemd.Unit in user manager
220220
systemd-run --wait --pipe --user --machine testuser@ \
221221
varlinkctl --more call "/run/user/$testuser_uid/systemd/io.systemd.Manager" io.systemd.Unit.List '{}'
222+
223+
# test report
224+
systemd-report

0 commit comments

Comments
 (0)