new ipc server that implements shared memory and semaphores.

this server, tests, vm support, library stubs and other contributions
are the work of Guanqun Lu, a 2009 GSOC student.
This commit is contained in:
Ben Gras 2009-09-21 14:53:13 +00:00
parent 32fbbd370c
commit 75d3db4911
7 changed files with 1182 additions and 0 deletions

View file

@ -24,6 +24,7 @@ all install depend clean:
cd ./vm && $(MAKE) $@
cd ./init && $(MAKE) $@
cd ./inet && $(MAKE) $@
cd ./ipc && $(MAKE) $@
image:
cd ./pm && $(MAKE) EXTRA_OPTS=$(EXTRA_OPTS) build

30
servers/ipc/Makefile Normal file
View file

@ -0,0 +1,30 @@
# Makefile for IPC server
SERVER = ipc
include /etc/make.conf
OBJ = main.o utility.o shm.o sem.o
CPPFLAGS=
CFLAGS = $(CPROFILE) $(CPPFLAGS)
# build local binary
all build: $(SERVER)
install: $(SERVER)
install -o root -c $< /usr/sbin/$(SERVER)
$(SERVER): $(OBJ)
$(CC) -o $@ $(LDFLAGS) $(OBJ) -lsys
# clean up local files
clean:
rm -f $(SERVER) *.o *.bak
depend:
mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
# Include generated dependencies.
include .depend

47
servers/ipc/inc.h Normal file
View file

@ -0,0 +1,47 @@
#define _SYSTEM 1
#define _MINIX 1
#include <minix/callnr.h>
#include <minix/com.h>
#include <minix/config.h>
#include <minix/ipc.h>
#include <minix/endpoint.h>
#include <minix/sysutil.h>
#include <minix/const.h>
#include <minix/type.h>
#include <minix/syslib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <sys/vm_i386.h>
#include <sys/vm.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
_PROTOTYPE( int do_shmget, (message *) );
_PROTOTYPE( int do_shmat, (message *) );
_PROTOTYPE( int do_shmdt, (message *) );
_PROTOTYPE( int do_shmctl, (message *) );
_PROTOTYPE( int check_perm, (struct ipc_perm *, endpoint_t, int) );
_PROTOTYPE( void list_shm_ds, (void) );
_PROTOTYPE( void update_refcount_and_destroy, (void) );
_PROTOTYPE( int do_semget, (message *) );
_PROTOTYPE( int do_semctl, (message *) );
_PROTOTYPE( int do_semop, (message *) );
_PROTOTYPE( int is_sem_nil, (void) );
_PROTOTYPE( int is_shm_nil, (void) );
_PROTOTYPE( void sem_process_vm_notify, (void) );
EXTERN int identifier;
EXTERN endpoint_t who_e;
EXTERN int call_type;
EXTERN endpoint_t SELF_E;

102
servers/ipc/main.c Normal file
View file

@ -0,0 +1,102 @@
#include "inc.h"
PUBLIC int identifier = 0x1234;
PUBLIC endpoint_t who_e;
PUBLIC int call_type;
PUBLIC endpoint_t SELF_E;
struct {
int type;
int (*func)(message *);
int reply; /* whether the reply action is passed through */
} ipc_calls[] = {
{ IPC_SHMGET, do_shmget, 0 },
{ IPC_SHMAT, do_shmat, 0 },
{ IPC_SHMDT, do_shmdt, 0 },
{ IPC_SHMCTL, do_shmctl, 0 },
{ IPC_SEMGET, do_semget, 0 },
{ IPC_SEMCTL, do_semctl, 0 },
{ IPC_SEMOP, do_semop, 1 },
};
#define SIZE(a) (sizeof(a)/sizeof(a[0]))
int verbose = 0;
PUBLIC int main(int argc, char *argv[])
{
message m;
SELF_E = getprocnr();
if(verbose)
printf("IPC: self: %d\n", SELF_E);
while (TRUE) {
int r;
int i;
if ((r = receive(ANY, &m)) != OK)
printf("IPC receive error %d.\n", r);
who_e = m.m_source;
call_type = m.m_type;
if(verbose)
printf("IPC: get %d from %d\n", call_type, who_e);
if (call_type & NOTIFY_MESSAGE) {
switch (who_e) {
case PM_PROC_NR:
/* PM sends a notify() on shutdown,
* checkout if there are still IPC keys,
* give warning messages.
*/
if (!is_sem_nil() || !is_shm_nil())
printf("IPC: exit with un-clean states.\n");
break;
case VM_PROC_NR:
/* currently, only semaphore needs such information. */
sem_process_vm_notify();
break;
default:
printf("IPC: ignoring notify() from %d\n",
who_e);
break;
}
continue;
}
/* dispatch messages */
for (i = 0; i < SIZE(ipc_calls); i++) {
if (ipc_calls[i].type == call_type) {
int result;
result = ipc_calls[i].func(&m);
if (ipc_calls[i].reply)
break;
m.m_type = result;
if(verbose && result != OK)
printf("IPC: error for %d: %d\n",
call_type, result);
if ((r = sendnb(who_e, &m)) != OK)
printf("IPC send error %d.\n", r);
break;
}
}
if (i == SIZE(ipc_calls)) {
/* warn and then ignore */
printf("IPC unknown call type: %d from %d.\n",
call_type, who_e);
}
update_refcount_and_destroy();
}
/* no way to get here */
return -1;
}

