]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/posixshmcontrol/posixshmcontrol.c
diff: use getline() instead of fgetln()
[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/param.h>
30 #include <sys/filio.h>
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #include <sys/syscall.h>
34 #include <sys/sysctl.h>
35 #include <sys/user.h>
36 #include <err.h>
37 #include <fcntl.h>
38 #include <grp.h>
39 #include <jail.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>] [-l <largepage>] <path> ...\n"
54             "posixshmcontrol rm <path> ...\n"
55             "posixshmcontrol ls [-h] [-n] [-j jail]\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, int idx)
63 {
64         int fd;
65
66         if (idx == -1) {
67                 fd = shm_open(path, O_RDWR | O_CREAT, mode);
68                 if (fd == -1) {
69                         warn("create %s", path);
70                         return (1);
71                 }
72         } else {
73                 fd = shm_create_largepage(path, O_RDWR, idx,
74                     SHM_LARGEPAGE_ALLOC_DEFAULT, mode);
75                 if (fd == -1) {
76                         warn("shm_create_largepage %s psind %d", path, idx);
77                         return (1);
78                 }
79         }
80         close(fd);
81         return (0);
82 }
83
84 static int
85 create_shm(int argc, char **argv)
86 {
87         char *end;
88         size_t *pagesizes;
89         long mode;
90         uint64_t pgsz;
91         int c, i, idx, pn, ret, ret1;
92         bool printed;
93
94         mode = 0600;
95         idx = -1;
96         while ((c = getopt(argc, argv, "l:m:")) != -1) {
97                 switch (c) {
98                 case 'm':
99                         errno = 0;
100                         mode = strtol(optarg, &end, 0);
101                         if (mode == 0 && errno != 0)
102                                 err(1, "mode");
103                         if (*end != '\0')
104                                 errx(1, "non-integer mode");
105                         break;
106                 case 'l':
107                         if (expand_number(optarg, &pgsz) == -1)
108                                 err(1, "size");
109                         pn = getpagesizes(NULL, 0);
110                         if (pn == -1)
111                                 err(1, "getpagesizes");
112                         pagesizes = malloc(sizeof(size_t) * pn);
113                         if (pagesizes == NULL)
114                                 err(1, "malloc");
115                         if (getpagesizes(pagesizes, pn) == -1)
116                                 err(1, "gtpagesizes");
117                         for (idx = 0; idx < pn; idx++) {
118                                 if (pagesizes[idx] == pgsz)
119                                         break;
120                         }
121                         if (idx == pn) {
122                                 fprintf(stderr,
123     "pagesize should be superpagesize, supported sizes:");
124                                 printed = false;
125                                 for (i = 0; i < pn; i++) {
126                                         if (pagesizes[i] == 0 ||
127                                             pagesizes[i] == (size_t)
128                                             getpagesize())
129                                                 continue;
130                                         printed = true;
131                                         fprintf(stderr, " %zu", pagesizes[i]);
132                                 }
133                                 if (!printed)
134                                         fprintf(stderr, " none");
135                                 fprintf(stderr, "\n");
136                                 exit(1);
137                         }
138                         if (pgsz == (uint64_t)getpagesize())
139                                 errx(1, "pagesize should be large");
140                         free(pagesizes);
141                         break;
142                 case '?':
143                 default:
144                         usage();
145                         return (2);
146                 }
147         }
148         argc -= optind;
149         argv += optind;
150
151         if (argc == 0) {
152                 usage();
153                 return (2);
154         }
155
156         ret = 0;
157         for (i = 0; i < argc; i++) {
158                 ret1 = create_one_shm(argv[i], mode, idx);
159                 if (ret1 != 0 && ret == 0)
160                         ret = ret1;
161         }
162         return (ret);
163 }
164
165 static int
166 delete_one_shm(const char *path)
167 {
168         int error, ret;
169
170         error = shm_unlink(path);
171         if (error != 0) {
172                 warn("unlink of %s failed", path);
173                 ret = 1;
174         } else {
175                 ret = 0;
176         }
177         return (ret);
178 }
179
180 static int
181 delete_shm(int argc, char **argv)
182 {
183         int i, ret, ret1;
184
185         if (argc == 1) {
186                 usage();
187                 return (2);
188         }
189
190         ret = 0;
191         for (i = 1; i < argc; i++) {
192                 ret1 = delete_one_shm(argv[i]);
193                 if (ret1 != 0 && ret == 0)
194                         ret = ret1;
195         }
196         return (ret);
197 }
198
199 static const char listmib[] = "kern.ipc.posix_shm_list";
200
201 static void
202 shm_decode_mode(mode_t m, char *str)
203 {
204         int i;
205
206         i = 0;
207         str[i++] = (m & S_IRUSR) != 0 ? 'r' : '-';
208         str[i++] = (m & S_IWUSR) != 0 ? 'w' : '-';
209         str[i++] = (m & S_IXUSR) != 0 ? 'x' : '-';
210         str[i++] = (m & S_IRGRP) != 0 ? 'r' : '-';
211         str[i++] = (m & S_IWGRP) != 0 ? 'w' : '-';
212         str[i++] = (m & S_IXGRP) != 0 ? 'x' : '-';
213         str[i++] = (m & S_IROTH) != 0 ? 'r' : '-';
214         str[i++] = (m & S_IWOTH) != 0 ? 'w' : '-';
215         str[i++] = (m & S_IXOTH) != 0 ? 'x' : '-';
216         str[i] = '\0';
217 }
218
219 static int
220 list_shm(int argc, char **argv)
221 {
222         char *buf, *bp, *ep, jailpath[MAXPATHLEN], sizebuf[8], str[10];
223         const char *jailparam;
224         const struct kinfo_file *kif;
225         struct stat st;
226         int c, error, fd, jid, mib[3], ret;
227         size_t len, jailpathlen, miblen;
228         bool hsize, jailed, uname;
229
230         hsize = false;
231         jailed = false;
232         uname = true;
233
234         while ((c = getopt(argc, argv, "hj:n")) != -1) {
235                 switch (c) {
236                 case 'h':
237                         hsize = true;
238                         break;
239                 case 'n':
240                         uname = false;
241                         break;
242                 case 'j':
243                         jid = strtoul(optarg, &ep, 10);
244                         if (ep > optarg && !*ep) {
245                                 jailparam = "jid";
246                                 jailed = jid > 0;
247                         } else {
248                                 jailparam = "name";
249                                 jailed = true;
250                         }
251                         if (jailed) {
252                                 if (jail_getv(0, jailparam, optarg, "path",
253                                     jailpath, NULL) < 0) {
254                                         if (errno == ENOENT)
255                                                 warnx("no such jail: %s", optarg);
256                                         else
257                                                 warnx("%s", jail_errmsg);
258                                         return (1);
259                                 }
260                                 jailpathlen = strlen(jailpath);
261                                 jailpath[jailpathlen] = '/';
262                         }
263                         break;
264                 default:
265                         usage();
266                         return (2);
267                 }
268         }
269         if (argc != optind) {
270                 usage();
271                 return (2);
272         }
273
274         miblen = nitems(mib);
275         error = sysctlnametomib(listmib, mib, &miblen);
276         if (error == -1) {
277                 warn("cannot translate %s", listmib);
278                 return (1);
279         }
280         len = 0;
281         error = sysctl(mib, miblen, NULL, &len, NULL, 0);
282         if (error == -1) {
283                 warn("cannot get %s length", listmib);
284                 return (1);
285         }
286         len = len * 4 / 3;
287         buf = malloc(len);
288         if (buf == NULL) {
289                 warn("malloc");
290                 return (1);
291         }
292         error = sysctl(mib, miblen, buf, &len, NULL, 0);
293         if (error != 0) {
294                 warn("reading %s", listmib);
295                 ret = 1;
296                 goto out;
297         }
298         ret = 0;
299         printf("MODE    \tOWNER\tGROUP\tSIZE\tPATH\n");
300         for (bp = buf; bp < buf + len; bp += kif->kf_structsize) {
301                 kif = (const struct kinfo_file *)(void *)bp;
302                 if (kif->kf_structsize == 0)
303                         break;
304                 if (jailed && strncmp(kif->kf_path, jailpath, jailpathlen + 1))
305                         continue;
306                 fd = shm_open(kif->kf_path, O_RDONLY, 0);
307                 if (fd == -1) {
308                         if (errno != EACCES) {
309                                 warn("open %s", kif->kf_path);
310                                 ret = 1;
311                         }
312                         continue;
313                 }
314                 error = fstat(fd, &st);
315                 close(fd);
316                 if (error != 0) {
317                         warn("stat %s", kif->kf_path);
318                         ret = 1;
319                         continue;
320                 }
321                 shm_decode_mode(kif->kf_un.kf_file.kf_file_mode, str);
322                 printf("%s\t", str);
323                 if (uname) {
324                         printf("%s\t%s\t", user_from_uid(st.st_uid, 0),
325                             group_from_gid(st.st_gid, 0));
326                 } else {
327                         printf("%d\t%d\t", st.st_uid, st.st_gid);
328                 }
329                 if (hsize) {
330                         humanize_number(sizebuf, sizeof(sizebuf),
331                             kif->kf_un.kf_file.kf_file_size, "", HN_AUTOSCALE,
332                             HN_NOSPACE);
333                         printf("%s\t", sizebuf);
334                 } else {
335                         printf("%jd\t",
336                             (uintmax_t)kif->kf_un.kf_file.kf_file_size);
337                 }
338                 printf("%s\n", kif->kf_path);
339         }
340 out:
341         free(buf);
342         return (ret);
343 }
344
345 static int
346 read_one_shm(const char *path)
347 {
348         char buf[4096];
349         ssize_t size, se;
350         int fd, ret;
351
352         ret = 1;
353         fd = shm_open(path, O_RDONLY, 0);
354         if (fd == -1) {
355                 warn("open %s", path);
356                 goto out;
357         }
358         for (;;) {
359                 size = read(fd, buf, sizeof(buf));
360                 if (size > 0) {
361                         se = fwrite(buf, 1, size, stdout);
362                         if (se < size) {
363                                 warnx("short write to stdout");
364                                 goto out;
365                         }
366                 }
367                 if (size == (ssize_t)sizeof(buf))
368                         continue;
369                 if (size >= 0 && size < (ssize_t)sizeof(buf)) {
370                         ret = 0;
371                         goto out;
372                 }
373                 warn("read from %s", path);
374                 goto out;
375         }
376 out:
377         close(fd);
378         return (ret);
379 }
380
381 static int
382 read_shm(int argc, char **argv)
383 {
384         int i, ret, ret1;
385
386         if (argc == 1) {
387                 usage();
388                 return (2);
389         }
390
391         ret = 0;
392         for (i = 1; i < argc; i++) {
393                 ret1 = read_one_shm(argv[i]);
394                 if (ret1 != 0 && ret == 0)
395                         ret = ret1;
396         }
397         return (ret);
398 }
399
400 static int
401 stat_one_shm(const char *path, bool hsize, bool uname)
402 {
403         char sizebuf[8];
404         struct stat st;
405         int error, fd, ret;
406         struct shm_largepage_conf conf_dummy;
407         bool largepage;
408
409         fd = shm_open(path, O_RDONLY, 0);
410         if (fd == -1) {
411                 warn("open %s", path);
412                 return (1);
413         }
414         ret = 0;
415         error = fstat(fd, &st);
416         if (error == -1) {
417                 warn("stat %s", path);
418                 ret = 1;
419         } else {
420                 printf("path\t%s\n", path);
421                 printf("inode\t%jd\n", (uintmax_t)st.st_ino);
422                 printf("mode\t%#o\n", st.st_mode);
423                 printf("nlink\t%jd\n", (uintmax_t)st.st_nlink);
424                 if (uname) {
425                         printf("owner\t%s\n", user_from_uid(st.st_uid, 0));
426                         printf("group\t%s\n", group_from_gid(st.st_gid, 0));
427                 } else {
428                         printf("uid\t%d\n", st.st_uid);
429                         printf("gid\t%d\n", st.st_gid);
430                 }
431                 if (hsize) {
432                         humanize_number(sizebuf, sizeof(sizebuf),
433                             st.st_size, "", HN_AUTOSCALE, HN_NOSPACE);
434                         printf("size\t%s\n", sizebuf);
435                 } else {
436                         printf("size\t%jd\n", (uintmax_t)st.st_size);
437                 }
438                 printf("atime\t%ld.%09ld\n", (long)st.st_atime,
439                     (long)st.st_atim.tv_nsec);
440                 printf("mtime\t%ld.%09ld\n", (long)st.st_mtime,
441                     (long)st.st_mtim.tv_nsec);
442                 printf("ctime\t%ld.%09ld\n", (long)st.st_ctime,
443                     (long)st.st_ctim.tv_nsec);
444                 printf("birth\t%ld.%09ld\n", (long)st.st_birthtim.tv_sec,
445                     (long)st.st_birthtim.tv_nsec);
446                 error = ioctl(fd, FIOGSHMLPGCNF, &conf_dummy);
447                 largepage = error == 0;
448                 if (st.st_blocks != 0 && largepage)
449                         printf("pagesz\t%jd\n", roundup((uintmax_t)st.st_size,
450                             PAGE_SIZE) / st.st_blocks);
451                 else
452                         printf("pages\t%jd\n", st.st_blocks);
453         }
454         close(fd);
455         return (ret);
456 }
457
458 static int
459 stat_shm(int argc, char **argv)
460 {
461         int c, i, ret, ret1;
462         bool hsize, uname;
463
464         hsize = false;
465         uname = true;
466
467         while ((c = getopt(argc, argv, "hn")) != -1) {
468                 switch (c) {
469                 case 'h':
470                         hsize = true;
471                         break;
472                 case 'n':
473                         uname = false;
474                         break;
475                 default:
476                         usage();
477                         return (2);
478                 }
479         }
480         argc -= optind;
481         argv += optind;
482
483         if (argc == 0) {
484                 usage();
485                 return (2);
486         }
487
488         ret = 0;
489         for (i = 0; i < argc; i++) {
490                 ret1 = stat_one_shm(argv[i], hsize, uname);
491                 if (ret1 != 0 && ret == 0)
492                         ret = ret1;
493         }
494         return (ret);
495 }
496
497 static int
498 truncate_one_shm(const char *path, uint64_t newsize)
499 {
500         int error, fd, ret;
501
502         ret = 0;
503         fd = shm_open(path, O_RDWR, 0);
504         if (fd == -1) {
505                 warn("open %s", path);
506                 return (1);
507         }
508         error = ftruncate(fd, newsize);
509         if (error == -1) {
510                 warn("truncate %s", path);
511                 ret = 1;
512         }
513         close(fd);
514         return (ret);
515 }
516
517 static int
518 truncate_shm(int argc, char **argv)
519 {
520         uint64_t newsize;
521         int c, i, ret, ret1;
522
523         newsize = 0;
524         while ((c = getopt(argc, argv, "s:")) != -1) {
525                 switch (c) {
526                 case 's':
527                         if (expand_number(optarg, &newsize) == -1)
528                                 err(1, "size");
529                         break;
530                 case '?':
531                 default:
532                         return (2);
533                 }
534         }
535         argc -= optind;
536         argv += optind;
537
538         if (argc == 0) {
539                 usage();
540                 return (2);
541         }
542
543         ret = 0;
544         for (i = 0; i < argc; i++) {
545                 ret1 = truncate_one_shm(argv[i], newsize);
546                 if (ret1 != 0 && ret == 0)
547                         ret = ret1;
548         }
549         return (ret);
550 }
551
552 struct opmode {
553         const char *cmd;
554         int (*impl)(int argc, char **argv);
555 };
556
557 static const struct opmode opmodes[] = {
558         { .cmd = "create",      .impl = create_shm},
559         { .cmd = "rm",          .impl = delete_shm, },
560         { .cmd = "list",        .impl = list_shm },
561         { .cmd = "ls",          .impl = list_shm },
562         { .cmd = "dump",        .impl = read_shm, },
563         { .cmd = "stat",        .impl = stat_shm, },
564         { .cmd = "truncate",    .impl = truncate_shm, },
565 };
566
567 int
568 main(int argc, char *argv[])
569 {
570         const struct opmode *opmode;
571         int i, ret;
572
573         ret = 0;
574         opmode = NULL;
575
576         if (argc < 2) {
577                 usage();
578                 exit(2);
579         }
580         for (i = 0; i < (int)nitems(opmodes); i++) {
581                 if (strcmp(argv[1], opmodes[i].cmd) == 0) {
582                         opmode = &opmodes[i];
583                         break;
584                 }
585         }
586         if (opmode == NULL) {
587                 usage();
588                 exit(2);
589         }
590         ret = opmode->impl(argc - 1, argv + 1);
591         exit(ret);
592 }