615 lines
14 KiB
C
615 lines
14 KiB
C
#define __USE_MISC
|
|
|
|
#include <minix/vm.h>
|
|
|
|
#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;
|
|
|
|
m.m_type = ret;
|
|
sendnb(who, &m);
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
#if 0
|
|
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");
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
|
|
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))) {
|
|
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 = 0;
|
|
|
|
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 */
|
|
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;
|
|
|
|
sendnb(who_e, m);
|
|
}
|
|
|
|
/* 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");
|
|
}
|
|
|