]> Zhao Yanbai Git Server - minix.git/commitdiff
MIB: add support for System V IPC information node 64/3264/3
authorDavid van Moolenbroek <david@minix3.org>
Thu, 17 Dec 2015 12:57:54 +0000 (12:57 +0000)
committerLionel Sambuc <lionel.sambuc@gmail.com>
Sat, 16 Jan 2016 13:04:12 +0000 (14:04 +0100)
The kernel.ipc.sysvipc_info node is the gateway from NetBSD ipcs(1)
and ipcrm(1) to the IPC server, and thus necessary for a clean
import of these two utilities.  The MIB service implementation uses
the preexisting (Linux-specific) information calls on the IPC server
to obtain the information.

Change-Id: I85d1e193162d6b689f114764254dd7f314d2cfa0

minix/lib/libminc/Makefile
minix/servers/mib/kern.c
minix/usr.bin/trace/service/mib.c

index 7b545093b668b43d0174196671bddc042f3d4829..5fdc3c4be3f42b1e8496631c9d8033f266a90f46 100644 (file)
@@ -285,11 +285,11 @@ CLEANFILES+= ${f:C/\.o/.bc/}
 .for f in \
        access.o brk.o close.o environ.o execve.o fork.o fsync.o \
        getgid.o getpid.o geteuid.o getuid.o gettimeofday.o getvfsstat.o \
-       init.o kernel_utils.o link.o loadname.o lseek.o _mcontext.o mknod.o \
-       mmap.o nanosleep.o open.o pread.o pwrite.o read.o sbrk.o \
-       select.o setuid.o sigprocmask.o stack_utils.o stat.o stime.o \
-       svrctl.o syscall.o _ucontext.o umask.o unlink.o wait4.o write.o \
-       kill.o __sysctl.o
+       init.o kernel_utils.o kill.o link.o loadname.o lseek.o _mcontext.o \
+       minix_rs.o mknod.o mmap.o nanosleep.o open.o pread.o pwrite.o read.o \
+       sbrk.o select.o sem.o setuid.o shmctl.o sigprocmask.o stack_utils.o \
+       stat.o stime.o svrctl.o syscall.o __sysctl.o _ucontext.o umask.o \
+       unlink.o wait4.o write.o
 ${f} ${f:C/\.o/.bc/}:  ${LIBMINIXCDIR}/sys/${f:C/\.o/.c/}
 OBJS+= ${f}
 CLEANFILES+= ${f}
index e1565a23000a5b37aaa9bb086a57cca0d010d5cd..3657a0d893bc8652f77f7cd97763284760338098 100644 (file)
@@ -5,6 +5,8 @@
 #include <sys/svrctl.h>
 #include <minix/sysinfo.h>
 #include <machine/partition.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
 
 #include "servers/vfs/const.h"
 #include "servers/vfs/dmap.h"
@@ -298,9 +300,172 @@ mib_kern_boottime(struct mib_call * call __unused,
        return mib_copyout(oldp, 0, &tv, sizeof(tv));
 }
 
