]>
Commit | Line | Data |
---|---|---|
71f17cd2 YB |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
3 | #define FUSE_USE_VERSION 26 | |
4 | ||
5 | #define __STDC_FORMAT_MACROS | |
6 | #include <dirent.h> | |
7 | #include <errno.h> | |
8 | #include <fcntl.h> | |
9 | #include <fuse.h> | |
10 | #include <inttypes.h> | |
11 | #include <libgen.h> | |
12 | #include <pthread.h> | |
13 | #include <sched.h> | |
14 | #include <stdbool.h> | |
15 | #include <stdint.h> | |
16 | #include <stdio.h> | |
17 | #include <stdlib.h> | |
18 | #include <string.h> | |
19 | #include <time.h> | |
20 | #include <unistd.h> | |
21 | #include <wait.h> | |
22 | #include <linux/magic.h> | |
23 | #include <linux/sched.h> | |
24 | #include <sys/epoll.h> | |
25 | #include <sys/mman.h> | |
26 | #include <sys/mount.h> | |
27 | #include <sys/param.h> | |
28 | #include <sys/socket.h> | |
29 | #include <sys/syscall.h> | |
30 | #include <sys/sysinfo.h> | |
31 | #include <sys/vfs.h> | |
32 | ||
33 | #include "bindings.h" | |
34 | #include "config.h" // for VERSION | |
35 | #include "sysfs_fuse.h" | |
36 | ||
37 | static int sys_devices_system_cpu_read(char *buf, size_t size, off_t offset, | |
38 | struct fuse_file_info *fi) | |
39 | { | |
40 | struct file_info *d = (struct file_info *)fi->fh; | |
41 | char *cache = d->buf; | |
42 | ssize_t total_len = 0; | |
43 | ||
44 | if (offset) { | |
45 | if (!d->cached) | |
46 | return 0; | |
47 | if (offset > d->size) | |
48 | return -EINVAL; | |
49 | int left = d->size - offset; | |
50 | total_len = left > size ? size : left; | |
51 | memcpy(buf, cache + offset, total_len); | |
52 | return total_len; | |
53 | } | |
54 | ||
55 | total_len = snprintf(d->buf, d->buflen, "0-1\n"); | |
56 | if (total_len < 0 || total_len >= d->buflen) { | |
57 | lxcfs_error("%s\n", "failed to write to cache"); | |
58 | return 0; | |
59 | } | |
60 | ||
61 | d->size = (int)total_len; | |
62 | d->cached = 1; | |
63 | ||
64 | if (total_len > size) | |
65 | total_len = size; | |
66 | ||
67 | memcpy(buf, d->buf, total_len); | |
68 | return total_len; | |
69 | } | |
70 | ||
71 | static int sys_devices_system_cpu_online_read(char *buf, size_t size, | |
72 | off_t offset, | |
73 | struct fuse_file_info *fi) | |
74 | { | |
75 | struct fuse_context *fc = fuse_get_context(); | |
76 | struct file_info *d = (struct file_info *)fi->fh; | |
77 | char *cache = d->buf; | |
78 | char *cg; | |
79 | char *cpuset = NULL; | |
80 | bool use_view; | |
81 | ||
82 | int max_cpus = 0; | |
83 | pid_t initpid; | |
84 | ssize_t total_len = 0; | |
85 | ||
86 | if (offset) { | |
87 | if (!d->cached) | |
88 | return 0; | |
89 | if (offset > d->size) | |
90 | return -EINVAL; | |
91 | int left = d->size - offset; | |
92 | total_len = left > size ? size : left; | |
93 | memcpy(buf, cache + offset, total_len); | |
94 | return total_len; | |
95 | } | |
96 | ||
97 | initpid = lookup_initpid_in_store(fc->pid); | |
98 | if (initpid <= 0) | |
99 | initpid = fc->pid; | |
100 | cg = get_pid_cgroup(initpid, "cpuset"); | |
101 | if (!cg) | |
102 | return read_file("/sys/devices/system/cpu/online", buf, size, d); | |
103 | prune_init_slice(cg); | |
104 | ||
105 | cpuset = get_cpuset(cg); | |
106 | if (!cpuset) | |
107 | goto err; | |
108 | ||
109 | use_view = use_cpuview(cg); | |
110 | ||
111 | if (use_view) | |
112 | max_cpus = max_cpu_count(cg); | |
113 | ||
114 | if (max_cpus == 0) | |
115 | return read_file("/sys/devices/system/cpu/online", buf, size, d); | |
116 | if (max_cpus > 1) | |
117 | total_len = snprintf(d->buf, d->buflen, "0-%d\n", max_cpus - 1); | |
118 | else | |
119 | total_len = snprintf(d->buf, d->buflen, "0\n"); | |
120 | if (total_len < 0 || total_len >= d->buflen) { | |
121 | lxcfs_error("%s\n", "failed to write to cache"); | |
122 | return 0; | |
123 | } | |
124 | ||
125 | d->size = (int)total_len; | |
126 | d->cached = 1; | |
127 | ||
128 | if (total_len > size) | |
129 | total_len = size; | |
130 | ||
131 | memcpy(buf, d->buf, total_len); | |
132 | err: | |
133 | free(cpuset); | |
134 | free(cg); | |
135 | return total_len; | |
136 | } | |
137 | ||
138 | static off_t get_sysfile_size(const char *which) | |
139 | { | |
140 | FILE *f; | |
141 | char *line = NULL; | |
142 | size_t len = 0; | |
143 | ssize_t sz, answer = 0; | |
144 | ||
145 | f = fopen(which, "r"); | |
146 | if (!f) | |
147 | return 0; | |
148 | ||
149 | while ((sz = getline(&line, &len, f)) != -1) | |
150 | answer += sz; | |
151 | fclose(f); | |
152 | free(line); | |
153 | ||
154 | return answer; | |
155 | } | |
156 | ||
157 | int sys_getattr(const char *path, struct stat *sb) | |
158 | { | |
159 | struct timespec now; | |
160 | ||
161 | memset(sb, 0, sizeof(struct stat)); | |
162 | if (clock_gettime(CLOCK_REALTIME, &now) < 0) | |
163 | return -EINVAL; | |
164 | sb->st_uid = sb->st_gid = 0; | |
165 | sb->st_atim = sb->st_mtim = sb->st_ctim = now; | |
166 | if (strcmp(path, "/sys") == 0) { | |
167 | sb->st_mode = S_IFDIR | 00555; | |
168 | sb->st_nlink = 2; | |
169 | return 0; | |
170 | } | |
171 | if (strcmp(path, "/sys/devices") == 0) { | |
172 | sb->st_mode = S_IFDIR | 00555; | |
173 | sb->st_nlink = 2; | |
174 | return 0; | |
175 | } | |
176 | if (strcmp(path, "/sys/devices/system") == 0) { | |
177 | sb->st_mode = S_IFDIR | 00555; | |
178 | sb->st_nlink = 2; | |
179 | return 0; | |
180 | } | |
181 | if (strcmp(path, "/sys/devices/system/cpu") == 0) { | |
182 | sb->st_mode = S_IFDIR | 00555; | |
183 | sb->st_nlink = 2; | |
184 | return 0; | |
185 | } | |
186 | if (strcmp(path, "/sys/devices/system/cpu/online") == 0) { | |
187 | sb->st_size = 0; | |
188 | sb->st_mode = S_IFREG | 00444; | |
189 | sb->st_nlink = 1; | |
190 | return 0; | |
191 | } | |
192 | ||
193 | return -ENOENT; | |
194 | } | |
195 | ||
196 | int sys_readdir(const char *path, void *buf, fuse_fill_dir_t filler, | |
197 | off_t offset, struct fuse_file_info *fi) | |
198 | { | |
199 | if (strcmp(path, "/sys") == 0) { | |
200 | if (filler(buf, ".", NULL, 0) != 0 || | |
201 | filler(buf, "..", NULL, 0) != 0 || | |
202 | filler(buf, "devices", NULL, 0) != 0) | |
203 | return -ENOENT; | |
204 | return 0; | |
205 | } | |
206 | if (strcmp(path, "/sys/devices") == 0) { | |
207 | if (filler(buf, ".", NULL, 0) != 0 || | |
208 | filler(buf, "..", NULL, 0) != 0 || | |
209 | filler(buf, "system", NULL, 0) != 0) | |
210 | return -ENOENT; | |
211 | return 0; | |
212 | } | |
213 | if (strcmp(path, "/sys/devices/system") == 0) { | |
214 | if (filler(buf, ".", NULL, 0) != 0 || | |
215 | filler(buf, "..", NULL, 0) != 0 || | |
216 | filler(buf, "cpu", NULL, 0) != 0) | |
217 | return -ENOENT; | |
218 | return 0; | |
219 | } | |
220 | if (strcmp(path, "/sys/devices/system/cpu") == 0) { | |
221 | if (filler(buf, ".", NULL, 0) != 0 || | |
222 | filler(buf, "..", NULL, 0) != 0 || | |
223 | filler(buf, "online", NULL, 0) != 0) | |
224 | return -ENOENT; | |
225 | return 0; | |
226 | } | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | int sys_open(const char *path, struct fuse_file_info *fi) | |
232 | { | |
233 | int type = -1; | |
234 | struct file_info *info; | |
235 | ||
236 | if (strcmp(path, "/sys/devices") == 0) | |
237 | type = LXC_TYPE_SYS_DEVICES; | |
238 | if (strcmp(path, "/sys/devices/system") == 0) | |
239 | type = LXC_TYPE_SYS_DEVICES_SYSTEM; | |
240 | if (strcmp(path, "/sys/devices/system/cpu") == 0) | |
241 | type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU; | |
242 | if (strcmp(path, "/sys/devices/system/cpu/online") == 0) | |
243 | type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE; | |
244 | if (type == -1) | |
245 | return -ENOENT; | |
246 | ||
247 | info = malloc(sizeof(*info)); | |
248 | if (!info) | |
249 | return -ENOMEM; | |
250 | ||
251 | memset(info, 0, sizeof(*info)); | |
252 | info->type = type; | |
253 | ||
254 | info->buflen = get_sysfile_size(path) + BUF_RESERVE_SIZE; | |
255 | do { | |
256 | info->buf = malloc(info->buflen); | |
257 | } while (!info->buf); | |
258 | memset(info->buf, 0, info->buflen); | |
259 | /* set actual size to buffer size */ | |
260 | info->size = info->buflen; | |
261 | ||
262 | fi->fh = (unsigned long)info; | |
263 | return 0; | |
264 | } | |
265 | ||
266 | int sys_access(const char *path, int mask) | |
267 | { | |
268 | if (strcmp(path, "/sys") == 0 && access(path, R_OK) == 0) | |
269 | return 0; | |
270 | if (strcmp(path, "/sys/devices") == 0 && access(path, R_OK) == 0) | |
271 | return 0; | |
272 | if (strcmp(path, "/sys/devices/system") == 0 && access(path, R_OK) == 0) | |
273 | return 0; | |
274 | if (strcmp(path, "/sys/devices/system/cpu") == 0 && | |
275 | access(path, R_OK) == 0) | |
276 | return 0; | |
277 | /* these are all read-only */ | |
278 | if ((mask & ~R_OK) != 0) | |
279 | return -EACCES; | |
280 | return 0; | |
281 | } | |
282 | ||
283 | int sys_release(const char *path, struct fuse_file_info *fi) | |
284 | { | |
285 | do_release_file_info(fi); | |
286 | return 0; | |
287 | } | |
288 | ||
289 | int sys_releasedir(const char *path, struct fuse_file_info *fi) | |
290 | { | |
291 | do_release_file_info(fi); | |
292 | return 0; | |
293 | } | |
294 | ||
295 | int sys_read(const char *path, char *buf, size_t size, off_t offset, | |
296 | struct fuse_file_info *fi) | |
297 | { | |
298 | struct file_info *f = (struct file_info *)fi->fh; | |
299 | ||
300 | switch (f->type) { | |
301 | case LXC_TYPE_SYS_DEVICES: | |
302 | return sys_devices_read(buf, size, offset, fi); | |
303 | case LXC_TYPE_SYS_DEVICES_SYSTEM: | |
304 | return sys_devices_system_read(buf, size, offset, fi); | |
305 | case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU: | |
306 | return sys_devices_system_cpu_read(buf, size, offset, fi); | |
307 | case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE: | |
308 | return sys_devices_system_cpu_online_read(buf, size, offset, fi); | |
309 | default: | |
310 | return -EINVAL; | |
311 | } | |
312 | } |