1
2
3
4
5
6
7
8
9
10
11
12
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <sys/socket.h>
16#include <sys/mount.h>
17#include <ctype.h>
18#include <fcntl.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <string.h>
23#include <errno.h>
24#include <limits.h>
25
26#include "utils.h"
27
28#ifndef HAVE_HANDLE_AT
29# include <sys/syscall.h>
30#endif
31
32#define CGROUP2_FS_NAME "cgroup2"
33
34
35#define MNT_CGRP2_PATH "/var/run/cgroup2"
36
37
38#ifndef HAVE_HANDLE_AT
39struct file_handle {
40 unsigned handle_bytes;
41 int handle_type;
42 unsigned char f_handle[];
43};
44
45static int name_to_handle_at(int dirfd, const char *pathname,
46 struct file_handle *handle, int *mount_id, int flags)
47{
48 return syscall(__NR_name_to_handle_at, dirfd, pathname, handle,
49 mount_id, flags);
50}
51
52static int open_by_handle_at(int mount_fd, struct file_handle *handle, int flags)
53{
54 return syscall(__NR_open_by_handle_at, mount_fd, handle, flags);
55}
56#endif
57
58
59static char *find_fs_mount(const char *fs_to_find)
60{
61 char path[4096];
62 char fstype[128];
63 char *mnt = NULL;
64 FILE *fp;
65
66 fp = fopen("/proc/mounts", "r");
67 if (!fp) {
68 fprintf(stderr,
69 "Failed to open mounts file: %s\n", strerror(errno));
70 return NULL;
71 }
72
73 while (fscanf(fp, "%*s %4095s %127s %*s %*d %*d\n",
74 path, fstype) == 2) {
75 if (strcmp(fstype, fs_to_find) == 0) {
76 mnt = strdup(path);
77 break;
78 }
79 }
80
81 fclose(fp);
82
83 return mnt;
84}
85
86
87char *find_cgroup2_mount(bool do_mount)
88{
89 char *mnt = find_fs_mount(CGROUP2_FS_NAME);
90
91 if (mnt)
92 return mnt;
93
94 if (!do_mount) {
95 fprintf(stderr, "Failed to find cgroup2 mount\n");
96 return NULL;
97 }
98
99 mnt = strdup(MNT_CGRP2_PATH);
100 if (!mnt) {
101 fprintf(stderr, "Failed to allocate memory for cgroup2 path\n");
102 return NULL;
103
104 }
105
106 if (make_path(mnt, 0755)) {
107 fprintf(stderr, "Failed to setup cgroup2 directory\n");
108 free(mnt);
109 return NULL;
110 }
111
112 if (mount("none", mnt, CGROUP2_FS_NAME, 0, NULL)) {
113
114 if (errno == EBUSY)
115 goto out;
116
117 if (errno == ENODEV) {
118 fprintf(stderr,
119 "Failed to mount cgroup2. Are CGROUPS enabled in your kernel?\n");
120 } else {
121 fprintf(stderr,
122 "Failed to mount cgroup2: %s\n",
123 strerror(errno));
124 }
125 free(mnt);
126 return NULL;
127 }
128out:
129 return mnt;
130}
131
132__u64 get_cgroup2_id(const char *path)
133{
134 char fh_buf[sizeof(struct file_handle) + sizeof(__u64)] = { 0 };
135 struct file_handle *fhp = (struct file_handle *)fh_buf;
136 union {
137 __u64 id;
138 unsigned char bytes[sizeof(__u64)];
139 } cg_id = { .id = 0 };
140 char *mnt = NULL;
141 int mnt_fd = -1;
142 int mnt_id;
143
144 if (!path) {
145 fprintf(stderr, "Invalid cgroup2 path\n");
146 return 0;
147 }
148
149 fhp->handle_bytes = sizeof(__u64);
150 if (name_to_handle_at(AT_FDCWD, path, fhp, &mnt_id, 0) < 0) {
151
152
153 while (*path == '/')
154 path++;
155 if (*path == '\0') {
156 fprintf(stderr, "Invalid cgroup2 path\n");
157 goto out;
158 }
159
160 mnt = find_cgroup2_mount(false);
161 if (!mnt)
162 goto out;
163
164 mnt_fd = open(mnt, O_RDONLY);
165 if (mnt_fd < 0) {
166 fprintf(stderr, "Failed to open cgroup2 mount\n");
167 goto out;
168 }
169
170 fhp->handle_bytes = sizeof(__u64);
171 if (name_to_handle_at(mnt_fd, path, fhp, &mnt_id, 0) < 0) {
172 fprintf(stderr, "Failed to get cgroup2 ID: %s\n",
173 strerror(errno));
174 goto out;
175 }
176 }
177 if (fhp->handle_bytes != sizeof(__u64)) {
178 fprintf(stderr, "Invalid size of cgroup2 ID\n");
179 goto out;
180 }
181
182 memcpy(cg_id.bytes, fhp->f_handle, sizeof(__u64));
183
184out:
185 if (mnt_fd >= 0)
186 close(mnt_fd);
187 free(mnt);
188
189 return cg_id.id;
190}
191
192#define FILEID_INO32_GEN 1
193
194
195char *get_cgroup2_path(__u64 id, bool full)
196{
197 char fh_buf[sizeof(struct file_handle) + sizeof(__u64)] = { 0 };
198 struct file_handle *fhp = (struct file_handle *)fh_buf;
199 union {
200 __u64 id;
201 unsigned char bytes[sizeof(__u64)];
202 } cg_id = { .id = id };
203 int mnt_fd = -1, fd = -1;
204 char link_buf[PATH_MAX];
205 char *path = NULL;
206 char fd_path[64];
207 int link_len;
208 char *mnt = NULL;
209
210 if (!id) {
211 fprintf(stderr, "Invalid cgroup2 ID\n");
212 goto out;
213 }
214
215 mnt = find_cgroup2_mount(false);
216 if (!mnt)
217 goto out;
218
219 mnt_fd = open(mnt, O_RDONLY);
220 if (mnt_fd < 0) {
221 fprintf(stderr, "Failed to open cgroup2 mount\n");
222 goto out;
223 }
224
225 fhp->handle_bytes = sizeof(__u64);
226 fhp->handle_type = FILEID_INO32_GEN;
227 memcpy(fhp->f_handle, cg_id.bytes, sizeof(__u64));
228
229 fd = open_by_handle_at(mnt_fd, fhp, 0);
230 if (fd < 0) {
231 fprintf(stderr, "Failed to open cgroup2 by ID\n");
232 goto out;
233 }
234
235 snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", fd);
236 link_len = readlink(fd_path, link_buf, sizeof(link_buf) - 1);
237 if (link_len < 0) {
238 fprintf(stderr,
239 "Failed to read value of symbolic link %s\n",
240 fd_path);
241 goto out;
242 }
243 link_buf[link_len] = '\0';
244
245 if (full)
246 path = strdup(link_buf);
247 else
248 path = strdup(link_buf + strlen(mnt));
249 if (!path)
250 fprintf(stderr,
251 "Failed to allocate memory for cgroup2 path\n");
252
253out:
254 if (fd >= 0)
255 close(fd);
256 if (mnt_fd >= 0)
257 close(mnt_fd);
258 free(mnt);
259
260 return path;
261}
262
263int make_path(const char *path, mode_t mode)
264{
265 char *dir, *delim;
266 int rc = -1;
267
268 delim = dir = strdup(path);
269 if (dir == NULL) {
270 fprintf(stderr, "strdup failed copying path");
271 return -1;
272 }
273
274
275 if (*delim == '/')
276 delim++;
277
278 while (1) {
279 delim = strchr(delim, '/');
280 if (delim)
281 *delim = '\0';
282
283 rc = mkdir(dir, mode);
284 if (rc && errno != EEXIST) {
285 fprintf(stderr, "mkdir failed for %s: %s\n",
286 dir, strerror(errno));
287 goto out;
288 }
289
290 if (delim == NULL)
291 break;
292
293 *delim = '/';
294 delim++;
295 if (*delim == '\0')
296 break;
297 }
298 rc = 0;
299out:
300 free(dir);
301
302 return rc;
303}
304
305int get_command_name(const char *pid, char *comm, size_t len)
306{
307 char path[PATH_MAX];
308 char line[128];
309 FILE *fp;
310
311 if (snprintf(path, sizeof(path),
312 "/proc/%s/status", pid) >= sizeof(path)) {
313 return -1;
314 }
315
316 fp = fopen(path, "r");
317 if (!fp)
318 return -1;
319
320 comm[0] = '\0';
321 while (fgets(line, sizeof(line), fp)) {
322 char *nl, *name;
323
324 name = strstr(line, "Name:");
325 if (!name)
326 continue;
327
328 name += 5;
329 while (isspace(*name))
330 name++;
331
332 nl = strchr(name, '\n');
333 if (nl)
334 *nl = '\0';
335
336 strlcpy(comm, name, len);
337 break;
338 }
339
340 fclose(fp);
341
342 return 0;
343}
344
345char *get_task_name(pid_t pid)
346{
347 char *comm;
348 FILE *f;
349
350 if (!pid)
351 return NULL;
352
353 if (asprintf(&comm, "/proc/%d/comm", pid) < 0)
354 return NULL;
355
356 f = fopen(comm, "r");
357 if (!f)
358 return NULL;
359
360 if (fscanf(f, "%ms\n", &comm) != 1)
361 comm = NULL;
362
363 fclose(f);
364
365 return comm;
366}
367