1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <mdb/mdb_modapi.h>
  27 #include <mdb/mdb_ks.h>
  28 
  29 #include <sys/types.h>
  30 #include <sys/mman.h>
  31 #include <sys/project.h>
  32 #include <sys/ipc_impl.h>
  33 #include <sys/shm_impl.h>
  34 #include <sys/sem_impl.h>
  35 #include <sys/msg_impl.h>
  36 
  37 #include <vm/anon.h>
  38 
  39 #define CMN_HDR_START   "%<u>"
  40 #define CMN_HDR_END     "%</u>\n"
  41 #define CMN_INDENT      (4)
  42 #define CMN_INACTIVE    "%s facility inactive.\n"
  43 
  44 /*
  45  * Bitmap data for page protection flags suitable for use with %b.
  46  */
  47 const mdb_bitmask_t prot_flag_bits[] = {
  48         { "PROT_READ", PROT_READ, PROT_READ },
  49         { "PROT_WRITE", PROT_WRITE, PROT_WRITE },
  50         { "PROT_EXEC", PROT_EXEC, PROT_EXEC },
  51         { "PROT_USER", PROT_USER, PROT_USER },
  52         { NULL, 0, 0 }
  53 };
  54 
  55 static void
  56 printtime_nice(const char *str, time_t time)
  57 {
  58         if (time)
  59                 mdb_printf("%s%Y\n", str, time);
  60         else
  61                 mdb_printf("%sn/a\n", str);
  62 }
  63 
  64 /*
  65  * Print header common to all IPC types.
  66  */
  67 static void
  68 ipcperm_header()
  69 {
  70         mdb_printf(CMN_HDR_START "%?s %5s %5s %8s %5s %5s %6s %5s %5s %5s %5s"
  71             CMN_HDR_END, "ADDR", "REF", "ID", "KEY", "MODE", "PRJID", "ZONEID",
  72             "OWNER", "GROUP", "CREAT", "CGRP");
  73 }
  74 
  75 /*
  76  * Print data common to all IPC types.
  77  */
  78 static void
  79 ipcperm_print(uintptr_t addr, kipc_perm_t *perm)
  80 {
  81         kproject_t proj;
  82         int res;
  83 
  84         res = mdb_vread(&proj, sizeof (kproject_t), (uintptr_t)perm->ipc_proj);
  85 
  86         if (res == -1)
  87                 mdb_warn("failed to read kproject_t at %#p", perm->ipc_proj);
  88 
  89         mdb_printf("%0?p %5d %5d", addr, perm->ipc_ref, perm->ipc_id);
  90         if (perm->ipc_key)
  91                 mdb_printf(" %8x", perm->ipc_key);
  92         else
  93                 mdb_printf(" %8s", "private");
  94         mdb_printf(" %5#o", perm->ipc_mode & 07777);
  95         if (res == -1)
  96                 mdb_printf(" %5s %5s", "<flt>", "<flt>");
  97         else
  98                 mdb_printf(" %5d %6d", proj.kpj_id, proj.kpj_zoneid);
  99         mdb_printf(" %5d %5d %5d %5d\n", perm->ipc_uid, perm->ipc_gid,
 100             perm->ipc_cuid, perm->ipc_cgid);
 101 
 102 }
 103 
 104 /*ARGSUSED*/
 105 static int
 106 ipcperm(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 107 {
 108         kipc_perm_t perm;
 109 
 110         if (!(flags & DCMD_ADDRSPEC))
 111                 return (DCMD_USAGE);
 112 
 113         if (DCMD_HDRSPEC(flags))
 114                 ipcperm_header();
 115 
 116         if (mdb_vread(&perm, sizeof (kipc_perm_t), addr) == -1) {
 117                 mdb_warn("failed to read kipc_perm_t at %#lx", addr);
 118                 return (DCMD_ERR);
 119         }
 120 
 121         ipcperm_print(addr, &perm);
 122         return (DCMD_OK);
 123 }
 124 
 125 
 126 #define MSG_SND_SIZE 0x1
 127 static int
 128 msgq_check_for_waiters(list_t *walk_this, int min, int max,
 129         int copy_wait, uintptr_t addr, int flag)
 130 
 131 {
 132         int found = 0;
 133         int ii;
 134         msgq_wakeup_t *walker, next;
 135         uintptr_t head;
 136 
 137         for (ii = min; ii < max; ii++) {
 138                 head = ((ulong_t)addr) + sizeof (list_t)*ii +
 139                     sizeof (list_node_t);
 140                 if (head != (uintptr_t)walk_this[ii].list_head.list_next) {
 141                         walker =
 142                             (msgq_wakeup_t *)walk_this[ii].list_head.list_next;
 143                         while (head != (uintptr_t)walker) {
 144                                 if (mdb_vread(&next, sizeof (msgq_wakeup_t),
 145                                     (uintptr_t)walker) == -1) {
 146                                         mdb_warn(
 147                                             "Failed to read message queue\n");
 148                                         return (found);
 149                                 }
 150 
 151                                 if (flag & MSG_SND_SIZE) {
 152                                         mdb_printf("%15lx\t%6d\t%15lx\t%15d\n",
 153                                             next.msgw_thrd, next.msgw_type,
 154                                             walker + (uintptr_t)
 155                                             OFFSETOF(msgq_wakeup_t,
 156                                             msgw_wake_cv), next.msgw_snd_size);
 157                                 } else {
 158                                         mdb_printf("%15lx\t%6d\t%15lx\t%15s\n",
 159                                             next.msgw_thrd, next.msgw_type,
 160                                             walker + (uintptr_t)
 161                                             OFFSETOF(msgq_wakeup_t,
 162                                             msgw_wake_cv),
 163                                             (copy_wait ? "yes":"no"));
 164                                 }
 165                                 found++;
 166                                 walker =
 167                                     (msgq_wakeup_t *)next.msgw_list.list_next;
 168                         }
 169                 }
 170         }
 171         return (found);
 172 }
 173 
 174 static void
 175 msq_print(kmsqid_t *msqid, uintptr_t addr)
 176 {
 177         int     total = 0;
 178 
 179         mdb_printf("&list: %-?p\n", addr + OFFSETOF(kmsqid_t, msg_list));
 180         mdb_printf("cbytes: 0t%lu    qnum: 0t%lu    qbytes: 0t%lu"
 181             "    qmax: 0t%lu\n", msqid->msg_cbytes, msqid->msg_qnum,
 182             msqid->msg_qbytes, msqid->msg_qmax);
 183         mdb_printf("lspid: 0t%d    lrpid: 0t%d\n",
 184             (int)msqid->msg_lspid, (int)msqid->msg_lrpid);
 185         printtime_nice("stime: ", msqid->msg_stime);
 186         printtime_nice("rtime: ", msqid->msg_rtime);
 187         printtime_nice("ctime: ", msqid->msg_ctime);
 188         mdb_printf("snd_cnt: 0t%lld    snd_cv: %hd (%p)\n",
 189             msqid->msg_snd_cnt, msqid->msg_snd_cv._opaque,
 190             addr + (uintptr_t)OFFSETOF(kmsqid_t, msg_snd_cv));
 191         mdb_printf("Blocked recievers\n");
 192         mdb_printf("%15s\t%6s\t%15s\t%15s\n", "Thread Addr",
 193             "Type", "cv addr", "copyout-wait?");
 194         total += msgq_check_for_waiters(&msqid->msg_cpy_block,
 195             0, 1, 1, addr + OFFSETOF(kmsqid_t, msg_cpy_block), 0);
 196         total += msgq_check_for_waiters(msqid->msg_wait_snd_ngt,
 197             0, MSG_MAX_QNUM + 1, 0,
 198             addr + OFFSETOF(kmsqid_t, msg_wait_snd_ngt), 0);
 199         mdb_printf("Blocked senders\n");
 200         total += msgq_check_for_waiters(&msqid->msg_wait_rcv,
 201             0, 1, 1, addr + OFFSETOF(kmsqid_t, msg_wait_rcv),
 202             MSG_SND_SIZE);
 203         mdb_printf("%15s\t%6s\t%15s\t%15s\n", "Thread Addr",
 204             "Type", "cv addr", "Msg Size");
 205         total += msgq_check_for_waiters(msqid->msg_wait_snd,
 206             0, MSG_MAX_QNUM + 1, 0, addr + OFFSETOF(kmsqid_t,
 207             msg_wait_snd), 0);
 208         mdb_printf("Total number of waiters: %d\n", total);
 209 }
 210 
 211 
 212 /*ARGSUSED1*/
 213 static void
 214 shm_print(kshmid_t *shmid, uintptr_t addr)
 215 {
 216         shmatt_t nattch;
 217 
 218         nattch = shmid->shm_perm.ipc_ref - (IPC_FREE(&shmid->shm_perm) ? 0 : 1);
 219 
 220         mdb_printf(CMN_HDR_START "%10s %?s %5s %7s %7s %7s %7s" CMN_HDR_END,
 221             "SEGSZ", "AMP", "LKCNT", "LPID", "CPID", "NATTCH", "CNATTCH");
 222         mdb_printf("%10#lx %?p %5u %7d %7d %7lu %7lu\n",
 223             shmid->shm_segsz, shmid->shm_amp, shmid->shm_lkcnt,
 224             (int)shmid->shm_lpid, (int)shmid->shm_cpid, nattch,
 225             shmid->shm_ismattch);
 226 
 227         printtime_nice("atime: ", shmid->shm_atime);
 228         printtime_nice("dtime: ", shmid->shm_dtime);
 229         printtime_nice("ctime: ", shmid->shm_ctime);
 230         mdb_printf("sptinfo: %-?p    sptseg: %-?p\n",
 231             shmid->shm_sptinfo, shmid->shm_sptseg);
 232         mdb_printf("sptprot: <%lb>\n", shmid->shm_sptprot, prot_flag_bits);
 233 }
 234 
 235 
 236 /*ARGSUSED1*/
 237 static void
 238 sem_print(ksemid_t *semid, uintptr_t addr)
 239 {
 240         mdb_printf("base: %-?p    nsems: 0t%u\n",
 241             semid->sem_base, semid->sem_nsems);
 242         printtime_nice("otime: ", semid->sem_otime);
 243         printtime_nice("ctime: ", semid->sem_ctime);
 244         mdb_printf("binary: %s\n", semid->sem_binary ? "yes" : "no");
 245 }
 246 
 247 typedef struct ipc_ops_vec {
 248         char    *iv_wcmd;       /* walker name          */
 249         char    *iv_ocmd;       /* output dcmd          */
 250         char    *iv_service;    /* service pointer      */
 251         void    (*iv_print)(void *, uintptr_t); /* output callback */
 252         size_t  iv_idsize;
 253 } ipc_ops_vec_t;
 254 
 255 ipc_ops_vec_t msq_ops_vec = {
 256         "msq",
 257         "kmsqid",
 258         "msq_svc",
 259         (void(*)(void *, uintptr_t))msq_print,
 260         sizeof (kmsqid_t)
 261 };
 262 
 263 ipc_ops_vec_t shm_ops_vec = {
 264         "shm",
 265         "kshmid",
 266         "shm_svc",
 267         (void(*)(void *, uintptr_t))shm_print,
 268         sizeof (kshmid_t)
 269 };
 270 
 271 ipc_ops_vec_t sem_ops_vec = {
 272         "sem",
 273         "ksemid",
 274         "sem_svc",
 275         (void(*)(void *, uintptr_t))sem_print,
 276         sizeof (ksemid_t)
 277 };
 278 
 279 
 280 /*
 281  * Generic IPC data structure display code
 282  */
 283 static int
 284 ds_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
 285     ipc_ops_vec_t *iv)
 286 {
 287         void *iddata;
 288 
 289         if (!(flags & DCMD_ADDRSPEC)) {
 290                 uint_t oflags = 0;
 291 
 292                 if (mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, 1, &oflags,
 293                     NULL) != argc)
 294                         return (DCMD_USAGE);
 295 
 296                 if (mdb_walk_dcmd(iv->iv_wcmd, oflags ? iv->iv_ocmd : "ipcperm",
 297                     argc, argv) == -1) {
 298                         mdb_warn("can't walk '%s'", iv->iv_wcmd);
 299                         return (DCMD_ERR);
 300                 }
 301                 return (DCMD_OK);
 302         }
 303 
 304         iddata = mdb_alloc(iv->iv_idsize, UM_SLEEP | UM_GC);
 305         if (mdb_vread(iddata, iv->iv_idsize, addr) == -1) {
 306                 mdb_warn("failed to read %s at %#lx", iv->iv_ocmd, addr);
 307                 return (DCMD_ERR);
 308         }
 309 
 310         if (!DCMD_HDRSPEC(flags) && iv->iv_print)
 311                 mdb_printf("\n");
 312 
 313         if (DCMD_HDRSPEC(flags) || iv->iv_print)
 314                 ipcperm_header();
 315 
 316         ipcperm_print(addr, (struct kipc_perm *)iddata);
 317         if (iv->iv_print) {
 318                 mdb_inc_indent(CMN_INDENT);
 319                 iv->iv_print(iddata, addr);
 320                 mdb_dec_indent(CMN_INDENT);
 321         }
 322 
 323         return (DCMD_OK);
 324 }
 325 
 326 
 327 /*
 328  * Stubs to call ds_print with the appropriate ops vector
 329  */
 330 static int
 331 cmd_kshmid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 332 {
 333         return (ds_print(addr, flags, argc, argv, &shm_ops_vec));
 334 }
 335 
 336 
 337 static int
 338 cmd_kmsqid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 339 {
 340         return (ds_print(addr, flags, argc, argv, &msq_ops_vec));
 341 }
 342 
 343 static int
 344 cmd_ksemid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 345 {
 346         return (ds_print(addr, flags, argc, argv, &sem_ops_vec));
 347 }
 348 
 349 /*
 350  * Generic IPC walker
 351  */
 352 
 353 static int
 354 ds_walk_init(mdb_walk_state_t *wsp)
 355 {
 356         ipc_ops_vec_t   *iv = wsp->walk_arg;
 357 
 358         if (wsp->walk_arg != NULL && wsp->walk_addr != NULL)
 359                 mdb_printf("ignoring provided address\n");
 360 
 361         if (wsp->walk_arg)
 362                 if (mdb_readvar(&wsp->walk_addr, iv->iv_service) == -1) {
 363                         mdb_printf("failed to read '%s'; module not present\n",
 364                             iv->iv_service);
 365                         return (WALK_DONE);
 366                 }
 367         else
 368                 wsp->walk_addr = wsp->walk_addr +
 369                     OFFSETOF(ipc_service_t, ipcs_usedids);
 370 
 371         if (mdb_layered_walk("list", wsp) == -1)
 372                 return (WALK_ERR);
 373 
 374         return (WALK_NEXT);
 375 }
 376 
 377 
 378 static int
 379 ds_walk_step(mdb_walk_state_t *wsp)
 380 {
 381         return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
 382             wsp->walk_cbdata));
 383 }
 384 
 385 /*
 386  * Generic IPC ID/key to pointer code
 387  */
 388 
 389 static int
 390 ipcid_impl(uintptr_t svcptr, uintptr_t id, uintptr_t *addr)
 391 {
 392         ipc_service_t service;
 393         kipc_perm_t perm;
 394         ipc_slot_t slot;
 395         uintptr_t slotptr;
 396         uint_t index;
 397 
 398         if (id > INT_MAX) {
 399                 mdb_warn("id out of range\n");
 400                 return (DCMD_ERR);
 401         }
 402 
 403         if (mdb_vread(&service, sizeof (ipc_service_t), svcptr) == -1) {
 404                 mdb_warn("failed to read ipc_service_t at %#lx", svcptr);
 405                 return (DCMD_ERR);
 406         }
 407 
 408         index = (uint_t)id & (service.ipcs_tabsz - 1);
 409         slotptr = (uintptr_t)(service.ipcs_table + index);
 410 
 411         if (mdb_vread(&slot, sizeof (ipc_slot_t), slotptr) == -1) {
 412                 mdb_warn("failed to read ipc_slot_t at %#lx", slotptr);
 413                 return (DCMD_ERR);
 414         }
 415 
 416         if (slot.ipct_data == NULL)
 417                 return (DCMD_ERR);
 418 
 419         if (mdb_vread(&perm, sizeof (kipc_perm_t),
 420             (uintptr_t)slot.ipct_data) == -1) {
 421                 mdb_warn("failed to read kipc_perm_t at %#p",
 422                     slot.ipct_data);
 423                 return (DCMD_ERR);
 424         }
 425 
 426         if (perm.ipc_id != (uint_t)id)
 427                 return (DCMD_ERR);
 428 
 429         *addr = (uintptr_t)slot.ipct_data;
 430 
 431         return (DCMD_OK);
 432 }
 433 
 434 
 435 typedef struct findkey_data {
 436         key_t fk_key;
 437         uintptr_t fk_addr;
 438         boolean_t fk_found;
 439 } findkey_data_t;
 440 
 441 static int
 442 findkey(uintptr_t addr, kipc_perm_t *perm, findkey_data_t *arg)
 443 {
 444         if (perm->ipc_key == arg->fk_key) {
 445                 arg->fk_found = B_TRUE;
 446                 arg->fk_addr = addr;
 447                 return (WALK_DONE);
 448         }
 449         return (WALK_NEXT);
 450 }
 451 
 452 static int
 453 ipckey_impl(uintptr_t svcptr, uintptr_t key, uintptr_t *addr)
 454 {
 455         ipc_service_t   service;
 456         findkey_data_t  fkdata;
 457 
 458         if ((key == IPC_PRIVATE) || (key > INT_MAX)) {
 459                 mdb_warn("key out of range\n");
 460                 return (DCMD_ERR);
 461         }
 462 
 463         if (mdb_vread(&service, sizeof (ipc_service_t), svcptr) == -1) {
 464                 mdb_warn("failed to read ipc_service_t at %#lx", svcptr);
 465                 return (DCMD_ERR);
 466         }
 467 
 468         fkdata.fk_key = (key_t)key;
 469         fkdata.fk_found = B_FALSE;
 470         if ((mdb_pwalk("avl", (mdb_walk_cb_t)findkey, &fkdata,
 471             svcptr + OFFSETOF(ipc_service_t, ipcs_keys)) == -1) ||
 472             !fkdata.fk_found)
 473                 return (DCMD_ERR);
 474 
 475         *addr = fkdata.fk_addr;
 476 
 477         return (DCMD_OK);
 478 }
 479 
 480 static int
 481 ipckeyid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
 482     int(*fp)(uintptr_t, uintptr_t, uintptr_t *))
 483 {
 484         uintmax_t val;
 485         uintptr_t raddr;
 486         int result;
 487 
 488         if (!(flags & DCMD_ADDRSPEC) || (argc != 1))
 489                 return (DCMD_USAGE);
 490 
 491         if (argv[0].a_type == MDB_TYPE_IMMEDIATE)
 492                 val = argv[0].a_un.a_val;
 493         else if (argv[0].a_type == MDB_TYPE_STRING)
 494                 val = mdb_strtoull(argv[0].a_un.a_str);
 495         else
 496                 return (DCMD_USAGE);
 497 
 498         result = fp(addr, val, &raddr);
 499 
 500         if (result == DCMD_OK)
 501                 mdb_printf("%lx", raddr);
 502 
 503         return (result);
 504 }
 505 
 506 static int
 507 ipckey(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 508 {
 509         return (ipckeyid(addr, flags, argc, argv, ipckey_impl));
 510 }
 511 
 512 static int
 513 ipcid(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 514 {
 515         return (ipckeyid(addr, flags, argc, argv, ipcid_impl));
 516 }
 517 
 518 static int
 519 ds_ptr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
 520     ipc_ops_vec_t *iv)
 521 {
 522         uint_t          kflag = FALSE;
 523         uintptr_t       svcptr, raddr;
 524         int             result;
 525 
 526         if (!(flags & DCMD_ADDRSPEC))
 527                 return (DCMD_USAGE);
 528 
 529         if (mdb_getopts(argc, argv,
 530             'k', MDB_OPT_SETBITS, TRUE, &kflag, NULL) != argc)
 531                 return (DCMD_USAGE);
 532 
 533         if (mdb_readvar(&svcptr, iv->iv_service) == -1) {
 534                 mdb_warn("failed to read '%s'; module not present\n",
 535                     iv->iv_service);
 536                 return (DCMD_ERR);
 537         }
 538 
 539         result = kflag ? ipckey_impl(svcptr, addr, &raddr) :
 540             ipcid_impl(svcptr, addr, &raddr);
 541 
 542         if (result == DCMD_OK)
 543                 mdb_printf("%lx", raddr);
 544 
 545         return (result);
 546 }
 547 
 548 /*
 549  * Stubs to call ds_ptr with the appropriate ops vector
 550  */
 551 static int
 552 id2shm(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 553 {
 554         return (ds_ptr(addr, flags, argc, argv, &shm_ops_vec));
 555 }
 556 
 557 static int
 558 id2msq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 559 {
 560         return (ds_ptr(addr, flags, argc, argv, &msq_ops_vec));
 561 }
 562 
 563 static int
 564 id2sem(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 565 {
 566         return (ds_ptr(addr, flags, argc, argv, &sem_ops_vec));
 567 }
 568 
 569 
 570 /*
 571  * The message queue contents walker
 572  */
 573 
 574 static int
 575 msg_walk_init(mdb_walk_state_t *wsp)
 576 {
 577         wsp->walk_addr += OFFSETOF(kmsqid_t, msg_list);
 578         if (mdb_layered_walk("list", wsp) == -1)
 579                 return (WALK_ERR);
 580 
 581         return (WALK_NEXT);
 582 }
 583 
 584 static int
 585 msg_walk_step(mdb_walk_state_t *wsp)
 586 {
 587         return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
 588             wsp->walk_cbdata));
 589 }
 590 
 591 /*
 592  * The "::ipcs" command itself.  Just walks each IPC type in turn.
 593  */
 594 
 595 /*ARGSUSED*/
 596 static int
 597 ipcs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 598 {
 599         uint_t  oflags = 0;
 600 
 601         if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv, 'l',
 602             MDB_OPT_SETBITS, 1, &oflags, NULL) != argc)
 603                 return (DCMD_USAGE);
 604 
 605         mdb_printf("Message queues:\n");
 606         if (mdb_walk_dcmd("msq", oflags ? "kmsqid" : "ipcperm", argc, argv) ==
 607             -1) {
 608                 mdb_warn("can't walk 'msq'");
 609                 return (DCMD_ERR);
 610         }
 611 
 612         mdb_printf("\nShared memory:\n");
 613         if (mdb_walk_dcmd("shm", oflags ? "kshmid" : "ipcperm", argc, argv) ==
 614             -1) {
 615                 mdb_warn("can't walk 'shm'");
 616                 return (DCMD_ERR);
 617         }
 618 
 619         mdb_printf("\nSemaphores:\n");
 620         if (mdb_walk_dcmd("sem", oflags ? "ksemid" : "ipcperm", argc, argv) ==
 621             -1) {
 622                 mdb_warn("can't walk 'sem'");
 623                 return (DCMD_ERR);
 624         }
 625 
 626         return (DCMD_OK);
 627 }
 628 
 629 static int
 630 msgprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 631 {
 632         struct msg message;
 633         uint_t  lflag = FALSE;
 634         long    type = 0;
 635         char    *tflag = NULL;
 636 
 637         if (!(flags & DCMD_ADDRSPEC) || (mdb_getopts(argc, argv,
 638             'l', MDB_OPT_SETBITS, TRUE, &lflag,
 639             't', MDB_OPT_STR, &tflag, NULL) != argc))
 640                 return (DCMD_USAGE);
 641 
 642         /*
 643          * Handle negative values.
 644          */
 645         if (tflag != NULL) {
 646                 if (*tflag == '-') {
 647                         tflag++;
 648                         type = -1;
 649                 } else {
 650                         type = 1;
 651                 }
 652                 type *= mdb_strtoull(tflag);
 653         }
 654 
 655         if (DCMD_HDRSPEC(flags))
 656                 mdb_printf("%<u>%?s %?s %8s %8s %8s%</u>\n",
 657                     "ADDR", "TEXT", "SIZE", "TYPE", "REF");
 658 
 659         if (mdb_vread(&message, sizeof (struct msg), addr) == -1) {
 660                 mdb_warn("failed to read msg at %#lx", addr);
 661                 return (DCMD_ERR);
 662         }
 663 
 664         /*
 665          * If we are meeting our type contraints, display the message.
 666          * If -l was specified, we will also display the message
 667          * contents.
 668          */
 669         if ((type == 0) ||
 670             (type > 0 && message.msg_type == type) ||
 671             (type < 0 && message.msg_type <= -type)) {
 672 
 673                 if (lflag && !DCMD_HDRSPEC(flags))
 674                         mdb_printf("\n");
 675 
 676                 mdb_printf("%0?lx %?p %8ld %8ld %8ld\n", addr, message.msg_addr,
 677                     message.msg_size, message.msg_type, message.msg_copycnt);
 678 
 679                 if (lflag) {
 680                         mdb_printf("\n");
 681                         mdb_inc_indent(CMN_INDENT);
 682                         if (mdb_dumpptr(
 683                             (uintptr_t)message.msg_addr, message.msg_size,
 684                             MDB_DUMP_RELATIVE | MDB_DUMP_TRIM |
 685                             MDB_DUMP_ASCII | MDB_DUMP_HEADER |
 686                             MDB_DUMP_GROUP(4),
 687                             (mdb_dumpptr_cb_t)mdb_vread, NULL)) {
 688                                 mdb_dec_indent(CMN_INDENT);
 689                                 return (DCMD_ERR);
 690                         }
 691                         mdb_dec_indent(CMN_INDENT);
 692                 }
 693         }
 694 
 695         return (DCMD_OK);
 696 }
 697 
 698 /*
 699  * MDB module linkage
 700  */
 701 static const mdb_dcmd_t dcmds[] = {
 702         /* Generic routines */
 703         { "ipcperm", ":", "display an IPC perm structure", ipcperm },
 704         { "ipcid", ":id", "perform an IPC id lookup", ipcid },
 705         { "ipckey", ":key", "perform an IPC key lookup", ipckey },
 706 
 707         /* Specific routines */
 708         { "kshmid", "?[-l]", "display a struct kshmid", cmd_kshmid },
 709         { "kmsqid", "?[-l]", "display a struct kmsqid", cmd_kmsqid },
 710         { "ksemid", "?[-l]", "display a struct ksemid", cmd_ksemid },
 711         { "msg", ":[-l] [-t type]", "display contents of a message", msgprint },
 712 
 713         /* Convenience routines */
 714         { "id2shm", ":[-k]", "convert shared memory ID to pointer", id2shm },
 715         { "id2msq", ":[-k]", "convert message queue ID to pointer", id2msq },
 716         { "id2sem", ":[-k]", "convert semaphore ID to pointer", id2sem },
 717 
 718         { "ipcs", "[-l]", "display System V IPC information", ipcs },
 719         { NULL }
 720 };
 721 
 722 static const mdb_walker_t walkers[] = {
 723         { "ipcsvc", "walk a System V IPC service",
 724                 ds_walk_init, ds_walk_step },
 725         { "shm", "walk the active shmid_ds structures",
 726                 ds_walk_init, ds_walk_step, NULL, &shm_ops_vec },
 727         { "msq", "walk the active msqid_ds structures",
 728                 ds_walk_init, ds_walk_step, NULL, &msq_ops_vec },
 729         { "sem", "walk the active semid_ds structures",
 730                 ds_walk_init, ds_walk_step, NULL, &sem_ops_vec },
 731         { "msgqueue", "walk messages on a message queue",
 732                 msg_walk_init, msg_walk_step },
 733         { NULL }
 734 };
 735 
 736 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
 737 
 738 const mdb_modinfo_t *
 739 _mdb_init(void)
 740 {
 741         return (&modinfo);
 742 }