]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/cgroup-show.c
Merge pull request #2056 from evverx/expose-soft-limits-on-the-bus
[thirdparty/systemd.git] / src / shared / cgroup-show.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <dirent.h>
23 #include <errno.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "alloc-util.h"
30 #include "cgroup-show.h"
31 #include "cgroup-util.h"
32 #include "fd-util.h"
33 #include "formats-util.h"
34 #include "locale-util.h"
35 #include "macro.h"
36 #include "output-mode.h"
37 #include "path-util.h"
38 #include "process-util.h"
39 #include "string-util.h"
40 #include "terminal-util.h"
41
42 static int compare(const void *a, const void *b) {
43 const pid_t *p = a, *q = b;
44
45 if (*p < *q)
46 return -1;
47 if (*p > *q)
48 return 1;
49 return 0;
50 }
51
52 static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) {
53 unsigned i, j, pid_width;
54
55 if (n_pids == 0)
56 return;
57
58 qsort(pids, n_pids, sizeof(pid_t), compare);
59
60 /* Filter duplicates */
61 for (j = 0, i = 1; i < n_pids; i++) {
62 if (pids[i] == pids[j])
63 continue;
64 pids[++j] = pids[i];
65 }
66 n_pids = j + 1;
67 pid_width = DECIMAL_STR_WIDTH(pids[j]);
68
69 if (flags & OUTPUT_FULL_WIDTH)
70 n_columns = 0;
71 else {
72 if (n_columns > pid_width+2)
73 n_columns -= pid_width+2;
74 else
75 n_columns = 20;
76 }
77 for (i = 0; i < n_pids; i++) {
78 _cleanup_free_ char *t = NULL;
79
80 get_process_cmdline(pids[i], n_columns, true, &t);
81
82 if (extra)
83 printf("%s%s ", prefix, draw_special_char(DRAW_TRIANGULAR_BULLET));
84 else
85 printf("%s%s", prefix, draw_special_char(((more || i < n_pids-1) ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT)));
86
87 printf("%*"PID_PRI" %s\n", pid_width, pids[i], strna(t));
88 }
89 }
90
91
92 static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads, OutputFlags flags) {
93 char *fn;
94 _cleanup_fclose_ FILE *f = NULL;
95 size_t n = 0, n_allocated = 0;
96 _cleanup_free_ pid_t *pids = NULL;
97 _cleanup_free_ char *p = NULL;
98 pid_t pid;
99 int r;
100
101 r = cg_mangle_path(path, &p);
102 if (r < 0)
103 return r;
104
105 fn = strjoina(p, "/cgroup.procs");
106 f = fopen(fn, "re");
107 if (!f)
108 return -errno;
109
110 while ((r = cg_read_pid(f, &pid)) > 0) {
111
112 if (!kernel_threads && is_kernel_thread(pid) > 0)
113 continue;
114
115 if (!GREEDY_REALLOC(pids, n_allocated, n + 1))
116 return -ENOMEM;
117
118 assert(n < n_allocated);
119 pids[n++] = pid;
120 }
121
122 if (r < 0)
123 return r;
124
125 show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads, flags);
126
127 return 0;
128 }
129
130 int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
131 _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;
132 _cleanup_closedir_ DIR *d = NULL;
133 char *gn = NULL;
134 bool shown_pids = false;
135 int r;
136
137 assert(path);
138
139 if (n_columns <= 0)
140 n_columns = columns();
141
142 if (!prefix)
143 prefix = "";
144
145 r = cg_mangle_path(path, &fn);
146 if (r < 0)
147 return r;
148
149 d = opendir(fn);
150 if (!d)
151 return -errno;
152
153 while ((r = cg_read_subgroup(d, &gn)) > 0) {
154 _cleanup_free_ char *k = NULL;
155
156 k = strjoin(fn, "/", gn, NULL);
157 free(gn);
158 if (!k)
159 return -ENOMEM;
160
161 if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0)
162 continue;
163
164 if (!shown_pids) {
165 show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads, flags);
166 shown_pids = true;
167 }
168
169 if (last) {
170 printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH), cg_unescape(basename(last)));
171
172 if (!p1) {
173 p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERTICAL));
174 if (!p1)
175 return -ENOMEM;
176 }
177
178 show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, flags);
179 free(last);
180 }
181
182 last = k;
183 k = NULL;
184 }
185
186 if (r < 0)
187 return r;
188
189 if (!shown_pids)
190 show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags);
191
192 if (last) {
193 printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), cg_unescape(basename(last)));
194
195 if (!p2) {
196 p2 = strappend(prefix, " ");
197 if (!p2)
198 return -ENOMEM;
199 }
200
201 show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, flags);
202 }
203
204 return 0;
205 }
206
207 int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) {
208 _cleanup_free_ char *p = NULL;
209 int r;
210
211 assert(path);
212
213 r = cg_get_path(controller, path, NULL, &p);
214 if (r < 0)
215 return r;
216
217 return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags);
218 }
219
220 static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids, OutputFlags flags) {
221 _cleanup_free_ pid_t *copy = NULL;
222 unsigned i, j;
223 int r;
224
225 assert(path);
226
227 if (n_pids <= 0)
228 return 0;
229
230 if (n_columns <= 0)
231 n_columns = columns();
232
233 prefix = strempty(prefix);
234
235 copy = new(pid_t, n_pids);
236 if (!copy)
237 return -ENOMEM;
238
239 for (i = 0, j = 0; i < n_pids; i++) {
240 _cleanup_free_ char *k = NULL;
241
242 r = cg_pid_get_path(controller, pids[i], &k);
243 if (r < 0)
244 return r;
245
246 if (path_startswith(k, path))
247 continue;
248
249 copy[j++] = pids[i];
250 }
251
252 show_pid_array(copy, j, prefix, n_columns, true, false, false, flags);
253
254 return 0;
255 }
256
257 int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
258 int r;
259
260 assert(path);
261
262 r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, flags);
263 if (r < 0)
264 return r;
265
266 return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);
267 }
268
269 int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) {
270 _cleanup_free_ char *controller = NULL, *path = NULL;
271 int r;
272
273 assert(spec);
274
275 r = cg_split_spec(spec, &controller, &path);
276 if (r < 0)
277 return r;
278
279 return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, extra_pids, n_extra_pids, flags);
280 }