MIB: add support for System V IPC information node

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
This commit is contained in:
David van Moolenbroek 2015-12-17 12:57:54 +00:00 committed by Lionel Sambuc
parent 5b1db95619
commit 2f09e77b82
3 changed files with 220 additions and 6 deletions

View 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}

View 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 "

View 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),
};