Skip to content

Commit f4f0de4

Browse files
committed
[metrics] Add a basic CLI
systemd-report will list all the metrics.
1 parent 3841b56 commit f4f0de4

4 files changed

Lines changed: 245 additions & 0 deletions

File tree

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,6 +2398,7 @@ subdir('src/random-seed')
23982398
subdir('src/rc-local-generator')
23992399
subdir('src/remount-fs')
24002400
subdir('src/repart')
2401+
subdir('src/report')
24012402
subdir('src/reply-password')
24022403
subdir('src/resolve')
24032404
subdir('src/rfkill')

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

0 commit comments

Comments
 (0)