619
servers/ipc/sem.c Normal file
View file

@ -0,0 +1,619 @@
#define __USE_MISC
#include "inc.h"
struct waiting {
endpoint_t who; /* who is waiting */
int val; /* value he/she is waiting for */
};
struct semaphore {
unsigned short semval; /* semaphore value */
unsigned short semzcnt; /* # waiting for zero */
unsigned short semncnt; /* # waiting for increase */
struct waiting *zlist; /* process waiting for zero */
struct waiting *nlist; /* process waiting for increase */
pid_t sempid; /* process that did last op */
};
struct sem_struct {
key_t key;
int id;
struct semid_ds semid_ds;
struct semaphore sems[SEMMSL];
};
PRIVATE struct sem_struct sem_list[SEMMNI];
PRIVATE int sem_list_nr = 0;
PRIVATE struct sem_struct *sem_find_key(key_t key)
{
int i;
if (key == IPC_PRIVATE)
return NULL;
for (i = 0; i < sem_list_nr; i++)
if (sem_list[i].key == key)
return sem_list+i;
return NULL;
}
PRIVATE struct sem_struct *sem_find_id(int id)
{
int i;
for (i = 0; i < sem_list_nr; i++)
if (sem_list[i].id == id)
return sem_list+i;
return NULL;
}
/*===========================================================================*
* do_semget *
*===========================================================================*/
PUBLIC int do_semget(message *m)
{
key_t key;
int nsems, flag, id;
struct sem_struct *sem;
key = m->SEMGET_KEY;
nsems = m->SEMGET_NR;
flag = m->SEMGET_FLAG;
if ((sem = sem_find_key(key))) {
if ((flag & IPC_CREAT) && (flag & IPC_EXCL))
return EEXIST;
if (!check_perm(&sem->semid_ds.sem_perm, who_e, flag))
return EACCES;
if (nsems > sem->semid_ds.sem_nsems)
return EINVAL;
id = sem->id;
} else {
if (!(flag & IPC_CREAT))
return ENOENT;
if (nsems < 0 || nsems >= SEMMSL)
return EINVAL;
if (sem_list_nr == SEMMNI)
return ENOSPC;
/* create a new semaphore set */
sem = &sem_list[sem_list_nr];
memset(sem, 0, sizeof(struct sem_struct));
sem->semid_ds.sem_perm.cuid =
sem->semid_ds.sem_perm.uid = getnuid(who_e);
sem->semid_ds.sem_perm.cgid =
sem->semid_ds.sem_perm.gid = getngid(who_e);
sem->semid_ds.sem_perm.mode = flag & 0777;
sem->semid_ds.sem_nsems = nsems;
sem->semid_ds.sem_otime = 0;
sem->semid_ds.sem_ctime = time(NULL);
sem->id = id = identifier++;
sem->key = key;
sem_list_nr++;
}
m->SEMGET_RETID = id;
return OK;
}
PRIVATE void send_message_to_process(endpoint_t who, int ret, int ignore)
{
message m;
int r;
m.m_type = ret;
r = sendnb(who, &m);
if (r != OK && !ignore)
printf("IPC send error!\n");
}
PRIVATE void remove_semaphore(struct sem_struct *sem)
{
int i, nr;
nr = sem->semid_ds.sem_nsems;
for (i = 0; i < nr; i++) {
if (sem->sems[i].zlist)
free(sem->sems[i].zlist);
if (sem->sems[i].nlist)
free(sem->sems[i].nlist);
}
for (i = 0; i < sem_list_nr; i++) {
if (&sem_list[i] == sem)
break;
}
if (i < sem_list_nr && --sem_list_nr != i)
sem_list[i] = sem_list[sem_list_nr];
}
PRIVATE void show_semaphore(void)
{
int i, j, k;
for (i = 0; i < sem_list_nr; i++) {
int nr = sem_list[i].semid_ds.sem_nsems;
printf("===== [%d] =====\n", i);
for (j = 0; j < nr; j++) {
struct semaphore *semaphore = &sem_list[i].sems[j];
if (!semaphore->semzcnt && !semaphore->semncnt)
continue;
printf(" (%d): ", semaphore->semval);
if (semaphore->semzcnt) {
printf("zero(");
for (k = 0; k < semaphore->semzcnt; k++)
printf("%d,", semaphore->zlist[k].who);
printf(") ");
}
if (semaphore->semncnt) {
printf("incr(");
for (k = 0; k < semaphore->semncnt; k++)
printf("%d-%d,",
semaphore->nlist[k].who, semaphore->nlist[k].val);
printf(")");
}
printf("\n");
}
}
printf("\n");
}
PRIVATE void remove_process(endpoint_t pt)
{
int i;
for (i = 0; i < sem_list_nr; i++) {
struct sem_struct *sem = &sem_list[i];
int nr = sem->semid_ds.sem_nsems;
int j;
for (j = 0; j < nr; j++) {
struct semaphore *semaphore = &sem->sems[j];
int k;
for (k = 0; k < semaphore->semzcnt; k++) {
endpoint_t who_waiting = semaphore->zlist[k].who;
if (who_waiting == pt) {
/* remove this slot first */
memmove(semaphore->zlist+k, semaphore->zlist+k+1,
sizeof(struct waiting) * (semaphore->semzcnt-k-1));
--semaphore->semzcnt;
/* then send message to the process */
send_message_to_process(who_waiting, EINTR, 1);
break;
}
}
for (k = 0; k < semaphore->semncnt; k++) {
endpoint_t who_waiting = semaphore->nlist[k].who;
if (who_waiting == pt) {
/* remove it first */
memmove(semaphore->nlist+k, semaphore->nlist+k+1,
sizeof(struct waiting) * (semaphore->semncnt-k-1));
--semaphore->semncnt;
/* send the message to the process */
send_message_to_process(who_waiting, EINTR, 1);
break;
}
}
}
}
}
PRIVATE void update_one_semaphore(struct sem_struct *sem, int is_remove)
{
int i, j, nr;
struct semaphore *semaphore;
endpoint_t who;
nr = sem->semid_ds.sem_nsems;
if (is_remove) {
for (i = 0; i < nr; i++) {
semaphore = &sem->sems[i];
for (j = 0; j < semaphore->semzcnt; j++)
send_message_to_process(semaphore->zlist[j].who, EIDRM, 0);
for (j = 0; j < semaphore->semncnt; j++)
send_message_to_process(semaphore->nlist[j].who, EIDRM, 0);
}
remove_semaphore(sem);
return;
}
for (i = 0; i < nr; i++) {
semaphore = &sem->sems[i];
if (semaphore->zlist && !semaphore->semval) {
/* choose one process, policy: FIFO. */
who = semaphore->zlist[0].who;
memmove(semaphore->zlist, semaphore->zlist+1,
sizeof(struct waiting) * (semaphore->semzcnt-1));
--semaphore->semzcnt;
send_message_to_process(who, OK, 0);
}
if (semaphore->nlist) {
for (j = 0; j < semaphore->semncnt; j++) {
if (semaphore->nlist[j].val <= semaphore->semval) {
semaphore->semval -= semaphore->nlist[j].val;
who = semaphore->nlist[j].who;
memmove(semaphore->nlist+j, semaphore->nlist+j+1,
sizeof(struct waiting) * (semaphore->semncnt-j-1));
--semaphore->semncnt;
send_message_to_process(who, OK, 0);
/* choose only one process */
break;
}
}
}
}
}
PRIVATE void update_semaphores(void)
{
int i;
for (i = 0; i < sem_list_nr; i++)
update_one_semaphore(sem_list+i, 0 /* not remove */);
}
/*===========================================================================*
* do_semctl *
*===========================================================================*/
PUBLIC int do_semctl(message *m)
{
int r, i;
long opt;
uid_t uid;
int id, num, cmd, val;
unsigned short *buf;
struct semid_ds *ds, tmp_ds;
struct sem_struct *sem;
struct semaphore *semaphore;
id = m->SEMCTL_ID;
num = m->SEMCTL_NUM;
cmd = m->SEMCTL_CMD;
if (cmd == IPC_STAT || cmd == IPC_SET || cmd == IPC_INFO ||
cmd == SEM_INFO || cmd == SEM_STAT || cmd == GETALL ||
cmd == SETALL || cmd == SETVAL)
opt = m->SEMCTL_OPT;
if (!(sem = sem_find_id(id))) {
printf("IPC: not found %d\n", id);
return EINVAL;
}
/* IPC_SET and IPC_RMID as its own permission check */
if (cmd != IPC_SET && cmd != IPC_RMID) {
/* check read permission */
if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0444))
return EACCES;
}
switch (cmd) {
case IPC_STAT:
ds = (struct semid_ds *) opt;
if (!ds)
return EFAULT;
r = sys_datacopy(SELF_E, (vir_bytes) &sem->semid_ds,
who_e, (vir_bytes) ds, sizeof(struct semid_ds));
if (r != OK)
return EINVAL;
break;
case IPC_SET:
uid = getnuid(who_e);
if (uid != sem->semid_ds.sem_perm.cuid &&
uid != sem->semid_ds.sem_perm.uid &&
uid != 0)
return EPERM;
ds = (struct semid_ds *) opt;
r = sys_datacopy(who_e, (vir_bytes) ds,
SELF_E, (vir_bytes) &tmp_ds, sizeof(struct semid_ds));
if (r != OK)
return EINVAL;
sem->semid_ds.sem_perm.uid = tmp_ds.sem_perm.uid;
sem->semid_ds.sem_perm.gid = tmp_ds.sem_perm.gid;
sem->semid_ds.sem_perm.mode &= ~0777;
sem->semid_ds.sem_perm.mode |= tmp_ds.sem_perm.mode & 0666;
sem->semid_ds.sem_ctime = time(NULL);
break;
case IPC_RMID:
uid = getnuid(who_e);
if (uid != sem->semid_ds.sem_perm.cuid &&
uid != sem->semid_ds.sem_perm.uid &&
uid != 0)
return EPERM;
/* awaken all processes block in semop
* and remove the semaphore set.
*/
update_one_semaphore(sem, 1);
break;
case IPC_INFO:
break;
case SEM_INFO:
break;
case SEM_STAT:
break;
case GETALL:
buf = malloc(sizeof(unsigned short) * sem->semid_ds.sem_nsems);
if (!buf)
return ENOMEM;
for (i = 0; i < sem->semid_ds.sem_nsems; i++)
buf[i] = sem->sems[i].semval;
r = sys_datacopy(SELF_E, (vir_bytes) buf,
who_e, (vir_bytes) opt,
sizeof(unsigned short) * sem->semid_ds.sem_nsems);
if (r != OK)
return EINVAL;
free(buf);
break;
case GETNCNT:
if (num < 0 || num >= sem->semid_ds.sem_nsems)
return EINVAL;
m->SHMCTL_RET = sem->sems[num].semncnt;
break;
case GETPID:
if (num < 0 || num >= sem->semid_ds.sem_nsems)
return EINVAL;
m->SHMCTL_RET = sem->sems[num].sempid;
break;
case GETVAL:
if (num < 0 || num >= sem->semid_ds.sem_nsems)
return EINVAL;
m->SHMCTL_RET = sem->sems[num].semval;
break;
case GETZCNT:
if (num < 0 || num >= sem->semid_ds.sem_nsems)
return EINVAL;
m->SHMCTL_RET = sem->sems[num].semzcnt;
break;
case SETALL:
buf = malloc(sizeof(unsigned short) * sem->semid_ds.sem_nsems);
if (!buf)
return ENOMEM;
r = sys_datacopy(who_e, (vir_bytes) opt,
SELF_E, (vir_bytes) buf,
sizeof(unsigned short) * sem->semid_ds.sem_nsems);
if (r != OK)
return EINVAL;
#ifdef DEBUG_SEM
printf("SEMCTL: SETALL: opt: %p\n");
for (i = 0; i < sem->semid_ds.sem_nsems; i++)
printf("SEMCTL: SETALL val: [%d] %d\n", i, buf[i]);
#endif
for (i = 0; i < sem->semid_ds.sem_nsems; i++) {
if (buf[i] < 0 || buf[i] > SEMVMX) {
free(buf);
update_semaphores();
return ERANGE;
}
sem->sems[i].semval = buf[i];
}
free(buf);
/* awaken if possible */
update_semaphores();
break;
case SETVAL:
val = (int) opt;
/* check write permission */
if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0222))
return EACCES;
if (num < 0 || num >= sem->semid_ds.sem_nsems)
return EINVAL;
if (val < 0 || val > SEMVMX)
return ERANGE;
sem->sems[num].semval = val;
#ifdef DEBUG_SEM
printf("SEMCTL: SETVAL: %d %d\n", num, val);
#endif
sem->semid_ds.sem_ctime = time(NULL);
/* awaken if possible */
update_semaphores();
break;
default:
return EINVAL;
}
return OK;
}
/*===========================================================================*
* do_semop *
*===========================================================================*/
PUBLIC int do_semop(message *m)
{
int id, i, j, r;
struct sembuf *sops;
unsigned int nsops;
struct sem_struct *sem;
int no_reply;
id = m->SEMOP_ID;
nsops = (unsigned int) m->SEMOP_SIZE;
r = EINVAL;
if (!(sem = sem_find_id(id)))
goto out;
if (nsops <= 0)
goto out;
r = E2BIG;
if (nsops > SEMOPM)
goto out;
/* check for read permission */
r = EACCES;
if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0444))
goto out;
/* get the array from user application */
r = ENOMEM;
sops = malloc(sizeof(struct sembuf) * nsops);
if (!sops)
goto out_free;
r = sys_datacopy(who_e, (vir_bytes) m->SEMOP_OPS,
SELF_E, (vir_bytes) sops,
sizeof(struct sembuf) * nsops);
if (r != OK) {
r = EINVAL;
goto out_free;
}
#ifdef DEBUG_SEM
for (i = 0; i < nsops; i++)
printf("SEMOP: num:%d op:%d flg:%d\n",
sops[i].sem_num, sops[i].sem_op, sops[i].sem_flg);
#endif
/* check for value range */
r = EFBIG;
for (i = 0; i < nsops; i++)
if (sops[i].sem_num < 0 ||
sops[i].sem_num >= sem->semid_ds.sem_nsems)
goto out_free;
/* check for duplicate number */
r = EINVAL;
for (i = 0; i < nsops; i++)
for (j = i + 1; j < nsops; j++)
if (sops[i].sem_num == sops[j].sem_num)
goto out_free;
/* check for EAGAIN error */
r = EAGAIN;
for (i = 0; i < nsops; i++) {
int op_n, val;
op_n = sops[i].sem_op;
val = sem->sems[sops[i].sem_num].semval;
if ((sops[i].sem_flg & IPC_NOWAIT) &&
((!op_n && val) ||
(op_n < 0 &&
-op_n > val)))
goto out_free;
}
/* there will be no errors left, so we can go ahead */
no_reply = 0;
for (i = 0; i < nsops; i++) {
struct semaphore *s;
int op_n;
s = &sem->sems[sops[i].sem_num];
op_n = sops[i].sem_op;
s->sempid = getnpid(who_e);
if (op_n > 0) {
/* check for alter permission */
r = EACCES;
if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0222))
goto out_free;
s->semval += sops[i].sem_op;
} else if (!op_n) {
if (s->semval) {
/* put the process asleep */
s->semzcnt++;
s->zlist = realloc(s->zlist, sizeof(struct waiting) * s->semzcnt);
if (!s->zlist) {
printf("IPC: zero waiting list lost...\n");
break;
}
s->zlist[s->semzcnt-1].who = who_e;
s->zlist[s->semzcnt-1].val = op_n;
#ifdef DEBUG_SEM
printf("SEMOP: Put into sleep... %d\n", who_e);
#endif
no_reply++;
}
} else {
/* check for alter permission */
r = EACCES;
if (!check_perm(&sem->semid_ds.sem_perm, who_e, 0222))
goto out_free;
if (s->semval >= -op_n)
s->semval += op_n;
else {
/* put the process asleep */
s->semncnt++;
s->nlist = realloc(s->nlist, sizeof(struct waiting) * s->semncnt);
if (!s->nlist) {
printf("IPC: increase waiting list lost...\n");
break;
}
s->nlist[s->semncnt-1].who = who_e;
s->nlist[s->semncnt-1].val = -op_n;
no_reply++;
}
}
}
r = OK;
out_free:
free(sops);
out:
/* if we reach here by errors
* or with no errors but we should reply back.
*/
if (r != OK || !no_reply) {
m->m_type = r;
r = sendnb(who_e, m);
if (r != OK)
printf("IPC send error!\n");
}
/* awaken process if possible */
update_semaphores();
return 0;
}
/*===========================================================================*
* is_sem_nil *
*===========================================================================*/
PUBLIC int is_sem_nil(void)
{
return (sem_list_nr == 0);
}
/*===========================================================================*
* sem_process_vm_notify *
*===========================================================================*/
PUBLIC void sem_process_vm_notify(void)
{
endpoint_t pt;
int r;
while ((r = vm_query_exit(&pt)) >= 0) {
/* for each enpoint 'pt', check whether it's waiting... */
remove_process(pt);
if (r == 0)
break;
}
if (r < 0)
printf("IPC: query exit error!\n");
}

