]> Zhao Yanbai Git Server - minix.git/commitdiff
new ipc server that implements shared memory and semaphores.
authorBen Gras <ben@minix3.org>
Mon, 21 Sep 2009 14:53:13 +0000 (14:53 +0000)
committerBen Gras <ben@minix3.org>
Mon, 21 Sep 2009 14:53:13 +0000 (14:53 +0000)
this server, tests, vm support, library stubs and other contributions
are the work of Guanqun Lu, a 2009 GSOC student.

servers/Makefile
servers/ipc/Makefile [new file with mode: 0644]
servers/ipc/inc.h [new file with mode: 0644]
servers/ipc/main.c [new file with mode: 0644]
servers/ipc/sem.c [new file with mode: 0644]
servers/ipc/shm.c [new file with mode: 0644]
servers/ipc/utility.c [new file with mode: 0644]

index 8316c740541f6413064b89071d9e67349f59e367..6b8191ba8528e5789947e5bc621f867b7670e5d7 100644 (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
diff --git a/servers/ipc/Makefile b/servers/ipc/Makefile
new file mode 100644 (file)
index 0000000..390af03
--- /dev/null
@@ -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
+
diff --git a/servers/ipc/inc.h b/servers/ipc/inc.h
new file mode 100644 (file)
index 0000000..fc15751
--- /dev/null
@@ -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;
+
diff --git a/servers/ipc/main.c b/servers/ipc/main.c
new file mode 100644 (file)
index 0000000..f72ed7b
--- /dev/null
@@ -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;
+}
+
diff --git a/servers/ipc/sem.c b/servers/ipc/sem.c
new file mode 100644 (file)
index 0000000..965b560
--- /dev/null
@@ -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");
+}
+
diff --git a/servers/ipc/shm.c b/servers/ipc/shm.c
new file mode 100644 (file)
index 0000000..70585f7
--- /dev/null
@@ -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);
+}
diff --git a/servers/ipc/utility.c b/servers/ipc/utility.c
new file mode 100644 (file)
index 0000000..b17a3ee
--- /dev/null
@@ -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;
+}
+