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