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 2006 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 #include <errno.h>
  29 #include <unistd.h>
  30 #include <strings.h>
  31 #include <rctl.h>
  32 #include <alloca.h>
  33 #include <values.h>
  34 #include <sys/syscall.h>
  35 #include <sys/msg.h>
  36 #include <sys/ipc.h>
  37 #include <sys/sem.h>
  38 #include <sys/shm.h>
  39 #include <sys/stat.h>
  40 #include <sys/types.h>
  41 #include <sys/lx_debug.h>
  42 #include <sys/lx_types.h>
  43 #include <sys/lx_sysv_ipc.h>
  44 #include <sys/lx_misc.h>
  45 #include <sys/lx_syscall.h>
  46 
  47 #define SLOT_SEM        0
  48 #define SLOT_SHM        1
  49 #define SLOT_MSG        2
  50 
  51 static int
  52 get_rctlval(rctlblk_t *rblk, char *name)
  53 {
  54         rctl_qty_t r;
  55 
  56         if (getrctl(name, NULL, rblk, RCTL_FIRST) == -1)
  57                 return (-errno);
  58 
  59         r = rctlblk_get_value(rblk);
  60         if (r > MAXINT)
  61                 return (-EOVERFLOW);
  62         return (r);
  63 }
  64 
  65 /*
  66  * Given a slot number and a maximum number of ids to extract from the
  67  * kernel, return the msgid in the provided slot.
  68  */
  69 static int
  70 slot_to_id(int type, int slot)
  71 {
  72         uint_t nids, max;
  73         int *idbuf = NULL;
  74         int r = 0;
  75 
  76         nids = 0;
  77         for (;;) {
  78                 switch (type) {
  79                 case SLOT_SEM:
  80                         r = semids(idbuf, nids, &max);
  81                         break;
  82                 case SLOT_SHM:
  83                         r = shmids(idbuf, nids, &max);
  84                         break;
  85                 case SLOT_MSG:
  86                         r = msgids(idbuf, nids, &max);
  87                         break;
  88                 }
  89 
  90                 if (r < 0)
  91                         return (-errno);
  92 
  93                 if (max == 0)
  94                         return (-EINVAL);
  95 
  96                 if (max <= nids)
  97                         return (idbuf[slot]);
  98 
  99                 nids = max;
 100                 if ((idbuf = (int *)SAFE_ALLOCA(sizeof (int) * nids)) == NULL)
 101                         return (-ENOMEM);
 102         }
 103 }
 104 
 105 /*
 106  * Semaphore operations.
 107  */
 108 static int
 109 lx_semget(key_t key, int nsems, int semflg)
 110 {
 111         int sol_flag;
 112         int r;
 113 
 114         lx_debug("\nsemget(%d, %d, %d)\n", key, nsems, semflg);
 115         sol_flag = semflg & S_IAMB;
 116         if (semflg & LX_IPC_CREAT)
 117                 sol_flag |= IPC_CREAT;
 118         if (semflg & LX_IPC_EXCL)
 119                 sol_flag |= IPC_EXCL;
 120 
 121         r = semget(key, nsems, sol_flag);
 122         return ((r < 0) ? -errno : r);
 123 }
 124 
 125 static int
 126 lx_semop(int semid, struct sembuf *sops, size_t nsops)
 127 {
 128         int r;
 129 
 130         lx_debug("\nsemop(%d, 0x%p, %u)\n", semid, sops, nsops);
 131         if (nsops == 0)
 132                 return (-EINVAL);
 133 
 134         r = semop(semid, sops, nsops);
 135         return ((r < 0) ? -errno : r);
 136 }
 137 
 138 static int
 139 lx_semctl_ipcset(int semid, void *buf)
 140 {
 141         struct lx_semid_ds semds;
 142         struct semid_ds sol_semds;
 143         int r;
 144 
 145         if (uucopy(buf, &semds, sizeof (semds)))
 146                 return (-errno);
 147 
 148         bzero(&sol_semds, sizeof (sol_semds));
 149         sol_semds.sem_perm.uid = semds.sem_perm.uid;
 150         sol_semds.sem_perm.gid = semds.sem_perm.gid;
 151         sol_semds.sem_perm.mode = semds.sem_perm.mode;
 152 
 153         r = semctl(semid, 0, IPC_SET, &sol_semds);
 154         return ((r < 0) ? -errno : r);
 155 }
 156 
 157 static int
 158 lx_semctl_ipcstat(int semid, void *buf)
 159 {
 160         struct lx_semid_ds semds;
 161         struct semid_ds sol_semds;
 162 
 163         if (semctl(semid, 0, IPC_STAT, &sol_semds) != 0)
 164                 return (-errno);
 165 
 166         bzero(&semds, sizeof (semds));
 167         semds.sem_perm.key = sol_semds.sem_perm.key;
 168         semds.sem_perm.seq = sol_semds.sem_perm.seq;
 169         semds.sem_perm.uid = sol_semds.sem_perm.uid;
 170         semds.sem_perm.gid = sol_semds.sem_perm.gid;
 171         semds.sem_perm.cuid = sol_semds.sem_perm.cuid;
 172         semds.sem_perm.cgid = sol_semds.sem_perm.cgid;
 173 
 174         /* Linux only uses the bottom 9 bits */
 175         semds.sem_perm.mode = sol_semds.sem_perm.mode & S_IAMB;
 176         semds.sem_otime = sol_semds.sem_otime;
 177         semds.sem_ctime = sol_semds.sem_ctime;
 178         semds.sem_nsems = sol_semds.sem_nsems;
 179 
 180         if (uucopy(&semds, buf, sizeof (semds)))
 181                 return (-errno);
 182 
 183         return (0);
 184 }
 185 
 186 static int
 187 lx_semctl_ipcinfo(void *buf)
 188 {
 189         struct lx_seminfo i;
 190         rctlblk_t *rblk;
 191         int rblksz;
 192         uint_t nids;
 193         int idbuf;
 194 
 195         rblksz = rctlblk_size();
 196         if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL)
 197                 return (-ENOMEM);
 198 
 199         bzero(&i, sizeof (i));
 200         if ((i.semmni = get_rctlval(rblk, "project.max-sem-ids")) < 0)
 201                 return (i.semmni);
 202         if ((i.semmsl = get_rctlval(rblk, "process.max-sem-nsems")) < 0)
 203                 return (i.semmsl);
 204         if ((i.semopm = get_rctlval(rblk, "process.max-sem-ops")) < 0)
 205                 return (i.semopm);
 206 
 207         /*
 208          * We don't have corresponding rctls for these fields.  The values
 209          * are taken from the formulas used to derive the defaults listed
 210          * in the Linux header file.  We're lying, but trying to be
 211          * coherent about it.
 212          */
 213         i.semmap = i.semmni;
 214         i.semmns = i.semmni * i.semmsl;
 215         i.semmnu = INT_MAX;
 216         i.semume = INT_MAX;
 217         i.semvmx = LX_SEMVMX;
 218         if (semids(&idbuf, 0, &nids) < 0)
 219                 return (-errno);
 220         i.semusz = nids;
 221         i.semaem = INT_MAX;
 222 
 223         if (uucopy(&i, buf, sizeof (i)) != 0)
 224                 return (-errno);
 225 
 226         return (nids);
 227 }
 228 
 229 static int
 230 lx_semctl_semstat(int slot, void *buf)
 231 {
 232         int r, semid;
 233 
 234         semid = slot_to_id(SLOT_SEM, slot);
 235         if (semid < 0)
 236                 return (semid);
 237 
 238         r = lx_semctl_ipcstat(semid, buf);
 239         return (r < 0 ? r : semid);
 240 }
 241 
 242 /*
 243  * For the SETALL operation, we have to examine each of the semaphore
 244  * values to be sure it is legal.
 245  */
 246 static int
 247 lx_semctl_setall(int semid, union lx_semun *arg)
 248 {
 249         struct semid_ds semds;
 250         ushort_t *vals;
 251         int i, sz, r;
 252 
 253         /*
 254          * Find out how many semaphores are involved, reserve enough
 255          * memory for an internal copy of the array, and then copy it in
 256          * from the process.
 257          */
 258         if (semctl(semid, 0, IPC_STAT, &semds) != 0)
 259                 return (-errno);
 260         sz = semds.sem_nsems * sizeof (ushort_t);
 261         if ((vals = SAFE_ALLOCA(sz)) == NULL)
 262                 return (-ENOMEM);
 263         if (uucopy(arg->sems, vals, sz))
 264                 return (-errno);
 265 
 266         /* Validate each of the values. */
 267         for (i = 0; i < semds.sem_nsems; i++)
 268                 if (vals[i] > LX_SEMVMX)
 269                         return (-ERANGE);
 270 
 271         r = semctl(semid, 0, SETALL, arg->sems);
 272 
 273         return ((r < 0) ? -errno : r);
 274 }
 275 
 276 static int
 277 lx_semctl(int semid, int semnum, int cmd, void *ptr)
 278 {
 279         union lx_semun arg;
 280         int rval;
 281         int opt = cmd & ~LX_IPC_64;
 282         int use_errno = 0;
 283 
 284         lx_debug("\nsemctl(%d, %d, %d, 0x%p)\n", semid, semnum, cmd, ptr);
 285 
 286         /*
 287          * The final arg to semctl() is a pointer to a union.  For some
 288          * commands we can hand that pointer directly to the kernel.  For
 289          * these commands, we need to extract an argument from the union
 290          * before calling into the kernel.
 291          */
 292         if (opt == LX_SETVAL || opt == LX_SETALL || opt == LX_GETALL ||
 293             opt == LX_IPC_SET || opt == LX_IPC_STAT || opt == LX_SEM_STAT ||
 294             opt == LX_IPC_INFO || opt == LX_SEM_INFO)
 295                 if (uucopy(ptr, &arg, sizeof (arg)))
 296                         return (-errno);
 297 
 298         switch (opt) {
 299         case LX_GETVAL:
 300                 use_errno = 1;
 301                 rval = semctl(semid, semnum, GETVAL, NULL);
 302                 break;
 303         case LX_SETVAL:
 304                 if (arg.val > LX_SEMVMX) {
 305                         rval = -ERANGE;
 306                         break;
 307                 }
 308                 use_errno = 1;
 309                 rval = semctl(semid, semnum, SETVAL, arg.val);
 310                 break;
 311         case LX_GETPID:
 312                 use_errno = 1;
 313                 rval = semctl(semid, semnum, GETPID, NULL);
 314                 break;
 315         case LX_GETNCNT:
 316                 use_errno = 1;
 317                 rval = semctl(semid, semnum, GETNCNT, NULL);
 318                 break;
 319         case LX_GETZCNT:
 320                 use_errno = 1;
 321                 rval = semctl(semid, semnum, GETZCNT, NULL);
 322                 break;
 323         case LX_GETALL:
 324                 use_errno = 1;
 325                 rval = semctl(semid, semnum, GETALL, arg.sems);
 326                 break;
 327         case LX_SETALL:
 328                 rval = lx_semctl_setall(semid, &arg);
 329                 break;
 330         case LX_IPC_RMID:
 331                 use_errno = 1;
 332                 rval = semctl(semid, semnum, IPC_RMID, NULL);
 333                 break;
 334         case LX_SEM_STAT:
 335                 rval = lx_semctl_semstat(semid, arg.semds);
 336                 break;
 337         case LX_IPC_STAT:
 338                 rval = lx_semctl_ipcstat(semid, arg.semds);
 339                 break;
 340 
 341         case LX_IPC_SET:
 342                 rval = lx_semctl_ipcset(semid, arg.semds);
 343                 break;
 344 
 345         case LX_IPC_INFO:
 346         case LX_SEM_INFO:
 347                 rval = lx_semctl_ipcinfo(arg.semds);
 348                 break;
 349 
 350         default:
 351                 rval = -EINVAL;
 352         }
 353 
 354         if (use_errno == 1 && rval < 0)
 355                 return (-errno);
 356         return (rval);
 357 }
 358 
 359 /*
 360  * msg operations.
 361  */
 362 static int
 363 lx_msgget(key_t key, int flag)
 364 {
 365         int sol_flag;
 366         int r;
 367 
 368         lx_debug("\tlx_msgget(%d, %d)\n", key, flag);
 369 
 370         sol_flag = flag & S_IAMB;
 371         if (flag & LX_IPC_CREAT)
 372                 sol_flag |= IPC_CREAT;
 373         if (flag & LX_IPC_EXCL)
 374                 sol_flag |= IPC_EXCL;
 375 
 376         r = msgget(key, sol_flag);
 377         return (r < 0 ? -errno : r);
 378 }
 379 
 380 static int
 381 lx_msgsnd(int id, struct msgbuf *buf, size_t sz, int flag)
 382 {
 383         int sol_flag = 0;
 384         int r;
 385 
 386         lx_debug("\tlx_msgsnd(%d, 0x%p, %d, %d)\n", id, buf, sz, flag);
 387 
 388         if (flag & LX_IPC_NOWAIT)
 389                 sol_flag |= IPC_NOWAIT;
 390 
 391         if (((ssize_t)sz < 0) || (sz > LX_MSGMAX))
 392                 return (-EINVAL);
 393 
 394         r = msgsnd(id, buf, sz, sol_flag);
 395         return (r < 0 ? -errno : r);
 396 }
 397 
 398 static int
 399 lx_msgrcv(int id, struct msgbuf *buf, size_t sz, int flag)
 400 {
 401         int sol_flag = 0;
 402         struct {
 403                 void *msgp;
 404                 long msgtype;
 405         } args;
 406         int r;
 407 
 408         /*
 409          * Rather than passing 5 args into ipc(2) directly, glibc passes 4
 410          * args and uses the buf argument to point to a structure
 411          * containing two args: a pointer to the message and the message
 412          * type.
 413          */
 414         if (uucopy(buf, &args, sizeof (args)))
 415                 return (-errno);
 416 
 417         lx_debug("\tlx_msgrcv(%d, 0x%p, %d, %d, %ld, %d)\n",
 418             id, args.msgp, sz, args.msgtype, flag);
 419 
 420         /*
 421          * Check for a negative sz parameter.
 422          *
 423          * Unlike msgsnd(2), the Linux man page does not specify that
 424          * msgrcv(2) should return EINVAL if (sz > MSGMAX), only if (sz < 0).
 425          */
 426         if ((ssize_t)sz < 0)
 427                 return (-EINVAL);
 428 
 429         if (flag & LX_MSG_NOERROR)
 430                 sol_flag |= MSG_NOERROR;
 431         if (flag & LX_IPC_NOWAIT)
 432                 sol_flag |= IPC_NOWAIT;
 433 
 434         r = msgrcv(id, args.msgp, sz, args.msgtype, sol_flag);
 435         return (r < 0 ? -errno : r);
 436 }
 437 
 438 static int
 439 lx_msgctl_ipcstat(int msgid, void *buf)
 440 {
 441         struct lx_msqid_ds msgids;
 442         struct msqid_ds sol_msgids;
 443         int r;
 444 
 445         r = msgctl(msgid, IPC_STAT, &sol_msgids);
 446         if (r < 0)
 447                 return (-errno);
 448 
 449         bzero(&msgids, sizeof (msgids));
 450         msgids.msg_perm.key = sol_msgids.msg_perm.key;
 451         msgids.msg_perm.seq = sol_msgids.msg_perm.seq;
 452         msgids.msg_perm.uid = sol_msgids.msg_perm.uid;
 453         msgids.msg_perm.gid = sol_msgids.msg_perm.gid;
 454         msgids.msg_perm.cuid = sol_msgids.msg_perm.cuid;
 455         msgids.msg_perm.cgid = sol_msgids.msg_perm.cgid;
 456 
 457         /* Linux only uses the bottom 9 bits */
 458         msgids.msg_perm.mode = sol_msgids.msg_perm.mode & S_IAMB;
 459 
 460         msgids.msg_stime = sol_msgids.msg_stime;
 461         msgids.msg_rtime = sol_msgids.msg_rtime;
 462         msgids.msg_ctime = sol_msgids.msg_ctime;
 463         msgids.msg_qbytes = sol_msgids.msg_qbytes;
 464         msgids.msg_cbytes = sol_msgids.msg_cbytes;
 465         msgids.msg_qnum = sol_msgids.msg_qnum;
 466         msgids.msg_lspid = sol_msgids.msg_lspid;
 467         msgids.msg_lrpid = sol_msgids.msg_lrpid;
 468 
 469         if (uucopy(&msgids, buf, sizeof (msgids)))
 470                 return (-errno);
 471 
 472         return (0);
 473 }
 474 
 475 static int
 476 lx_msgctl_ipcinfo(int cmd, void *buf)
 477 {
 478         struct lx_msginfo m;
 479         rctlblk_t *rblk;
 480         int idbuf, rblksz, msgseg, maxmsgs;
 481         uint_t nids;
 482         int rval;
 483 
 484         rblksz = rctlblk_size();
 485         if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL)
 486                 return (-ENOMEM);
 487 
 488         bzero(&m, sizeof (m));
 489         if ((m.msgmni = get_rctlval(rblk, "project.max-msg-ids")) < 0)
 490                 return (m.msgmni);
 491         if ((m.msgmnb = get_rctlval(rblk, "process.max-msg-qbytes")) < 0)
 492                 return (m.msgmnb);
 493 
 494         if (cmd == LX_IPC_INFO) {
 495                 if ((maxmsgs = get_rctlval(rblk,
 496                     "process.max-msg-messages")) < 0)
 497                         return (maxmsgs);
 498                 m.msgtql = maxmsgs * m.msgmni;
 499                 m.msgmap = m.msgmnb;
 500                 m.msgpool = m.msgmax * m.msgmnb;
 501                 rval = 0;
 502         } else {
 503                 if (msgids(&idbuf, 0, &nids) < 0)
 504                         return (-errno);
 505                 m.msgpool = nids;
 506 
 507                 /*
 508                  * For these fields, we can't even come up with a good fake
 509                  * approximation.  These are listed as 'obsolete' or
 510                  * 'unused' in the header files, so hopefully nobody is
 511                  * relying on them anyway.
 512                  */
 513                 m.msgtql = INT_MAX;
 514                 m.msgmap = INT_MAX;
 515                 rval = nids;
 516         }
 517 
 518         /*
 519          * We don't have corresponding rctls for these fields.  The values
 520          * are taken from the formulas used to derive the defaults listed
 521          * in the Linux header file.  We're lying, but trying to be
 522          * coherent about it.
 523          */
 524         m.msgmax = m.msgmnb;
 525         m.msgssz = 16;
 526         msgseg = (m.msgpool * 1024) / m.msgssz;
 527         m.msgseg = (msgseg > 0xffff) ? 0xffff : msgseg;
 528 
 529         if (uucopy(&m, buf, sizeof (m)))
 530                 return (-errno);
 531         return (rval);
 532 }
 533 
 534 static int
 535 lx_msgctl_ipcset(int msgid, void *buf)
 536 {
 537         struct lx_msqid_ds msgids;
 538         struct msqid_ds sol_msgids;
 539         int r;
 540 
 541         if (uucopy(buf, &msgids, sizeof (msgids)))
 542                 return (-errno);
 543 
 544         bzero(&sol_msgids, sizeof (sol_msgids));
 545         sol_msgids.msg_perm.uid = LX_UID16_TO_UID32(msgids.msg_perm.uid);
 546         sol_msgids.msg_perm.gid = LX_UID16_TO_UID32(msgids.msg_perm.gid);
 547 
 548         /* Linux only uses the bottom 9 bits */
 549         sol_msgids.msg_perm.mode = msgids.msg_perm.mode & S_IAMB;
 550         sol_msgids.msg_qbytes = msgids.msg_qbytes;
 551 
 552         r = msgctl(msgid, IPC_SET, &sol_msgids);
 553         return (r < 0 ? -errno : r);
 554 }
 555 
 556 static int
 557 lx_msgctl_msgstat(int slot, void *buf)
 558 {
 559         int r, msgid;
 560 
 561         lx_debug("msgstat(%d, 0x%p)\n", slot, buf);
 562 
 563         msgid = slot_to_id(SLOT_MSG, slot);
 564 
 565         if (msgid < 0)
 566                 return (msgid);
 567 
 568         r = lx_msgctl_ipcstat(msgid, buf);
 569         return (r < 0 ? r : msgid);
 570 }
 571 
 572 /*
 573  * Split off the various msgctl's here
 574  */
 575 static int
 576 lx_msgctl(int msgid, int cmd, void *buf)
 577 {
 578         int r;
 579 
 580         lx_debug("\tlx_msgctl(%d, %d, 0x%p)\n", msgid, cmd, buf);
 581         switch (cmd & ~LX_IPC_64) {
 582         case LX_IPC_RMID:
 583                 r = msgctl(msgid, IPC_RMID, NULL);
 584                 if (r < 0)
 585                         r = -errno;
 586                 break;
 587         case LX_IPC_SET:
 588                 r = lx_msgctl_ipcset(msgid, buf);
 589                 break;
 590         case LX_IPC_STAT:
 591                 r = lx_msgctl_ipcstat(msgid, buf);
 592                 break;
 593         case LX_MSG_STAT:
 594                 r = lx_msgctl_msgstat(msgid, buf);
 595                 break;
 596 
 597         case LX_IPC_INFO:
 598         case LX_MSG_INFO:
 599                 r = lx_msgctl_ipcinfo(cmd, buf);
 600                 break;
 601 
 602         default:
 603                 r = -EINVAL;
 604                 break;
 605         }
 606 
 607         return (r);
 608 }
 609 
 610 /*
 611  * shm-related operations.
 612  */
 613 static int
 614 lx_shmget(key_t key, size_t size, int flag)
 615 {
 616         int sol_flag;
 617         int r;
 618 
 619         lx_debug("\tlx_shmget(%d, %d, %d)\n", key, size, flag);
 620 
 621         sol_flag = flag & S_IAMB;
 622         if (flag & LX_IPC_CREAT)
 623                 sol_flag |= IPC_CREAT;
 624         if (flag & LX_IPC_EXCL)
 625                 sol_flag |= IPC_EXCL;
 626 
 627         r = shmget(key, size, sol_flag);
 628         return (r < 0 ? -errno : r);
 629 }
 630 
 631 static int
 632 lx_shmat(int shmid, void *addr, int flags, void **rval)
 633 {
 634         int sol_flags;
 635         void *ptr;
 636 
 637         lx_debug("\tlx_shmat(%d, 0x%p, %d, 0%o)\n", shmid, addr, flags);
 638 
 639         sol_flags = 0;
 640         if (flags & LX_SHM_RDONLY)
 641                 sol_flags |= SHM_RDONLY;
 642         if (flags & LX_SHM_RND)
 643                 sol_flags |= SHM_RND;
 644         if ((flags & LX_SHM_REMAP) && (addr == NULL))
 645                 return (-EINVAL);
 646 
 647         ptr = shmat(shmid, addr, sol_flags);
 648         if (ptr == (void *)-1)
 649                 return (-errno);
 650         if (uucopy(&ptr, rval, sizeof (ptr)) != 0)
 651                 return (-errno);
 652 
 653         return (0);
 654 }
 655 
 656 static int
 657 lx_shmctl_ipcinfo(void *buf)
 658 {
 659         struct lx_shminfo s;
 660         rctlblk_t *rblk;
 661         int rblksz;
 662 
 663         rblksz = rctlblk_size();
 664         if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL)
 665                 return (-ENOMEM);
 666 
 667         bzero(&s, sizeof (s));
 668         if ((s.shmmni = get_rctlval(rblk, "project.max-shm-ids")) < 0)
 669                 return (s.shmmni);
 670         if ((s.shmmax = get_rctlval(rblk, "project.max-shm-memory")) < 0)
 671                 return (s.shmmax);
 672 
 673         /*
 674          * We don't have corresponding rctls for these fields.  The values
 675          * are taken from the formulas used to derive the defaults listed
 676          * in the Linux header file.  We're lying, but trying to be
 677          * coherent about it.
 678          */
 679         s.shmmin = 1;
 680         s.shmseg = INT_MAX;
 681         s.shmall = s.shmmax / getpagesize();
 682 
 683         if (uucopy(&s, buf, sizeof (s)))
 684                 return (-errno);
 685 
 686         return (0);
 687 }
 688 
 689 static int
 690 lx_shmctl_ipcstat(int shmid, void *buf)
 691 {
 692         struct lx_shmid_ds shmds;
 693         struct shmid_ds sol_shmds;
 694 
 695         if (shmctl(shmid, IPC_STAT, &sol_shmds) != 0)
 696                 return (-errno);
 697 
 698         bzero(&shmds, sizeof (shmds));
 699         shmds.shm_perm.key = sol_shmds.shm_perm.key;
 700         shmds.shm_perm.seq = sol_shmds.shm_perm.seq;
 701         shmds.shm_perm.uid = sol_shmds.shm_perm.uid;
 702         shmds.shm_perm.gid = sol_shmds.shm_perm.gid;
 703         shmds.shm_perm.cuid = sol_shmds.shm_perm.cuid;
 704         shmds.shm_perm.cgid = sol_shmds.shm_perm.cgid;
 705         shmds.shm_perm.mode = sol_shmds.shm_perm.mode & S_IAMB;
 706         if (sol_shmds.shm_lkcnt > 0)
 707                 shmds.shm_perm.mode |= LX_SHM_LOCKED;
 708         shmds.shm_segsz = sol_shmds.shm_segsz;
 709         shmds.shm_atime  = sol_shmds.shm_atime;
 710         shmds.shm_dtime = sol_shmds.shm_dtime;
 711         shmds.shm_ctime = sol_shmds.shm_ctime;
 712         shmds.shm_cpid = sol_shmds.shm_cpid;
 713         shmds.shm_lpid = sol_shmds.shm_lpid;
 714         shmds.shm_nattch = (ushort_t)sol_shmds.shm_nattch;
 715 
 716         if (uucopy(&shmds, buf, sizeof (shmds)))
 717                 return (-errno);
 718 
 719         return (0);
 720 }
 721 
 722 static int
 723 lx_shmctl_ipcset(int shmid, void *buf)
 724 {
 725         struct lx_shmid_ds shmds;
 726         struct shmid_ds sol_shmds;
 727         int r;
 728 
 729         if (uucopy(buf, &shmds, sizeof (shmds)))
 730                 return (-errno);
 731 
 732         bzero(&sol_shmds, sizeof (sol_shmds));
 733         sol_shmds.shm_perm.uid = shmds.shm_perm.uid;
 734         sol_shmds.shm_perm.gid = shmds.shm_perm.gid;
 735         sol_shmds.shm_perm.mode = shmds.shm_perm.mode & S_IAMB;
 736 
 737         r = shmctl(shmid, IPC_SET, &sol_shmds);
 738         return (r < 0 ? -errno : r);
 739 }
 740 
 741 /*
 742  * Build and return a shm_info structure. We only return the bare
 743  * essentials required by ipcs. The rest of the info is not readily
 744  * available.
 745  */
 746 static int
 747 lx_shmctl_shminfo(void *buf)
 748 {
 749         struct lx_shm_info shminfo;
 750         uint_t nids;
 751         int idbuf;
 752 
 753         bzero(&shminfo, sizeof (shminfo));
 754 
 755         if (shmids(&idbuf, 0, &nids) < 0)
 756                 return (-errno);
 757 
 758         shminfo.used_ids = nids;
 759         if (uucopy(&shminfo, buf, sizeof (shminfo)) != 0)
 760                 return (-errno);
 761 
 762         return (nids);
 763 }
 764 
 765 static int
 766 lx_shmctl_shmstat(int slot, void *buf)
 767 {
 768         int r, shmid;
 769 
 770         lx_debug("shmctl_shmstat(%d, 0x%p)\n", slot, buf);
 771         shmid = slot_to_id(SLOT_SHM, slot);
 772         if (shmid < 0)
 773                 return (shmid);
 774 
 775         r = lx_shmctl_ipcstat(shmid, buf);
 776         return (r < 0 ? r : shmid);
 777 }
 778 
 779 static int
 780 lx_shmctl(int shmid, int cmd, void *buf)
 781 {
 782         int r;
 783         int use_errno = 0;
 784 
 785         lx_debug("\tlx_shmctl(%d, %d, 0x%p)\n", shmid, cmd, buf);
 786         switch (cmd & ~LX_IPC_64) {
 787         case LX_IPC_RMID:
 788                 use_errno = 1;
 789                 r = shmctl(shmid, IPC_RMID, NULL);
 790                 break;
 791 
 792         case LX_IPC_SET:
 793                 r = lx_shmctl_ipcset(shmid, buf);
 794                 break;
 795 
 796         case LX_IPC_STAT:
 797                 r = lx_shmctl_ipcstat(shmid, buf);
 798                 break;
 799 
 800         case LX_IPC_INFO:
 801                 r = lx_shmctl_ipcinfo(buf);
 802                 break;
 803 
 804         case LX_SHM_LOCK:
 805                 use_errno = 1;
 806                 r = shmctl(shmid, SHM_LOCK, NULL);
 807                 break;
 808 
 809         case LX_SHM_UNLOCK:
 810                 use_errno = 1;
 811                 r = shmctl(shmid, SHM_UNLOCK, NULL);
 812                 break;
 813 
 814         case LX_SHM_INFO:
 815                 r = lx_shmctl_shminfo(buf);
 816                 break;
 817 
 818         case LX_SHM_STAT:
 819                 r = lx_shmctl_shmstat(shmid, buf);
 820                 break;
 821         default:
 822                 r = -EINVAL;
 823                 break;
 824         }
 825 
 826         if (use_errno == 1 && r < 0)
 827                 return (-errno);
 828 
 829         return (r);
 830 }
 831 
 832 /*
 833  * Under Linux, glibc funnels all of the sysv IPC operations into this
 834  * single ipc(2) system call.  We need to blow that up and filter the
 835  * remnants into the proper Solaris system calls.
 836  */
 837 int
 838 lx_ipc(uintptr_t cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
 839     uintptr_t arg4)
 840 {
 841         int r;
 842         void *bufptr = (void *)arg4;
 843 
 844         lx_debug("lx_ipc(%d, %d, %d, %d, 0x%p, %d)\n",
 845                 cmd, arg1, arg2, arg3, bufptr, arg4);
 846 
 847         switch (cmd) {
 848         case LX_MSGGET:
 849                 r = lx_msgget((key_t)arg1, (int)arg2);
 850                 break;
 851         case LX_MSGSND:
 852                 r = lx_msgsnd((int)arg1, bufptr, (size_t)arg2, (int)arg3);
 853                 break;
 854         case LX_MSGRCV:
 855                 r = lx_msgrcv((int)arg1, bufptr, (size_t)arg2, (int)arg3);
 856                 break;
 857         case LX_MSGCTL:
 858                 r = lx_msgctl((int)arg1, (int)arg2, bufptr);
 859                 break;
 860         case LX_SEMCTL:
 861                 r = lx_semctl((int)arg1, (size_t)arg2, (int)arg3, bufptr);
 862                 break;
 863         case LX_SEMOP:
 864                 /*
 865                  * 'struct sembuf' is the same on Linux and Solaris, so we
 866                  * pass bufptr straight through.
 867                  */
 868                 r = lx_semop((int)arg1, bufptr, (size_t)arg2);
 869                 break;
 870         case LX_SEMGET:
 871                 r = lx_semget((int)arg1, (size_t)arg2, (int)arg3);
 872                 break;
 873         case LX_SHMAT:
 874                 r = lx_shmat((int)arg1, bufptr, (size_t)arg2, (void *)arg3);
 875                 break;
 876         case LX_SHMDT:
 877                 r = shmdt(bufptr);
 878                 if (r < 0)
 879                         r = -errno;
 880                 break;
 881         case LX_SHMGET:
 882                 r = lx_shmget((int)arg1, (size_t)arg2, (int)arg3);
 883                 break;
 884         case LX_SHMCTL:
 885                 r = lx_shmctl((int)arg1, (int)arg2, bufptr);
 886                 break;
 887 
 888         default:
 889                 r = -EINVAL;
 890         }
 891 
 892         return (r);
 893 }