Example #1
0
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);
}
Example #2
0
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);
}
Example #3
0
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);
}