--- /dev/null
+#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");
+}
+
--- /dev/null
+#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);
+}