348
servers/ipc/shm.c Normal file
View file

@ -0,0 +1,348 @@
#include "inc.h"
#define MAX_SHM_NR 1024
struct shm_struct {
key_t key;
int id;
struct shmid_ds shmid_ds;
vir_bytes page;
phys_bytes phys;
};
PRIVATE struct shm_struct shm_list[MAX_SHM_NR];
PRIVATE int shm_list_nr = 0;
PRIVATE struct shm_struct *shm_find_key(key_t key)
{
int i;
if (key == IPC_PRIVATE)
return NULL;
for (i = 0; i < shm_list_nr; i++)
if (shm_list[i].key == key)
return shm_list+i;
return NULL;
}
PRIVATE struct shm_struct *shm_find_id(int id)
{
int i;
for (i = 0; i < shm_list_nr; i++)
if (shm_list[i].id == id)
return shm_list+i;
return NULL;
}
/*===========================================================================*
* do_shmget *
*===========================================================================*/
PUBLIC int do_shmget(message *m)
{
struct shm_struct *shm;
long key, size, old_size;
int flag;
int id;
key = m->SHMGET_KEY;
old_size = size = m->SHMGET_SIZE;
flag = m->SHMGET_FLAG;
if ((shm = shm_find_key(key))) {
if (!check_perm(&shm->shmid_ds.shm_perm, who_e, flag))
return EACCES;
if ((flag & IPC_CREAT) && (flag & IPC_EXCL))
return EEXIST;
if (size && shm->shmid_ds.shm_segsz < size)
return EINVAL;
id = shm->id;
} else { /* no key found */
if (!(flag & IPC_CREAT))
return ENOENT;
if (size <= 0)
return EINVAL;
/* round up to a multiple of PAGE_SIZE */
if (size % I386_PAGE_SIZE)
size += I386_PAGE_SIZE - size % I386_PAGE_SIZE;
if (size <= 0)
return EINVAL;
if (shm_list_nr == MAX_SHM_NR)
return ENOMEM;
/* TODO: shmmni should be changed... */
if (identifier == SHMMNI)
return ENOSPC;
shm = &shm_list[shm_list_nr];
memset(shm, 0, sizeof(struct shm_struct));
shm->page = (vir_bytes) mmap(0, size,
PROT_READ|PROT_WRITE,
MAP_CONTIG|MAP_PREALLOC|MAP_ANON|MAP_SHARED,
-1, 0);
if (shm->page == (vir_bytes) MAP_FAILED)
return ENOMEM;
shm->phys = vm_getphys(SELF_E, (void *) shm->page);
memset((void *)shm->page, 0, size);
shm->shmid_ds.shm_perm.cuid =
shm->shmid_ds.shm_perm.uid = getnuid(who_e);
shm->shmid_ds.shm_perm.cgid =
shm->shmid_ds.shm_perm.gid = getngid(who_e);
shm->shmid_ds.shm_perm.mode = flag & 0777;
shm->shmid_ds.shm_segsz = old_size;
shm->shmid_ds.shm_atime = 0;
shm->shmid_ds.shm_dtime = 0;
shm->shmid_ds.shm_ctime = time(NULL);
shm->shmid_ds.shm_cpid = getnpid(who_e);
shm->shmid_ds.shm_lpid = 0;
shm->shmid_ds.shm_nattch = 0;
shm->id = id = identifier++;
shm->key = key;
shm_list_nr++;
}
m->SHMGET_RETID = id;
return OK;
}
/*===========================================================================*
* do_shmat *
*===========================================================================*/
PUBLIC int do_shmat(message *m)
{
int id, flag;
vir_bytes addr;
void *ret;
struct shm_struct *shm;
id = m->SHMAT_ID;
addr = (vir_bytes) m->SHMAT_ADDR;
flag = m->SHMAT_FLAG;
if (addr && (addr % I386_PAGE_SIZE)) {
if (flag & SHM_RND)
addr -= (addr % I386_PAGE_SIZE);
else
return EINVAL;
}
if (!(shm = shm_find_id(id)))
return EINVAL;
if (flag & SHM_RDONLY)
flag = 0444;
else
flag = 0666;
if (!check_perm(&shm->shmid_ds.shm_perm, who_e, flag))
return EACCES;
ret = vm_remap(who_e, SELF_E, (void *)addr, (void *)shm->page,
shm->shmid_ds.shm_segsz);
if (ret == MAP_FAILED)
return ENOMEM;
shm->shmid_ds.shm_atime = time(NULL);
shm->shmid_ds.shm_lpid = getnpid(who_e);
/* nattach is updated lazily */
m->SHMAT_RETADDR = (long) ret;
return OK;
}
/*===========================================================================*
* update_refcount_and_destroy *
*===========================================================================*/
PUBLIC void update_refcount_and_destroy(void)
{
int i, j;
for (i = 0, j = 0; i < shm_list_nr; i++) {
u8_t rc;
rc = vm_getrefcount(SELF_E, (void *) shm_list[i].page);
if (rc == (u8_t) -1) {
printf("IPC: can't find physical region.\n");
continue;
}
shm_list[i].shmid_ds.shm_nattch = rc - 1;
if (shm_list[i].shmid_ds.shm_nattch ||
!(shm_list[i].shmid_ds.shm_perm.mode & SHM_DEST)) {
if (i != j)
shm_list[j] = shm_list[i];
j++;
} else {
int size = shm_list[i].shmid_ds.shm_segsz;
if (size % I386_PAGE_SIZE)
size += I386_PAGE_SIZE - size % I386_PAGE_SIZE;
munmap((void *)shm_list[i].page, size);
}
}
shm_list_nr = j;
}
/*===========================================================================*
* do_shmdt *
*===========================================================================*/
PUBLIC int do_shmdt(message *m)
{
struct shm_struct *shm;
vir_bytes addr;
phys_bytes paddr;
int n, i;
addr = m->SHMDT_ADDR;
if ((paddr = vm_getphys(who_e, (void *) addr)) == 0)
return EINVAL;
for (i = 0; i < shm_list_nr; i++) {
if (shm_list[i].phys == paddr) {
struct shm_struct *shm = &shm_list[i];
shm->shmid_ds.shm_atime = time(NULL);
shm->shmid_ds.shm_lpid = getnpid(who_e);
/* nattch is updated lazily */
vm_unmap(who_e, (void *) addr);
break;
}
}
if (i == shm_list_nr)
fprintf(stderr, "IPC: do_shmdt impossible error!\n");
update_refcount_and_destroy();
return OK;
}
/*===========================================================================*
* do_shmctl *
*===========================================================================*/
PUBLIC int do_shmctl(message *m)
{
int id = m->SHMCTL_ID;
int cmd = m->SHMCTL_CMD;
struct shmid_ds *ds = (struct shmid_ds *)m->SHMCTL_BUF;
struct shmid_ds tmp_ds;
struct shm_struct *shm;
struct shminfo sinfo;
struct shm_info s_info;
uid_t uid;
int r, i;
if (cmd == IPC_STAT)
update_refcount_and_destroy();
if ((cmd == IPC_STAT ||
cmd == IPC_SET ||
cmd == IPC_RMID) &&
!(shm = shm_find_id(id)))
return EINVAL;
switch (cmd) {
case IPC_STAT:
if (!ds)
return EFAULT;
/* check whether it has read permission */
if (!check_perm(&shm->shmid_ds.shm_perm, who_e, 0444))
return EACCES;
r = sys_datacopy(SELF_E, (vir_bytes)&shm->shmid_ds,
who_e, (vir_bytes)ds, sizeof(struct shmid_ds));
if (r != OK)
return EFAULT;
break;
case IPC_SET:
uid = getnuid(who_e);
if (uid != shm->shmid_ds.shm_perm.cuid &&
uid != shm->shmid_ds.shm_perm.uid &&
uid != 0)
return EPERM;
r = sys_datacopy(who_e, (vir_bytes)ds,
SELF_E, (vir_bytes)&tmp_ds, sizeof(struct shmid_ds));
if (r != OK)
return EFAULT;
shm->shmid_ds.shm_perm.uid = tmp_ds.shm_perm.uid;
shm->shmid_ds.shm_perm.gid = tmp_ds.shm_perm.gid;
shm->shmid_ds.shm_perm.mode &= ~0777;
shm->shmid_ds.shm_perm.mode |= tmp_ds.shm_perm.mode & 0666;
shm->shmid_ds.shm_ctime = time(NULL);
break;
case IPC_RMID:
uid = getnuid(who_e);
if (uid != shm->shmid_ds.shm_perm.cuid &&
uid != shm->shmid_ds.shm_perm.uid &&
uid != 0)
return EPERM;
shm->shmid_ds.shm_perm.mode |= SHM_DEST;
/* destroy if possible */
update_refcount_and_destroy();
break;
case IPC_INFO:
if (!ds)
return EFAULT;
sinfo.shmmax = (unsigned long) -1;
sinfo.shmmin = 1;
sinfo.shmmni = MAX_SHM_NR;
sinfo.shmseg = (unsigned long) -1;
sinfo.shmall = (unsigned long) -1;
r = sys_datacopy(SELF_E, (vir_bytes)&sinfo,
who_e, (vir_bytes)ds, sizeof(struct shminfo));
if (r != OK)
return EFAULT;
m->SHMCTL_RET = shm_list_nr - 1;
if (m->SHMCTL_RET < 0)
m->SHMCTL_RET = 0;
break;
case SHM_INFO:
if (!ds)
return EFAULT;
s_info.used_ids = shm_list_nr;
s_info.shm_tot = 0;
for (i = 0; i < shm_list_nr; i++)
s_info.shm_tot +=
shm_list[i].shmid_ds.shm_segsz/I386_PAGE_SIZE;
s_info.shm_rss = s_info.shm_tot;
s_info.shm_swp = 0;
s_info.swap_attempts = 0;
s_info.swap_successes = 0;
r = sys_datacopy(SELF_E, (vir_bytes)&s_info,
who_e, (vir_bytes)ds, sizeof(struct shm_info));
if (r != OK)
return EFAULT;
m->SHMCTL_RET = shm_list_nr - 1;
if (m->SHMCTL_RET < 0)
m->SHMCTL_RET = 0;
break;
case SHM_STAT:
if (id < 0 || id >= shm_list_nr)
return EINVAL;
shm = &shm_list[id];
r = sys_datacopy(SELF_E, (vir_bytes)&shm->shmid_ds,
who_e, (vir_bytes)ds, sizeof(struct shmid_ds));
if (r != OK)
return EFAULT;
m->SHMCTL_RET = shm->id;
break;
default:
return EINVAL;
}
return OK;
}
PUBLIC void list_shm_ds(void)
{
int i;
printf("key\tid\tpage\n");
for (i = 0; i < shm_list_nr; i++)
printf("%d\t%d\t%x\n",
shm_list[i].key,
shm_list[i].id,
shm_list[i].page);
}
/*===========================================================================*
* is_shm_nil *
*===========================================================================*/
PUBLIC int is_shm_nil(void)
{
return (shm_list_nr == 0);
}

35
servers/ipc/utility.c Normal file
View file

@ -0,0 +1,35 @@
#include "inc.h"
PUBLIC int check_perm(struct ipc_perm *req, endpoint_t who, int mode)
{
int req_mode;
int cur_mode;
uid_t uid = getnuid(who);
gid_t gid = getngid(who);
mode &= 0666;
/* is root? */
if (uid == 0)
return 1;
if (uid == req->uid || uid == req->cuid) {
/* same user */
req_mode = (req->mode >> 6) & 0x7;
cur_mode = (mode >> 6) & 0x7;
} else if (gid == req->gid || gid == req->cgid) {
/* same group */
req_mode = (req->mode >> 3) & 0x7;
cur_mode = (mode >> 3) & 0x7;
} else {
/* other group */
req_mode = req->mode & 0x7;
cur_mode = mode & 0x7;
}
if (cur_mode && ((cur_mode & req_mode) == cur_mode))
return 1;
else
return 0;
}