static int put_shmdata(int id) { struct shm_data *data; int ret = -1; SYSV_MUTEX_LOCK(&lock_resources); data = _hash_lookup(shmres, id); if (!data) { sysv_print_err("something wrong put_shmdata\n"); goto done; /* It should not reach here. */ } data->used--; if (data->used == 0 && data->removed) { sysv_print("really remove the sem\n"); SYSV_MUTEX_UNLOCK(&lock_resources); /* OBS: Even if the shmctl fails (the thread doesn't * have IPC_M permissions), all structures associated * with it will be removed in the current process.*/ sysvipc_shmdt(data->internal); semundo_clear(id, -1); if (data->removed == SEG_ALREADY_REMOVED) return 1; /* The semaphore was removed by another process so there is nothing else we must do. */ /* Else inform the daemon that the segment is removed. */ return (sysvipc_shmctl(id, IPC_RMID, NULL)); } ret = 0; done: SYSV_MUTEX_UNLOCK(&lock_resources); return (ret); }
int semctl1(struct lwp *l, int semid, int semnum, int cmd, void *v, register_t *retval) { kauth_cred_t cred = l->l_cred; union __semun *arg = v; struct semid_ds *sembuf = v, *semaptr; int i, error, ix; SEM_PRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, v)); mutex_enter(&semlock); ix = IPCID_TO_IX(semid); if (ix < 0 || ix >= seminfo.semmni) { mutex_exit(&semlock); return (EINVAL); } semaptr = &sema[ix]; if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || semaptr->sem_perm._seq != IPCID_TO_SEQ(semid)) { mutex_exit(&semlock); return (EINVAL); } switch (cmd) { case IPC_RMID: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0) break; semaptr->sem_perm.cuid = kauth_cred_geteuid(cred); semaptr->sem_perm.uid = kauth_cred_geteuid(cred); semtot -= semaptr->sem_nsems; for (i = semaptr->_sem_base - sem; i < semtot; i++) sem[i] = sem[i + semaptr->sem_nsems]; for (i = 0; i < seminfo.semmni; i++) { if ((sema[i].sem_perm.mode & SEM_ALLOC) && sema[i]._sem_base > semaptr->_sem_base) sema[i]._sem_base -= semaptr->sem_nsems; } semaptr->sem_perm.mode = 0; semundo_clear(ix, -1); cv_broadcast(&semcv[ix]); break; case IPC_SET: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M))) break; KASSERT(sembuf != NULL); semaptr->sem_perm.uid = sembuf->sem_perm.uid; semaptr->sem_perm.gid = sembuf->sem_perm.gid; semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | (sembuf->sem_perm.mode & 0777); semaptr->sem_ctime = time_second; break; case IPC_STAT: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; KASSERT(sembuf != NULL); memcpy(sembuf, semaptr, sizeof(struct semid_ds)); sembuf->sem_perm.mode &= 0777; break; case GETNCNT: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; if (semnum < 0 || semnum >= semaptr->sem_nsems) { error = EINVAL; break; } *retval = semaptr->_sem_base[semnum].semncnt; break; case GETPID: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; if (semnum < 0 || semnum >= semaptr->sem_nsems) { error = EINVAL; break; } *retval = semaptr->_sem_base[semnum].sempid; break; case GETVAL: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; if (semnum < 0 || semnum >= semaptr->sem_nsems) { error = EINVAL; break; } *retval = semaptr->_sem_base[semnum].semval; break; case GETALL: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; KASSERT(arg != NULL); for (i = 0; i < semaptr->sem_nsems; i++) { error = copyout(&semaptr->_sem_base[i].semval, &arg->array[i], sizeof(arg->array[i])); if (error != 0) break; } break; case GETZCNT: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R))) break; if (semnum < 0 || semnum >= semaptr->sem_nsems) { error = EINVAL; break; } *retval = semaptr->_sem_base[semnum].semzcnt; break; case SETVAL: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) break; if (semnum < 0 || semnum >= semaptr->sem_nsems) { error = EINVAL; break; } KASSERT(arg != NULL); if ((unsigned int)arg->val > seminfo.semvmx) { error = ERANGE; break; } semaptr->_sem_base[semnum].semval = arg->val; semundo_clear(ix, semnum); cv_broadcast(&semcv[ix]); break; case SETALL: if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) break; KASSERT(arg != NULL); for (i = 0; i < semaptr->sem_nsems; i++) { unsigned short semval; error = copyin(&arg->array[i], &semval, sizeof(arg->array[i])); if (error != 0) break; if ((unsigned int)semval > seminfo.semvmx) { error = ERANGE; break; } semaptr->_sem_base[i].semval = semval; } semundo_clear(ix, -1); cv_broadcast(&semcv[ix]); break; default: error = EINVAL; break; } mutex_exit(&semlock); return (error); }
int sysvipc___semctl(int semid, int semnum , int cmd, union semun *arg) { int i, error; struct semid_pool *semaptr = NULL; struct sem *semptr = NULL; struct shmid_ds shmds; int shm_access = 0; /*if (!jail_sysvipc_allowed && cred->cr_prison != NULL) return (ENOSYS); */ sysv_print("semctl cmd = %d\n", cmd); error = 0; switch (cmd) { case IPC_SET: /* Originally was IPC_M but this is checked by daemon. */ case SETVAL: case SETALL: shm_access = IPC_W; break; case IPC_STAT: case GETNCNT: case GETPID: case GETVAL: case GETALL: case GETZCNT: shm_access = IPC_R; break; default: break; } semaptr = get_semaptr(semid, cmd==IPC_RMID, shm_access); if (!semaptr) { /* errno already set. */ return (-1); } switch (cmd) { case IPC_RMID: /* Mark that the segment is removed. This is done in * get_semaptr call in order to announce other processes. * It will be actually removed after put_shmdata call and * not other thread from this address space use shm_data * structure. */ break; case IPC_SET: if (!arg->buf) { error = EFAULT; break; } memset(&shmds, 0, sizeof(shmds)/sizeof(unsigned char)); memcpy(&shmds.shm_perm, &arg->buf->sem_perm, sizeof(struct ipc_perm)); error = sysvipc_shmctl(semid, cmd, &shmds); /* OBS: didn't update ctime and mode as in kernel implementation * it is done. Those fields are already updated for shmid_ds * struct when calling shmctl */ break; case IPC_STAT: if (!arg->buf) { error = EFAULT; break; } error = sysvipc_shmctl(semid, cmd, &shmds); if (error) break; memcpy(&arg->buf->sem_perm, &shmds.shm_perm, sizeof(struct ipc_perm)); arg->buf->sem_nsems = (shmds.shm_segsz - sizeof(struct semid_pool)) / sizeof(struct sem); arg->buf->sem_ctime = shmds.shm_ctime; /* otime is semaphore specific so read it from * semaptr */ error = try_rwlock_rdlock(semid, semaptr); if (error) break; arg->buf->sem_otime = semaptr->ds.sem_otime; rwlock_unlock(semid, semaptr); break; case GETNCNT: if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) { errno = EINVAL; break; } error = try_rwlock_rdlock(semid, semaptr); if (error) break; error = semaptr->ds.sem_base[semnum].semncnt; rwlock_unlock(semid, semaptr); break; case GETPID: if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) { errno = EINVAL; break; } error = try_rwlock_rdlock(semid, semaptr); if (error) break; error = semaptr->ds.sem_base[semnum].sempid; rwlock_unlock(semid, semaptr); break; case GETVAL: if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) { errno = EINVAL; break; } error = try_rwlock_rdlock(semid, semaptr); if (error) break; error = semaptr->ds.sem_base[semnum].semval; rwlock_unlock(semid, semaptr); break; case GETALL: if (!arg->array) { error = EFAULT; break; } error = try_rwlock_rdlock(semid, semaptr); if (error) break; for (i = 0; i < semaptr->ds.sem_nsems; i++) { arg->array[i] = semaptr->ds.sem_base[i].semval; } rwlock_unlock(semid, semaptr); break; case GETZCNT: if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) { errno = EINVAL; break; } error = try_rwlock_rdlock(semid, semaptr); if (error) break; error = semaptr->ds.sem_base[semnum].semzcnt; rwlock_unlock(semid, semaptr); break; case SETVAL: if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) { errno = EINVAL; break; } error = try_rwlock_wrlock(semid, semaptr); if (error) break; semptr = &semaptr->ds.sem_base[semnum]; semptr->semval = arg->val; semundo_clear(semid, semnum); if (semptr->semzcnt || semptr->semncnt) umtx_wakeup((int *)&semptr->semval, 0); rwlock_unlock(semid, semaptr); break; case SETALL: if (!arg->array) { error = EFAULT; break; } error = try_rwlock_wrlock(semid, semaptr); if (error) break; for (i = 0; i < semaptr->ds.sem_nsems; i++) { semptr = &semaptr->ds.sem_base[i]; semptr->semval = arg->array[i]; if (semptr->semzcnt || semptr->semncnt) umtx_wakeup((int *)&semptr->semval, 0); } semundo_clear(semid, -1); rwlock_unlock(semid, semaptr); break; default: errno = EINVAL; break; } put_shmdata(semid); sysv_print("end semctl\n"); return (error); }