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