+/*
+ * Copy over an ipc_perm structure to an ipc_perm_sysctl structure.
+ */
+static void
+prepare_ipc_perm(struct ipc_perm_sysctl * perms, const struct ipc_perm * perm)
+{
+
+       memset(perms, 0, sizeof(*perms));
+       perms->_key = perm->_key;
+       perms->uid = perm->uid;
+       perms->gid = perm->gid;
+       perms->cuid = perm->cuid;
+       perms->cgid = perm->cgid;
+       perms->mode = perm->mode;
+       perms->_seq = perm->_seq;
+}
+
+/*
+ * Implementation of CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO.
+ */
+static ssize_t
+mib_kern_ipc_info(struct mib_call * call, struct mib_node * node __unused,
+       struct mib_oldp * oldp, struct mib_newp * newp __unused)
+{
+       struct sem_sysctl_info semsi;
+       struct shm_sysctl_info shmsi;
+       struct semid_ds semds;
+       struct shmid_ds shmds;
+       ssize_t r, off;
+       int i, max;
+
+       if (call->call_namelen != 1)
+               return EINVAL;
+
+       /*
+        * An important security note: according to the specification, IPC_STAT
+        * (and therefore, logically, its SEM_STAT and SHM_STAT siblings)
+        * performs read access checks on the caller, meaning that users other
+        * than root may not obtain details about IPC objects for which they do
+        * not have permission.  However, NetBSD's sysctl(2) interface to
+        * obtain the same information, which we mimic here, does *not* perform
+        * such permission checks.  For that reason, neither do we; the MIB
+        * service is running as root, so it can freely make stat calls, and
+        * expose the results to all users on the system.  If this is to be
+        * changed in the future, we would probably be better off moving the
+        * processing of this sysctl(2) node into the IPC server altogether.
+        */
+       off = 0;
+
+       switch (call->call_name[0]) {
+       case KERN_SYSVIPC_SEM_INFO:
+               memset(&semsi, 0, sizeof(semsi));
+               if ((max = semctl(0, 0, IPC_INFO, &semsi.seminfo)) == -1)
+                       return -errno;
+               /*
+                * As a hackish exception, the requested size may imply that
+                * just general information is to be returned, without throwing
+                * an ENOMEM error because there is no space for full output.
+                */
+               if (mib_getoldlen(oldp) == sizeof(semsi.seminfo))
+                       return mib_copyout(oldp, 0, &semsi.seminfo,
+                           sizeof(semsi.seminfo));
+               /*
+                * ipcs(1) blindly expects the returned array to be of size
+                * seminfo.semmni, using the SEM_ALLOC mode flag to see whether
+                * each entry is valid.  If we return a smaller size, ipcs(1)
+                * will access arbitrary memory.
+                */
+               if (semsi.seminfo.semmni <= 0)
+                       return EINVAL;
+               if (oldp == NULL)
+                       return sizeof(semsi) + sizeof(semsi.semids[0]) *
+                           (semsi.seminfo.semmni - 1);
+               /*
+                * Copy out entries one by one.  For the first entry, copy out
+                * the entire "semsi" structure.  For subsequent entries, reuse
+                * the single embedded 'semids' element of "semsi", and copy
+                * out only that element.
+                */
+               for (i = 0; i < semsi.seminfo.semmni; i++) {
+                       memset(&semsi.semids[0], 0, sizeof(semsi.semids[0]));
+                       if (i <= max && semctl(i, 0, SEM_STAT, &semds) >= 0) {
+                               prepare_ipc_perm(&semsi.semids[0].sem_perm,
+                                   &semds.sem_perm);
+                               semsi.semids[0].sem_nsems = semds.sem_nsems;
+                               semsi.semids[0].sem_otime = semds.sem_otime;
+                               semsi.semids[0].sem_ctime = semds.sem_ctime;
+                       }
+                       if (off == 0)
+                               r = mib_copyout(oldp, off, &semsi,
+                                   sizeof(semsi));
+                       else
+                               r = mib_copyout(oldp, off, &semsi.semids[0],
+                                   sizeof(semsi.semids[0]));
+                       if (r < 0)
+                               return r;
+                       off += r;
+               }
+               break;
+       case KERN_SYSVIPC_SHM_INFO:
+               memset(&shmsi, 0, sizeof(shmsi));
+               if ((max = shmctl(0, IPC_INFO,
+                   (struct shmid_ds *)&shmsi.shminfo)) == -1)
+                       return -errno;
+               /*
+                * As a hackish exception, the requested size may imply that
+                * just general information is to be returned, without throwing
+                * an ENOMEM error because there is no space for full output.
+                */
+               if (mib_getoldlen(oldp) == sizeof(shmsi.shminfo))
+                       return mib_copyout(oldp, 0, &shmsi.shminfo,
+                           sizeof(shmsi.shminfo));
+               /*
+                * ipcs(1) blindly expects the returned array to be of size
+                * shminfo.shmmni, using the SHMSEG_ALLOCATED (not exposed,
+                * 0x0800) mode flag to see whether each entry is valid.  If we
+                * return a smaller size, ipcs(1) will access arbitrary memory.
+                */
+               if (shmsi.shminfo.shmmni <= 0)
+                       return EINVAL;
+               if (oldp == NULL)
+                       return sizeof(shmsi) + sizeof(shmsi.shmids[0]) *
+                           (shmsi.shminfo.shmmni - 1);
+               /*
+                * Copy out entries one by one.  For the first entry, copy out
+                * the entire "shmsi" structure.  For subsequent entries, reuse
+                * the single embedded 'shmids' element of "shmsi", and copy
+                * out only that element.
+                */
+               for (i = 0; i < (int)shmsi.shminfo.shmmni; i++) {
+                       memset(&shmsi.shmids[0], 0, sizeof(shmsi.shmids[0]));
+                       if (i <= max && shmctl(i, SHM_STAT, &shmds) == 0) {
+                               prepare_ipc_perm(&shmsi.shmids[0].shm_perm,
+                                   &shmds.shm_perm);
+                               shmsi.shmids[0].shm_perm.mode |= 0x0800;
+                               shmsi.shmids[0].shm_segsz = shmds.shm_segsz;
+                               shmsi.shmids[0].shm_lpid = shmds.shm_lpid;
+                               shmsi.shmids[0].shm_cpid = shmds.shm_cpid;
+                               shmsi.shmids[0].shm_atime = shmds.shm_atime;
+                               shmsi.shmids[0].shm_dtime = shmds.shm_dtime;
+                               shmsi.shmids[0].shm_ctime = shmds.shm_ctime;
+                               shmsi.shmids[0].shm_nattch = shmds.shm_nattch;
+                       }
+                       if (off == 0)
+                               r = mib_copyout(oldp, off, &shmsi,
+                                   sizeof(shmsi));
+                       else
+                               r = mib_copyout(oldp, off, &shmsi.shmids[0],
+                                   sizeof(shmsi.shmids[0]));
+                       if (r < 0)
+                               return r;
+                       off += r;
+               }
+               break;
+       default:
+               return EOPNOTSUPP;
+       }
+
+       return off;
+}
+
 /* The CTL_KERN KERN_SYSVIPC nodes. */
 static struct mib_node mib_kern_ipc_table[] = {
-/* 1*/ /* KERN_SYSVIPC_INFO: not yet supported */
+/* 1*/ [KERN_SYSVIPC_INFO]     = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
+                                   mib_kern_ipc_info, "sysvipc_info",
+                                   "System V style IPC information"),
 /* 2*/ [KERN_SYSVIPC_MSG]      = MIB_INT(_P | _RO, 0, "sysvmsg", "System V "
                                    "style message support available"),
 /* 3*/ [KERN_SYSVIPC_SEM]      = MIB_INT(_P | _RO, 1, "sysvsem", "System V "
index 32cf8c4dbad762cf04b7d4cbee760c9c443ccd7a..d98a0f619e3c538044cea29b7a13a5d244878c20 100644 (file)
@@ -234,6 +234,54 @@ put_kern_boottime(struct trace_proc * proc, const char * name,
        return TRUE;
 }
 
+/*
+ * Print CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO.
+ */
+static int
+put_kern_sysvipc_info(struct trace_proc * proc, const char * name,
+       int type, const void * ptr __unused, vir_bytes addr, size_t size)
+{
+       const int *mib;
+       const char *text;
+       int i;
+
+       /*
+        * TODO: print the obtained structure(s).  For now we are just
+        * concerned with the name components.
+        */
+       if (type != ST_NAME) {
+               put_ptr(proc, name, addr);
+
+               return TRUE;
+       }
+
+       mib = (const int *)ptr;
+
+       for (i = 0; i < size; i++) {
+               text = NULL;
+
+               if (i == 0) {
+                       switch (mib[i]) {
+                       case KERN_SYSVIPC_SEM_INFO: text = "<sem>"; break;
+                       case KERN_SYSVIPC_SHM_INFO: text = "<shm>"; break;
+                       case KERN_SYSVIPC_MSG_INFO: text = "<msg>"; break;
+                       }
+               }
+
+               if (!valuesonly && text != NULL)
+                       put_field(proc, NULL, text);
+               else
+                       put_value(proc, NULL, "%d", mib[i]);
+       }
+
+       return 0;
+}
+
+/* The CTL_KERN KERN_SYSVIPC table. */
+static const struct sysctl_tab kern_sysvipc_tab[] = {
+       PROC(KERN_SYSVIPC_INFO, 0, put_kern_sysvipc_info),
+};
+
 /* The CTL_KERN table. */
 static const struct sysctl_tab kern_tab[] = {
        PROC(KERN_CLOCKRATE, sizeof(struct clockinfo), put_kern_clockrate),
@@ -242,6 +290,7 @@ static const struct sysctl_tab kern_tab[] = {
        PROC(KERN_CP_TIME, sizeof(uint64_t) * CPUSTATES, put_kern_cp_time),
        PROC(KERN_CONSDEV, sizeof(dev_t), put_kern_consdev),
        PROC(KERN_DRIVERS, 0, put_kern_drivers),
+       NODE(KERN_SYSVIPC, kern_sysvipc_tab),
        PROC(KERN_BOOTTIME, 0, put_kern_boottime),
 };