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