]> git.proxmox.com Git - mirror_lxcfs.git/blame - sysfs_fuse.c
support /sys/devices/system/cpu/online
[mirror_lxcfs.git] / sysfs_fuse.c
CommitLineData
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
37static 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
71static 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);
132err:
133 free(cpuset);
134 free(cg);
135 return total_len;
136}
137
138static 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
157int 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
196int 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
231int 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
266int 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
283int sys_release(const char *path, struct fuse_file_info *fi)
284{
285 do_release_file_info(fi);
286 return 0;
287}
288
289int sys_releasedir(const char *path, struct fuse_file_info *fi)
290{
291 do_release_file_info(fi);
292 return 0;
293}
294
295int 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}