]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/posixshmcontrol/posixshmcontrol.c
MFC r358826:
[FreeBSD/FreeBSD.git] / usr.bin / posixshmcontrol / posixshmcontrol.c
1 /*-
2  * Copyright (c) 2019 The FreeBSD Foundation
3  *
4  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
5  * under sponsorship from the FreeBSD Foundation.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/mman.h>
34 #include <sys/stat.h>
35 #include <sys/sysctl.h>
36 #include <sys/user.h>
37 #include <err.h>
38 #include <fcntl.h>
39 #include <grp.h>
40 #include <libutil.h>
41 #include <pwd.h>
42 #include <stdbool.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 static void
49 usage(void)
50 {
51
52         fprintf(stderr, "Usage:\n"
53             "posixshmcontrol create [-m <mode>] <path> ...\n"
54             "posixshmcontrol rm <path> ...\n"
55             "posixshmcontrol ls [-h] [-n]\n"
56             "posixshmcontrol dump <path> ...\n"
57             "posixshmcontrol stat [-h] [-n] <path> ...\n"
58             "posixshmcontrol truncate [-s <newlen>] <path> ...\n");
59 }
60
61 static int
62 create_one_shm(const char *path, long mode)
63 {
64         int fd;
65
66         fd = shm_open(path, O_RDWR | O_CREAT, mode);
67         if (fd == -1) {
68                 warn("create %s", path);
69                 return (1);
70         }
71         close(fd);
72         return (0);
73 }
74
75 static int
76 create_shm(int argc, char **argv)
77 {
78         char *end;
79         long mode;
80         int c, i, ret, ret1;
81
82         mode = 0600;
83         while ((c = getopt(argc, argv, "m:")) != -1) {
84                 switch (c) {
85                 case 'm':
86                         errno = 0;
87                         mode = strtol(optarg, &end, 0);
88                         if (mode == 0 && errno != 0)
89                                 err(1, "mode:");
90                         if (*end != '\0')
91                                 errx(1, "non-integer mode");
92                         break;
93                 case '?':
94                 default:
95                         usage();
96                         return (2);
97                 }
98         }
99
100         argc -= optind;
101         argv += optind;
102         ret = 0;
103         for (i = 0; i < argc; i++) {
104                 ret1 = create_one_shm(argv[i], mode);
105                 if (ret1 != 0 && ret == 0)
106                         ret = ret1;
107         }
108         return (ret);
109 }
110
111 static int
112 delete_one_shm(const char *path)
113 {
114         int error, ret;
115
116         error = shm_unlink(path);
117         if (error != 0) {
118                 warn("unlink of %s failed", path);
119                 ret = 1;
120         } else {
121                 ret = 0;
122         }
123         return (ret);
124 }
125
126 static int
127 delete_shm(int argc, char **argv)
128 {
129         int i, ret, ret1;
130
131         ret = 0;
132         for (i = 1; i < argc; i++) {
133                 ret1 = delete_one_shm(argv[i]);
134                 if (ret1 != 0 && ret == 0)
135                         ret = ret1;
136         }
137         return (ret);
138 }
139
140 static const char listmib[] = "kern.ipc.posix_shm_list";
141
142 static void
143 shm_decode_mode(mode_t m, char *str)
144 {
145         int i;
146
147         i = 0;
148         str[i++] = (m & S_IRUSR) != 0 ? 'r' : '-';
149         str[i++] = (m & S_IWUSR) != 0 ? 'w' : '-';
150         str[i++] = (m & S_IXUSR) != 0 ? 'x' : '-';
151         str[i++] = (m & S_IRGRP) != 0 ? 'r' : '-';
152         str[i++] = (m & S_IWGRP) != 0 ? 'w' : '-';
153         str[i++] = (m & S_IXGRP) != 0 ? 'x' : '-';
154         str[i++] = (m & S_IROTH) != 0 ? 'r' : '-';
155         str[i++] = (m & S_IWOTH) != 0 ? 'w' : '-';
156         str[i++] = (m & S_IXOTH) != 0 ? 'x' : '-';
157         str[i] = '\0';
158 }
159
160 static int
161 list_shm(int argc, char **argv)
162 {
163         char *buf, *bp, sizebuf[8], str[10];
164         const struct kinfo_file *kif;
165         struct stat st;
166         int c, error, fd, mib[3], ret;
167         size_t len, miblen;
168         bool hsize, uname;
169
170         hsize = false;
171         uname = true;
172
173         while ((c = getopt(argc, argv, "hn")) != -1) {
174                 switch (c) {
175                 case 'h':
176                         hsize = true;
177                         break;
178                 case 'n':
179                         uname = false;
180                         break;
181                 default:
182                         usage();
183                         return (2);
184                 }
185         }
186         if (argc != optind) {
187                 usage();
188                 return (2);
189         }
190
191         miblen = nitems(mib);
192         error = sysctlnametomib(listmib, mib, &miblen);
193         if (error == -1) {
194                 warn("cannot translate %s", listmib);
195                 return (1);
196         }
197         len = 0;
198         error = sysctl(mib, miblen, NULL, &len, NULL, 0);
199         if (error == -1) {
200                 warn("cannot get %s length", listmib);
201                 return (1);
202         }
203         len = len * 4 / 3;
204         buf = malloc(len);
205         if (buf == NULL) {
206                 warn("malloc");
207                 return (1);
208         }
209         error = sysctl(mib, miblen, buf, &len, NULL, 0);
210         if (error != 0) {
211                 warn("reading %s", listmib);
212                 ret = 1;
213                 goto out;
214         }
215         ret = 0;
216         printf("MODE    \tOWNER\tGROUP\tSIZE\tPATH\n");
217         for (bp = buf; bp < buf + len; bp += kif->kf_structsize) {
218                 kif = (const struct kinfo_file *)(void *)bp;
219                 if (kif->kf_structsize == 0)
220                         break;
221                 fd = shm_open(kif->kf_path, O_RDONLY, 0);
222                 if (fd == -1) {
223                         warn("open %s", kif->kf_path);
224                         ret = 1;
225                         continue;
226                 }
227                 error = fstat(fd, &st);
228                 close(fd);
229                 if (error != 0) {
230                         warn("stat %s", kif->kf_path);
231                         ret = 1;
232                         continue;
233                 }
234                 shm_decode_mode(kif->kf_un.kf_file.kf_file_mode, str);
235                 printf("%s\t", str);
236                 if (uname) {
237                         printf("%s\t%s\t", user_from_uid(st.st_uid, 0),
238                             group_from_gid(st.st_gid, 0));
239                 } else {
240                         printf("%d\t%d\t", st.st_uid, st.st_gid);
241                 }
242                 if (hsize) {
243                         humanize_number(sizebuf, sizeof(sizebuf),
244                             kif->kf_un.kf_file.kf_file_size, "", HN_AUTOSCALE,
245                             HN_NOSPACE);
246                         printf("%s\t", sizebuf);
247                 } else {
248                         printf("%jd\t",
249                             (uintmax_t)kif->kf_un.kf_file.kf_file_size);
250                 }
251                 printf("%s\n", kif->kf_path);
252         }
253 out:
254         free(buf);
255         return (ret);
256 }
257
258 static int
259 read_one_shm(const char *path)
260 {
261         char buf[4096];
262         ssize_t size, se;
263         int fd, ret;
264
265         ret = 1;
266         fd = shm_open(path, O_RDONLY, 0);
267         if (fd == -1) {
268                 warn("open %s", path);
269                 goto out;
270         }
271         for (;;) {
272                 size = read(fd, buf, sizeof(buf));
273                 if (size > 0) {
274                         se = fwrite(buf, 1, size, stdout);
275                         if (se < size) {
276                                 warnx("short write to stdout");
277                                 goto out;
278                         }
279                 }
280                 if (size == (ssize_t)sizeof(buf))
281                         continue;
282                 if (size >= 0 && size < (ssize_t)sizeof(buf)) {
283                         ret = 0;
284                         goto out;
285                 }
286                 warn("read from %s", path);
287                 goto out;
288         }
289 out:
290         close(fd);
291         return (ret);
292 }
293
294 static int
295 read_shm(int argc, char **argv)
296 {
297         int i, ret, ret1;
298
299         ret = 0;
300         for (i = 1; i < argc; i++) {
301                 ret1 = read_one_shm(argv[i]);
302                 if (ret1 != 0 && ret == 0)
303                         ret = ret1;
304         }
305         return (ret);
306 }
307
308 static int
309 stat_one_shm(const char *path, bool hsize, bool uname)
310 {
311         char sizebuf[8];
312         struct stat st;
313         int error, fd, ret;
314
315         fd = shm_open(path, O_RDONLY, 0);
316         if (fd == -1) {
317                 warn("open %s", path);
318                 return (1);
319         }
320         ret = 0;
321         error = fstat(fd, &st);
322         if (error == -1) {
323                 warn("stat %s", path);
324                 ret = 1;
325         } else {
326                 printf("path\t%s\n", path);
327                 printf("inode\t%jd\n", (uintmax_t)st.st_ino);
328                 printf("mode\t%#o\n", st.st_mode);
329                 printf("nlink\t%jd\n", (uintmax_t)st.st_nlink);
330                 if (uname) {
331                         printf("owner\t%s\n", user_from_uid(st.st_uid, 0));
332                         printf("group\t%s\n", group_from_gid(st.st_gid, 0));
333                 } else {
334                         printf("uid\t%d\n", st.st_uid);
335                         printf("gid\t%d\n", st.st_gid);
336                 }
337                 if (hsize) {
338                         humanize_number(sizebuf, sizeof(sizebuf),
339                             st.st_size, "", HN_AUTOSCALE, HN_NOSPACE);
340                         printf("size\t%s\n", sizebuf);
341                 } else {
342                         printf("size\t%jd\n", (uintmax_t)st.st_size);
343                 }
344                 printf("atime\t%ld.%09ld\n", (long)st.st_atime,
345                     (long)st.st_atim.tv_nsec);
346                 printf("mtime\t%ld.%09ld\n", (long)st.st_mtime,
347                     (long)st.st_mtim.tv_nsec);
348                 printf("ctime\t%ld.%09ld\n", (long)st.st_ctime,
349                     (long)st.st_ctim.tv_nsec);
350                 printf("birth\t%ld.%09ld\n", (long)st.st_birthtim.tv_sec,
351                     (long)st.st_birthtim.tv_nsec);
352         }
353         close(fd);
354         return (ret);
355 }
356
357 static int
358 stat_shm(int argc, char **argv)
359 {
360         int c, i, ret, ret1;
361         bool hsize, uname;
362
363         hsize = false;
364         uname = true;
365
366         while ((c = getopt(argc, argv, "hn")) != -1) {
367                 switch (c) {
368                 case 'h':
369                         hsize = true;
370                         break;
371                 case 'n':
372                         uname = false;
373                         break;
374                 default:
375                         usage();
376                         return (2);
377                 }
378         }
379         argc -= optind;
380         argv += optind;
381
382         ret = 0;
383         for (i = 0; i < argc; i++) {
384                 ret1 = stat_one_shm(argv[i], hsize, uname);
385                 if (ret1 != 0 && ret == 0)
386                         ret = ret1;
387         }
388         return (ret);
389 }
390
391 static int
392 truncate_one_shm(const char *path, uint64_t newsize)
393 {
394         int error, fd, ret;
395
396         ret = 0;
397         fd = shm_open(path, O_RDWR, 0);
398         if (fd == -1) {
399                 warn("open %s", path);
400                 return (1);
401         }
402         error = ftruncate(fd, newsize);
403         if (error == -1) {
404                 warn("truncate %s", path);
405                 ret = 1;
406         }
407         close(fd);
408         return (ret);
409 }
410
411 static int
412 truncate_shm(int argc, char **argv)
413 {
414         uint64_t newsize;
415         int c, i, ret, ret1;
416
417         newsize = 0;
418         while ((c = getopt(argc, argv, "s:")) != -1) {
419                 switch (c) {
420                 case 's':
421                         if (expand_number(optarg, &newsize) == -1)
422                                 err(1, "size:");
423                         break;
424                 case '?':
425                 default:
426                         return (2);
427                 }
428         }
429
430         argc -= optind;
431         argv += optind;
432         ret = 0;
433         for (i = 0; i < argc; i++) {
434                 ret1 = truncate_one_shm(argv[i], newsize);
435                 if (ret1 != 0 && ret == 0)
436                         ret = ret1;
437         }
438         return (ret);
439 }
440
441 struct opmode {
442         const char *cmd;
443         int (*impl)(int argc, char **argv);
444 };
445
446 static const struct opmode opmodes[] = {
447         { .cmd = "create",      .impl = create_shm},
448         { .cmd = "rm",          .impl = delete_shm, },
449         { .cmd = "list",        .impl = list_shm },
450         { .cmd = "ls",          .impl = list_shm },
451         { .cmd = "dump",        .impl = read_shm, },
452         { .cmd = "stat",        .impl = stat_shm, },
453         { .cmd = "truncate",    .impl = truncate_shm, },
454 };
455
456 int
457 main(int argc, char *argv[])
458 {
459         const struct opmode *opmode;
460         int i, ret;
461
462         ret = 0;
463         opmode = NULL;
464
465         if (argc < 2) {
466                 usage();
467                 exit(2);
468         }
469         for (i = 0; i < (int)nitems(opmodes); i++) {
470                 if (strcmp(argv[1], opmodes[i].cmd) == 0) {
471                         opmode = &opmodes[i];
472                         break;
473                 }
474         }
475         if (opmode == NULL) {
476                 usage();
477                 exit(2);
478         }
479         ret = opmode->impl(argc - 1, argv + 1);
480         exit(ret);
481 }