1 /* 2 * Copyright (c) 1994 SigmaSoft, Th. Lockert <tholo@sigmasoft.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <assert.h> 32 #include <err.h> 33 #include <fcntl.h> 34 #include <grp.h> 35 #include <kvm.h> 36 #include <nlist.h> 37 #include <limits.h> 38 #include <paths.h> 39 #include <pwd.h> 40 #include <stddef.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include <sys/types.h> 47 #include <sys/param.h> 48 #include <sys/time.h> 49 #include <sys/proc.h> 50 #include <sys/sysctl.h> 51 #define _KERNEL 52 #include <sys/ipc.h> 53 #include <sys/sem.h> 54 #include <sys/shm.h> 55 #include <sys/msg.h> 56 57 /* SysCtlGatherStruct structure. */ 58 struct scgs_vector { 59 const char *sysctl; 60 off_t offset; 61 size_t size; 62 }; 63 64 int use_sysctl = 1; 65 struct semid_ds *sema; 66 struct seminfo seminfo; 67 struct msginfo msginfo; 68 struct msqid_ds *msqids; 69 struct shminfo shminfo; 70 struct shmid_ds *shmsegs; 71 72 char *fmt_perm(u_short); 73 void cvt_time(time_t, char *); 74 void sysctlgatherstruct(void *addr, size_t size, struct scgs_vector *vec); 75 void kget(int idx, void *addr, size_t size); 76 void usage(void); 77 78 static struct nlist symbols[] = { 79 {"sema"}, 80 #define X_SEMA 0 81 {"seminfo"}, 82 #define X_SEMINFO 1 83 {"msginfo"}, 84 #define X_MSGINFO 2 85 {"msqids"}, 86 #define X_MSQIDS 3 87 {"shminfo"}, 88 #define X_SHMINFO 4 89 {"shmsegs"}, 90 #define X_SHMSEGS 5 91 {NULL} 92 }; 93 94 #define SHMINFO_XVEC \ 95 X(shmmax, sizeof(int)) \ 96 X(shmmin, sizeof(int)) \ 97 X(shmmni, sizeof(int)) \ 98 X(shmseg, sizeof(int)) \ 99 X(shmall, sizeof(int)) 100 101 #define SEMINFO_XVEC \ 102 X(semmap, sizeof(int)) \ 103 X(semmni, sizeof(int)) \ 104 X(semmns, sizeof(int)) \ 105 X(semmnu, sizeof(int)) \ 106 X(semmsl, sizeof(int)) \ 107 X(semopm, sizeof(int)) \ 108 X(semume, sizeof(int)) \ 109 X(semusz, sizeof(int)) \ 110 X(semvmx, sizeof(int)) \ 111 X(semaem, sizeof(int)) 112 113 #define MSGINFO_XVEC \ 114 X(msgmax, sizeof(int)) \ 115 X(msgmni, sizeof(int)) \ 116 X(msgmnb, sizeof(int)) \ 117 X(msgtql, sizeof(int)) \ 118 X(msgssz, sizeof(int)) \ 119 X(msgseg, sizeof(int)) 120 121 #define X(a, b) { "kern.ipc." #a, offsetof(TYPEC, a), (b) }, 122 #define TYPEC struct shminfo 123 struct scgs_vector shminfo_scgsv[] = { SHMINFO_XVEC { NULL } }; 124 #undef TYPEC 125 #define TYPEC struct seminfo 126 struct scgs_vector seminfo_scgsv[] = { SEMINFO_XVEC { NULL } }; 127 #undef TYPEC 128 #define TYPEC struct msginfo 129 struct scgs_vector msginfo_scgsv[] = { MSGINFO_XVEC { NULL } }; 130 #undef TYPEC 131 #undef X 132 133 static kvm_t *kd; 134 135 char * 136 fmt_perm(mode) 137 u_short mode; 138 { 139 static char buffer[100]; 140 141 buffer[0] = '-'; 142 buffer[1] = '-'; 143 buffer[2] = ((mode & 0400) ? 'r' : '-'); 144 buffer[3] = ((mode & 0200) ? 'w' : '-'); 145 buffer[4] = ((mode & 0100) ? 'a' : '-'); 146 buffer[5] = ((mode & 0040) ? 'r' : '-'); 147 buffer[6] = ((mode & 0020) ? 'w' : '-'); 148 buffer[7] = ((mode & 0010) ? 'a' : '-'); 149 buffer[8] = ((mode & 0004) ? 'r' : '-'); 150 buffer[9] = ((mode & 0002) ? 'w' : '-'); 151 buffer[10] = ((mode & 0001) ? 'a' : '-'); 152 buffer[11] = '\0'; 153 return (&buffer[0]); 154 } 155 156 void 157 cvt_time(t, buf) 158 time_t t; 159 char *buf; 160 { 161 struct tm *tm; 162 163 if (t == 0) { 164 strcpy(buf, "no-entry"); 165 } else { 166 tm = localtime(&t); 167 sprintf(buf, "%2d:%02d:%02d", 168 tm->tm_hour, tm->tm_min, tm->tm_sec); 169 } 170 } 171 #define SHMINFO 1 172 #define SHMTOTAL 2 173 #define MSGINFO 4 174 #define MSGTOTAL 8 175 #define SEMINFO 16 176 #define SEMTOTAL 32 177 178 #define BIGGEST 1 179 #define CREATOR 2 180 #define OUTSTANDING 4 181 #define PID 8 182 #define TIME 16 183 184 int 185 main(argc, argv) 186 int argc; 187 char *argv[]; 188 { 189 int display = SHMINFO | MSGINFO | SEMINFO; 190 int option = 0; 191 char *core = NULL, *namelist = NULL; 192 char kvmoferr[_POSIX2_LINE_MAX]; /* Error buf for kvm_openfiles. */ 193 int i; 194 195 while ((i = getopt(argc, argv, "MmQqSsabC:cN:optTy")) != -1) 196 switch (i) { 197 case 'M': 198 display = SHMTOTAL; 199 break; 200 case 'm': 201 display = SHMINFO; 202 break; 203 case 'Q': 204 display = MSGTOTAL; 205 break; 206 case 'q': 207 display = MSGINFO; 208 break; 209 case 'S': 210 display = SEMTOTAL; 211 break; 212 case 's': 213 display = SEMINFO; 214 break; 215 case 'T': 216 display = SHMTOTAL | MSGTOTAL | SEMTOTAL; 217 break; 218 case 'a': 219 option |= BIGGEST | CREATOR | OUTSTANDING | PID | TIME; 220 break; 221 case 'b': 222 option |= BIGGEST; 223 break; 224 case 'C': 225 core = optarg; 226 break; 227 case 'c': 228 option |= CREATOR; 229 break; 230 case 'N': 231 namelist = optarg; 232 break; 233 case 'o': 234 option |= OUTSTANDING; 235 break; 236 case 'p': 237 option |= PID; 238 break; 239 case 't': 240 option |= TIME; 241 break; 242 case 'y': 243 use_sysctl = 0; 244 break; 245 default: 246 usage(); 247 } 248 249 /* 250 * If paths to the exec file or core file were specified, we 251 * aren't operating on the running kernel, so we can't use 252 * sysctl. 253 */ 254 if (namelist != NULL || core != NULL) 255 use_sysctl = 0; 256 257 if (!use_sysctl) { 258 kd = kvm_openfiles(namelist, core, NULL, O_RDONLY, kvmoferr); 259 if (kd == NULL) 260 errx(1, "kvm_openfiles: %s", kvmoferr); 261 switch (kvm_nlist(kd, symbols)) { 262 case 0: 263 break; 264 case -1: 265 errx(1, "unable to read kernel symbol table"); 266 default: 267 #ifdef notdef /* they'll be told more civilly later */ 268 warnx("nlist failed"); 269 for (i = 0; symbols[i].n_name != NULL; i++) 270 if (symbols[i].n_value == 0) 271 warnx("symbol %s not found", 272 symbols[i].n_name); 273 #endif 274 break; 275 } 276 } 277 278 kget(X_MSGINFO, &msginfo, sizeof(msginfo)); 279 if ((display & (MSGINFO | MSGTOTAL))) { 280 if (display & MSGTOTAL) { 281 printf("msginfo:\n"); 282 printf("\tmsgmax: %6d\t(max characters in a message)\n", 283 msginfo.msgmax); 284 printf("\tmsgmni: %6d\t(# of message queues)\n", 285 msginfo.msgmni); 286 printf("\tmsgmnb: %6d\t(max characters in a message queue)\n", 287 msginfo.msgmnb); 288 printf("\tmsgtql: %6d\t(max # of messages in system)\n", 289 msginfo.msgtql); 290 printf("\tmsgssz: %6d\t(size of a message segment)\n", 291 msginfo.msgssz); 292 printf("\tmsgseg: %6d\t(# of message segments in system)\n\n", 293 msginfo.msgseg); 294 } 295 if (display & MSGINFO) { 296 struct msqid_ds *xmsqids; 297 size_t xmsqids_len; 298 299 300 xmsqids_len = sizeof(struct msqid_ds) * msginfo.msgmni; 301 xmsqids = malloc(xmsqids_len); 302 kget(X_MSQIDS, xmsqids, xmsqids_len); 303 304 printf("Message Queues:\n"); 305 printf("T ID KEY MODE OWNER GROUP"); 306 if (option & CREATOR) 307 printf(" CREATOR CGROUP"); 308 if (option & OUTSTANDING) 309 printf(" CBYTES QNUM"); 310 if (option & BIGGEST) 311 printf(" QBYTES"); 312 if (option & PID) 313 printf(" LSPID LRPID"); 314 if (option & TIME) 315 printf(" STIME RTIME CTIME"); 316 printf("\n"); 317 for (i = 0; i < msginfo.msgmni; i += 1) { 318 if (xmsqids[i].msg_qbytes != 0) { 319 char stime_buf[100], rtime_buf[100], 320 ctime_buf[100]; 321 struct msqid_ds *msqptr = &xmsqids[i]; 322 323 cvt_time(msqptr->msg_stime, stime_buf); 324 cvt_time(msqptr->msg_rtime, rtime_buf); 325 cvt_time(msqptr->msg_ctime, ctime_buf); 326 327 printf("q %6d %10d %s %8s %8s", 328 IXSEQ_TO_IPCID(i, msqptr->msg_perm), 329 (int)msqptr->msg_perm.key, 330 fmt_perm(msqptr->msg_perm.mode), 331 user_from_uid(msqptr->msg_perm.uid, 0), 332 group_from_gid(msqptr->msg_perm.gid, 0)); 333 334 if (option & CREATOR) 335 printf(" %8s %8s", 336 user_from_uid(msqptr->msg_perm.cuid, 0), 337 group_from_gid(msqptr->msg_perm.cgid, 0)); 338 339 if (option & OUTSTANDING) 340 printf(" %6lu %6lu", 341 msqptr->msg_cbytes, 342 msqptr->msg_qnum); 343 344 if (option & BIGGEST) 345 printf(" %6lu", 346 msqptr->msg_qbytes); 347 348 if (option & PID) 349 printf(" %6d %6d", 350 msqptr->msg_lspid, 351 msqptr->msg_lrpid); 352 353 if (option & TIME) 354 printf("%s %s %s", 355 stime_buf, 356 rtime_buf, 357 ctime_buf); 358 359 printf("\n"); 360 } 361 } 362 printf("\n"); 363 } 364 } else 365 if (display & (MSGINFO | MSGTOTAL)) { 366 fprintf(stderr, 367 "SVID messages facility not configured in the system\n"); 368 } 369 370 kget(X_SHMINFO, &shminfo, sizeof(shminfo)); 371 if ((display & (SHMINFO | SHMTOTAL))) { 372 if (display & SHMTOTAL) { 373 printf("shminfo:\n"); 374 printf("\tshmmax: %7d\t(max shared memory segment size)\n", 375 shminfo.shmmax); 376 printf("\tshmmin: %7d\t(min shared memory segment size)\n", 377 shminfo.shmmin); 378 printf("\tshmmni: %7d\t(max number of shared memory identifiers)\n", 379 shminfo.shmmni); 380 printf("\tshmseg: %7d\t(max shared memory segments per process)\n", 381 shminfo.shmseg); 382 printf("\tshmall: %7d\t(max amount of shared memory in pages)\n\n", 383 shminfo.shmall); 384 } 385 if (display & SHMINFO) { 386 struct shmid_ds *xshmids; 387 size_t xshmids_len; 388 389 xshmids_len = sizeof(struct shmid_ds) * shminfo.shmmni; 390 xshmids = malloc(xshmids_len); 391 kget(X_SHMSEGS, xshmids, xshmids_len); 392 393 printf("Shared Memory:\n"); 394 printf("T ID KEY MODE OWNER GROUP"); 395 if (option & CREATOR) 396 printf(" CREATOR CGROUP"); 397 if (option & OUTSTANDING) 398 printf(" NATTCH"); 399 if (option & BIGGEST) 400 printf(" SEGSZ"); 401 if (option & PID) 402 printf(" CPID LPID"); 403 if (option & TIME) 404 printf(" ATIME DTIME CTIME"); 405 printf("\n"); 406 for (i = 0; i < shminfo.shmmni; i += 1) { 407 if (xshmids[i].shm_perm.mode & 0x0800) { 408 char atime_buf[100], dtime_buf[100], 409 ctime_buf[100]; 410 struct shmid_ds *shmptr = &xshmids[i]; 411 412 cvt_time(shmptr->shm_atime, atime_buf); 413 cvt_time(shmptr->shm_dtime, dtime_buf); 414 cvt_time(shmptr->shm_ctime, ctime_buf); 415 416 printf("m %6d %10d %s %8s %8s", 417 IXSEQ_TO_IPCID(i, shmptr->shm_perm), 418 (int)shmptr->shm_perm.key, 419 fmt_perm(shmptr->shm_perm.mode), 420 user_from_uid(shmptr->shm_perm.uid, 0), 421 group_from_gid(shmptr->shm_perm.gid, 0)); 422 423 if (option & CREATOR) 424 printf(" %8s %8s", 425 user_from_uid(shmptr->shm_perm.cuid, 0), 426 group_from_gid(shmptr->shm_perm.cgid, 0)); 427 428 if (option & OUTSTANDING) 429 printf(" %6d", 430 shmptr->shm_nattch); 431 432 if (option & BIGGEST) 433 printf(" %6d", 434 shmptr->shm_segsz); 435 436 if (option & PID) 437 printf(" %6d %6d", 438 shmptr->shm_cpid, 439 shmptr->shm_lpid); 440 441 if (option & TIME) 442 printf("%s %s %s", 443 atime_buf, 444 dtime_buf, 445 ctime_buf); 446 447 printf("\n"); 448 } 449 } 450 printf("\n"); 451 } 452 } else 453 if (display & (SHMINFO | SHMTOTAL)) { 454 fprintf(stderr, 455 "SVID shared memory facility not configured in the system\n"); 456 } 457 458 kget(X_SEMINFO, &seminfo, sizeof(seminfo)); 459 if ((display & (SEMINFO | SEMTOTAL))) { 460 struct semid_ds *xsema; 461 size_t xsema_len; 462 463 if (display & SEMTOTAL) { 464 printf("seminfo:\n"); 465 printf("\tsemmap: %6d\t(# of entries in semaphore map)\n", 466 seminfo.semmap); 467 printf("\tsemmni: %6d\t(# of semaphore identifiers)\n", 468 seminfo.semmni); 469 printf("\tsemmns: %6d\t(# of semaphores in system)\n", 470 seminfo.semmns); 471 printf("\tsemmnu: %6d\t(# of undo structures in system)\n", 472 seminfo.semmnu); 473 printf("\tsemmsl: %6d\t(max # of semaphores per id)\n", 474 seminfo.semmsl); 475 printf("\tsemopm: %6d\t(max # of operations per semop call)\n", 476 seminfo.semopm); 477 printf("\tsemume: %6d\t(max # of undo entries per process)\n", 478 seminfo.semume); 479 printf("\tsemusz: %6d\t(size in bytes of undo structure)\n", 480 seminfo.semusz); 481 printf("\tsemvmx: %6d\t(semaphore maximum value)\n", 482 seminfo.semvmx); 483 printf("\tsemaem: %6d\t(adjust on exit max value)\n\n", 484 seminfo.semaem); 485 } 486 if (display & SEMINFO) { 487 xsema_len = sizeof(struct semid_ds) * seminfo.semmni; 488 xsema = malloc(xsema_len); 489 kget(X_SEMA, xsema, xsema_len); 490 491 printf("Semaphores:\n"); 492 printf("T ID KEY MODE OWNER GROUP"); 493 if (option & CREATOR) 494 printf(" CREATOR CGROUP"); 495 if (option & BIGGEST) 496 printf(" NSEMS"); 497 if (option & TIME) 498 printf(" OTIME CTIME"); 499 printf("\n"); 500 for (i = 0; i < seminfo.semmni; i += 1) { 501 if ((xsema[i].sem_perm.mode & SEM_ALLOC) != 0) { 502 char ctime_buf[100], otime_buf[100]; 503 struct semid_ds *semaptr = &xsema[i]; 504 505 cvt_time(semaptr->sem_otime, otime_buf); 506 cvt_time(semaptr->sem_ctime, ctime_buf); 507 508 printf("s %6d %10d %s %8s %8s", 509 IXSEQ_TO_IPCID(i, semaptr->sem_perm), 510 (int)semaptr->sem_perm.key, 511 fmt_perm(semaptr->sem_perm.mode), 512 user_from_uid(semaptr->sem_perm.uid, 0), 513 group_from_gid(semaptr->sem_perm.gid, 0)); 514 515 if (option & CREATOR) 516 printf(" %8s %8s", 517 user_from_uid(semaptr->sem_perm.cuid, 0), 518 group_from_gid(semaptr->sem_perm.cgid, 0)); 519 520 if (option & BIGGEST) 521 printf(" %6d", 522 semaptr->sem_nsems); 523 524 if (option & TIME) 525 printf("%s %s", 526 otime_buf, 527 ctime_buf); 528 529 printf("\n"); 530 } 531 } 532 533 printf("\n"); 534 } 535 } else 536 if (display & (SEMINFO | SEMTOTAL)) { 537 fprintf(stderr, "SVID semaphores facility not configured in the system\n"); 538 } 539 if (!use_sysctl) 540 kvm_close(kd); 541 542 exit(0); 543 } 544 545 void 546 sysctlgatherstruct(addr, size, vecarr) 547 void *addr; 548 size_t size; 549 struct scgs_vector *vecarr; 550 { 551 struct scgs_vector *xp; 552 size_t tsiz; 553 int rv; 554 555 for (xp = vecarr; xp->sysctl != NULL; xp++) { 556 assert(xp->offset <= size); 557 tsiz = xp->size; 558 rv = sysctlbyname(xp->sysctl, (char *)addr + xp->offset, 559 &tsiz, NULL, 0); 560 if (rv == -1) 561 errx(1, "sysctlbyname: %s", xp->sysctl); 562 if (tsiz != xp->size) 563 errx(1, "%s size mismatch (expected %d, got %d)", 564 xp->sysctl, xp->size, tsiz); 565 } 566 } 567 568 void 569 kget(idx, addr, size) 570 int idx; 571 void *addr; 572 size_t size; 573 { 574 char *symn; /* symbol name */ 575 size_t tsiz; 576 int rv; 577 unsigned long kaddr; 578 const char *sym2sysctl[] = { /* symbol to sysctl name table */ 579 "kern.ipc.sema", 580 "kern.ipc.seminfo", 581 "kern.ipc.msginfo", 582 "kern.ipc.msqids", 583 "kern.ipc.shminfo", 584 "kern.ipc.shmsegs" }; 585 586 assert((unsigned)idx <= sizeof(sym2sysctl) / sizeof(*sym2sysctl)); 587 if (!use_sysctl) { 588 symn = symbols[idx].n_name; 589 if (*symn == '_') 590 symn++; 591 if (symbols[idx].n_type == 0 || symbols[idx].n_value == 0) 592 errx(1, "symbol %s undefined", symn); 593 /* 594 * For some symbols, the value we retreieve is 595 * actually a pointer; since we want the actual value, 596 * we have to manually dereference it. 597 */ 598 switch (idx) { 599 case X_MSQIDS: 600 tsiz = sizeof(msqids); 601 rv = kvm_read(kd, symbols[idx].n_value, 602 &msqids, tsiz); 603 kaddr = (u_long)msqids; 604 break; 605 case X_SHMSEGS: 606 tsiz = sizeof(shmsegs); 607 rv = kvm_read(kd, symbols[idx].n_value, 608 &shmsegs, tsiz); 609 kaddr = (u_long)shmsegs; 610 break; 611 case X_SEMA: 612 tsiz = sizeof(sema); 613 rv = kvm_read(kd, symbols[idx].n_value, 614 &sema, tsiz); 615 kaddr = (u_long)sema; 616 break; 617 default: 618 rv = tsiz = 0; 619 kaddr = symbols[idx].n_value; 620 break; 621 } 622 if ((unsigned)rv != tsiz) 623 errx(1, "%s: %s", symn, kvm_geterr(kd)); 624 if ((unsigned)kvm_read(kd, kaddr, addr, size) != size) 625 errx(1, "%s: %s", symn, kvm_geterr(kd)); 626 } else { 627 switch (idx) { 628 case X_SHMINFO: 629 sysctlgatherstruct(addr, size, shminfo_scgsv); 630 break; 631 case X_SEMINFO: 632 sysctlgatherstruct(addr, size, seminfo_scgsv); 633 break; 634 case X_MSGINFO: 635 sysctlgatherstruct(addr, size, msginfo_scgsv); 636 break; 637 default: 638 tsiz = size; 639 rv = sysctlbyname(sym2sysctl[idx], addr, &tsiz, 640 NULL, 0); 641 if (rv == -1) 642 err(1, "sysctlbyname: %s", sym2sysctl[idx]); 643 if (tsiz != size) 644 errx(1, "%s size mismatch " 645 "(expected %d, got %d)", 646 sym2sysctl[idx], size, tsiz); 647 break; 648 } 649 } 650 } 651 652 void 653 usage() 654 { 655 656 fprintf(stderr, 657 "usage: ipcs [-abcmopqstyMQST] [-C corefile] [-N namelist]\n"); 658 exit(1); 659 } 660