summaryrefslogtreecommitdiffstats
path: root/src/analyze/analyze-dump.c
blob: 2e838c906f31a8a0f099cde486e5c6fb6c4e54c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "sd-bus.h"

#include "analyze-dump.h"
#include "analyze.h"
#include "bus-error.h"
#include "bus-locator.h"
#include "bus-util.h"
#include "copy.h"

static int dump_fallback(sd_bus *bus) {
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
        const char *text;
        int r;

        assert(bus);

        r = bus_call_method(bus, bus_systemd_mgr, "Dump", &error, &reply, NULL);
        if (r < 0)
                return log_error_errno(r, "Failed to call Dump: %s", bus_error_message(&error, r));

        r = sd_bus_message_read(reply, "s", &text);
        if (r < 0)
                return bus_log_parse_error(r);

        fputs(text, stdout);
        return 0;
}

static int dump_fd_reply(sd_bus_message *message) {
        int fd, r;

        r = sd_bus_message_read(message, "h", &fd);
        if (r < 0)
                return bus_log_parse_error(r);

        fflush(stdout);
        r = copy_bytes(fd, STDOUT_FILENO, UINT64_MAX, 0);
        if (r < 0)
                return r;

        return 1;  /* Success */
}

static int dump(sd_bus *bus) {
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
        int r;

        r = bus_call_method(bus, bus_systemd_mgr, "DumpByFileDescriptor", &error, &reply, NULL);
        if (IN_SET(r, -EACCES, -EBADR))
                return 0;  /* Fall back to non-fd method. We need to do this even if the bus supports sending
                            * fds to cater to very old managers which didn't have the fd-based method. */
        if (r < 0)
                return log_error_errno(r, "Failed to call DumpByFileDescriptor: %s",
                                       bus_error_message(&error, r));

        return dump_fd_reply(reply);
}

static int dump_patterns_fallback(sd_bus *bus, char **patterns) {
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
        const char *text;
        int r;

        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "DumpUnitsMatchingPatterns");
        if (r < 0)
                return bus_log_create_error(r);

        r = sd_bus_message_append_strv(m, patterns);
        if (r < 0)
                return bus_log_create_error(r);

        r = sd_bus_call(bus, m, 0, &error, &reply);
        if (r < 0)
                return log_error_errno(r, "Failed to call DumpUnitsMatchingPatterns: %s",
                                       bus_error_message(&error, r));

        r = sd_bus_message_read(reply, "s", &text);
        if (r < 0)
                return bus_log_parse_error(r);

        fputs(text, stdout);
        return 0;
}

static int dump_patterns(sd_bus *bus, char **patterns) {
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
        int r;

        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "DumpUnitsMatchingPatternsByFileDescriptor");
        if (r < 0)
                return bus_log_create_error(r);

        r = sd_bus_message_append_strv(m, patterns);
        if (r < 0)
                return bus_log_create_error(r);

        r = sd_bus_call(bus, m, 0, &error, &reply);
        if (r < 0)
                return log_error_errno(r, "Failed to call DumpUnitsMatchingPatternsByFileDescriptor: %s",
                                       bus_error_message(&error, r));

        return dump_fd_reply(reply);
}

static int mangle_patterns(char **args, char ***ret) {
        _cleanup_strv_free_ char **mangled = NULL;
        int r;

        STRV_FOREACH(arg, args) {
                char *t;

                r = unit_name_mangle_with_suffix(*arg, NULL, UNIT_NAME_MANGLE_GLOB, ".service", &t);
                if (r < 0)
                        return log_error_errno(r, "Failed to mangle name '%s': %m", *arg);

                r = strv_consume(&mangled, t);
                if (r < 0)
                        return log_oom();
        }

        if (strv_isempty(mangled))
                mangled = strv_free(mangled);

        *ret = TAKE_PTR(mangled);
        return 0;
}

int verb_dump(int argc, char *argv[], void *userdata) {
        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
        _cleanup_strv_free_ char **patterns = NULL;
        int r;

        r = acquire_bus(&bus, NULL);
        if (r < 0)
                return bus_log_connect_error(r, arg_transport);

        pager_open(arg_pager_flags);

        r = mangle_patterns(strv_skip(argv, 1), &patterns);
        if (r < 0)
                return r;

        r = sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD);
        if (r < 0)
                return log_error_errno(r, "Unable to determine if bus connection supports fd passing: %m");
        if (r > 0)
                r = patterns ? dump_patterns(bus, patterns) : dump(bus);
        if (r == 0) /* wasn't supported */
                r = patterns ? dump_patterns_fallback(bus, patterns) : dump_fallback(bus);
        if (r < 0)
                return r;

        return EXIT_SUCCESS;
}