From 75d3db49118d6497087d23a7b4959fa99142a5e6 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Mon, 21 Sep 2009 14:53:13 +0000 Subject: [PATCH] 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. --- servers/Makefile | 1 + servers/ipc/Makefile | 30 ++ servers/ipc/inc.h | 47 ++++ servers/ipc/main.c | 102 +++++++ servers/ipc/sem.c | 619 ++++++++++++++++++++++++++++++++++++++++++ servers/ipc/shm.c | 348 ++++++++++++++++++++++++ servers/ipc/utility.c | 35 +++ 7 files changed, 1182 insertions(+) create mode 100644 servers/ipc/Makefile create mode 100644 servers/ipc/inc.h create mode 100644 servers/ipc/main.c create mode 100644 servers/ipc/sem.c create mode 100644 servers/ipc/shm.c create mode 100644 servers/ipc/utility.c diff --git a/servers/Makefile b/servers/Makefile index 8316c7405..6b8191ba8 100644 --- a/servers/Makefile +++ b/servers/Makefile @@ -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 index 000000000..390af0394 --- /dev/null +++ b/servers/ipc/Makefile @@ -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 index 000000000..fc15751a3 --- /dev/null +++ b/servers/ipc/inc.h @@ -0,0 +1,47 @@ +#define _SYSTEM 1 +#define _MINIX 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +_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 index 000000000..f72ed7b78 --- /dev/null +++ b/servers/ipc/main.c @@ -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 index 000000000..965b560d5 --- /dev/null +++ b/servers/ipc/sem.c @@ -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 index 000000000..70585f7c8 --- /dev/null +++ b/servers/ipc/shm.c @@ -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 index 000000000..b17a3ee13 --- /dev/null +++ b/servers/ipc/utility.c @@ -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; +} + -- 2.44.0