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