Skip to content

Commit 067174f

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

4 files changed

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