1 /*	$OpenBSD: sysv_sem.c,v 1.32 2004/07/15 11:24:46 millert Exp $	*/
2 /*	$NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 2002,2003 Todd C. Miller <Todd.Miller@courtesan.com>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  *
19  * Sponsored in part by the Defense Advanced Research Projects
20  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22  */
23 /*
24  * Implementation of SVID semaphores
25  *
26  * Author:  Daniel Boulet
27  *
28  * This software is provided ``AS IS'' without any warranties of any kind.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/proc.h>
35 #include <sys/sem.h>
36 #include <sys/sysctl.h>
37 #include <sys/malloc.h>
38 #include <sys/pool.h>
39 
40 #include <sys/mount.h>
41 #include <sys/syscallargs.h>
42 
43 /* SVID defines EIDRM but BSD does not */
44 #ifndef EIDRM
45 #define EIDRM	EINVAL
46 #endif
47 
48 #ifdef SEM_DEBUG
49 #define DPRINTF(x)	printf x
50 #else
51 #define DPRINTF(x)
52 #endif
53 
54 int	semtot = 0;
55 int	semutot = 0;
56 struct	semid_ds **sema;	/* semaphore id list */
57 SLIST_HEAD(, sem_undo) semu_list; /* list of undo structures */
58 struct	pool sema_pool;		/* pool for struct semid_ds */
59 struct	pool semu_pool;		/* pool for struct sem_undo (SEMUSZ) */
60 unsigned short *semseqs;	/* array of sem sequence numbers */
61 
62 struct sem_undo *semu_alloc(struct proc *);
63 int semundo_adjust(struct proc *, struct sem_undo **, int, int, int);
64 void semundo_clear(int, int);
65 
66 void
seminit(void)67 seminit(void)
68 {
69 
70 	pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, 0, "semapl",
71 	    &pool_allocator_nointr);
72 	pool_init(&semu_pool, SEMUSZ, 0, 0, 0, "semupl",
73 	    &pool_allocator_nointr);
74 	sema = malloc(seminfo.semmni * sizeof(struct semid_ds *),
75 	    M_SEM, M_WAITOK);
76 	bzero(sema, seminfo.semmni * sizeof(struct semid_ds *));
77 	semseqs = malloc(seminfo.semmni * sizeof(unsigned short),
78 	    M_SEM, M_WAITOK);
79 	bzero(semseqs, seminfo.semmni * sizeof(unsigned short));
80 	SLIST_INIT(&semu_list);
81 }
82 
83 /*
84  * Allocate a new sem_undo structure for a process
85  * (returns ptr to structure or NULL if no more room)
86  */
87 struct sem_undo *
semu_alloc(struct proc * p)88 semu_alloc(struct proc *p)
89 {
90 	struct sem_undo *suptr, *sutmp;
91 
92 	if (semutot == seminfo.semmnu)
93 		return (NULL);		/* no space */
94 
95 	/*
96 	 * Allocate a semu w/o waiting if possible.
97 	 * If we do have to wait, we must check to verify that a semu
98 	 * with un_proc == p has not been allocated in the meantime.
99 	 */
100 	semutot++;
101 	if ((suptr = pool_get(&semu_pool, 0)) == NULL) {
102 		sutmp = pool_get(&semu_pool, PR_WAITOK);
103 		SLIST_FOREACH(suptr, &semu_list, un_next) {
104 			if (suptr->un_proc == p) {
105 				pool_put(&semu_pool, sutmp);
106 				semutot--;
107 				return (suptr);
108 			}
109 		}
110 		suptr = sutmp;
111 	}
112 	suptr->un_cnt = 0;
113 	suptr->un_proc = p;
114 	SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
115 	return (suptr);
116 }
117 
118 /*
119  * Adjust a particular entry for a particular proc
120  */
121 int
semundo_adjust(struct proc * p,struct sem_undo ** supptr,int semid,int semnum,int adjval)122 semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
123 	int adjval)
124 {
125 	struct sem_undo *suptr;
126 	struct undo *sunptr;
127 	int i;
128 
129 	/*
130 	 * Look for and remember the sem_undo if the caller doesn't provide it.
131 	 */
132 	suptr = *supptr;
133 	if (suptr == NULL) {
134 		SLIST_FOREACH(suptr, &semu_list, un_next) {
135 			if (suptr->un_proc == p) {
136 				*supptr = suptr;
137 				break;
138 			}
139 		}
140 		if (suptr == NULL) {
141 			if (adjval == 0)
142 				return (0);
143 			suptr = semu_alloc(p);
144 			if (suptr == NULL)
145 				return (ENOSPC);
146 			*supptr = suptr;
147 		}
148 	}
149 
150 	/*
151 	 * Look for the requested entry and adjust it
152 	 * (delete if adjval becomes 0).
153 	 */
154 	sunptr = &suptr->un_ent[0];
155 	for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
156 		if (sunptr->un_id != semid || sunptr->un_num != semnum)
157 			continue;
158 		if (adjval == 0)
159 			sunptr->un_adjval = 0;
160 		else
161 			sunptr->un_adjval += adjval;
162 		if (sunptr->un_adjval != 0)
163 			return (0);
164 
165 		if (--suptr->un_cnt == 0) {
166 			SLIST_REMOVE(&semu_list, suptr, sem_undo, un_next);
167 			pool_put(&semu_pool, suptr);
168 			semutot--;
169 		} else if (i < suptr->un_cnt)
170 			suptr->un_ent[i] =
171 			    suptr->un_ent[suptr->un_cnt];
172 		return (0);
173 	}
174 
175 	/* Didn't find the right entry - create it */
176 	if (adjval == 0)
177 		return (0);
178 	if (suptr->un_cnt == SEMUME)
179 		return (EINVAL);
180 
181 	sunptr = &suptr->un_ent[suptr->un_cnt];
182 	suptr->un_cnt++;
183 	sunptr->un_adjval = adjval;
184 	sunptr->un_id = semid;
185 	sunptr->un_num = semnum;
186 	return (0);
187 }
188 
189 void
semundo_clear(int semid,int semnum)190 semundo_clear(int semid, int semnum)
191 {
192 	struct sem_undo *suptr = SLIST_FIRST(&semu_list);
193 	struct sem_undo *suprev = SLIST_END(&semu_list);
194 	struct undo *sunptr;
195 	int i;
196 
197 	while (suptr != SLIST_END(&semu_list)) {
198 		sunptr = &suptr->un_ent[0];
199 		for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
200 			if (sunptr->un_id == semid) {
201 				if (semnum == -1 || sunptr->un_num == semnum) {
202 					suptr->un_cnt--;
203 					if (i < suptr->un_cnt) {
204 						suptr->un_ent[i] =
205 						  suptr->un_ent[suptr->un_cnt];
206 						i--, sunptr--;
207 					}
208 				}
209 				if (semnum != -1)
210 					break;
211 			}
212 		}
213 		if (suptr->un_cnt == 0) {
214 			struct sem_undo *sutmp = suptr;
215 
216 			if (suptr == SLIST_FIRST(&semu_list))
217 				SLIST_REMOVE_HEAD(&semu_list, un_next);
218 			else
219 				SLIST_REMOVE_NEXT(&semu_list, suprev, un_next);
220 			suptr = SLIST_NEXT(suptr, un_next);
221 			pool_put(&semu_pool, sutmp);
222 			semutot--;
223 		} else {
224 			suprev = suptr;
225 			suptr = SLIST_NEXT(suptr, un_next);
226 		}
227 	}
228 }
229 
230 int
sys___semctl(struct proc * p,void * v,register_t * retval)231 sys___semctl(struct proc *p, void *v, register_t *retval)
232 {
233 	struct sys___semctl_args /* {
234 		syscallarg(int) semid;
235 		syscallarg(int) semnum;
236 		syscallarg(int) cmd;
237 		syscallarg(union semun *) arg;
238 	} */ *uap = v;
239 	union semun arg;
240 	int error = 0, cmd = SCARG(uap, cmd);
241 
242 	switch (cmd) {
243 	case IPC_SET:
244 	case IPC_STAT:
245 	case GETALL:
246 	case SETVAL:
247 	case SETALL:
248 		error = copyin(SCARG(uap, arg), &arg, sizeof(arg));
249 		break;
250 	}
251 	if (error == 0) {
252 		error = semctl1(p, SCARG(uap, semid), SCARG(uap, semnum),
253 		    cmd, &arg, retval, copyin, copyout);
254 	}
255 	return (error);
256 }
257 
258 int
semctl1(struct proc * p,int semid,int semnum,int cmd,union semun * arg,register_t * retval,int (* ds_copyin)(const void *,void *,size_t),int (* ds_copyout)(const void *,void *,size_t))259 semctl1(struct proc *p, int semid, int semnum, int cmd, union semun *arg,
260     register_t *retval, int (*ds_copyin)(const void *, void *, size_t),
261     int (*ds_copyout)(const void *, void *, size_t))
262 {
263 	struct ucred *cred = p->p_ucred;
264 	int i, ix, error = 0;
265 	struct semid_ds sbuf;
266 	struct semid_ds *semaptr;
267 
268 	DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg));
269 
270 	ix = IPCID_TO_IX(semid);
271 	if (ix < 0 || ix >= seminfo.semmni)
272 		return (EINVAL);
273 
274 	if ((semaptr = sema[ix]) == NULL ||
275 	    semaptr->sem_perm.seq != IPCID_TO_SEQ(semid))
276 		return (EINVAL);
277 
278 	switch (cmd) {
279 	case IPC_RMID:
280 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0)
281 			return (error);
282 		semaptr->sem_perm.cuid = cred->cr_uid;
283 		semaptr->sem_perm.uid = cred->cr_uid;
284 		semtot -= semaptr->sem_nsems;
285 		free(semaptr->sem_base, M_SEM);
286 		pool_put(&sema_pool, semaptr);
287 		sema[ix] = NULL;
288 		semundo_clear(ix, -1);
289 		wakeup(&sema[ix]);
290 		break;
291 
292 	case IPC_SET:
293 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
294 			return (error);
295 		if ((error = ds_copyin(arg->buf, &sbuf, sizeof(sbuf))) != 0)
296 			return (error);
297 		semaptr->sem_perm.uid = sbuf.sem_perm.uid;
298 		semaptr->sem_perm.gid = sbuf.sem_perm.gid;
299 		semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
300 		    (sbuf.sem_perm.mode & 0777);
301 		semaptr->sem_ctime = time.tv_sec;
302 		break;
303 
304 	case IPC_STAT:
305 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
306 			return (error);
307 		error = ds_copyout(semaptr, arg->buf, sizeof(struct semid_ds));
308 		break;
309 
310 	case GETNCNT:
311 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
312 			return (error);
313 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
314 			return (EINVAL);
315 		*retval = semaptr->sem_base[semnum].semncnt;
316 		break;
317 
318 	case GETPID:
319 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
320 			return (error);
321 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
322 			return (EINVAL);
323 		*retval = semaptr->sem_base[semnum].sempid;
324 		break;
325 
326 	case GETVAL:
327 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
328 			return (error);
329 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
330 			return (EINVAL);
331 		*retval = semaptr->sem_base[semnum].semval;
332 		break;
333 
334 	case GETALL:
335 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
336 			return (error);
337 		for (i = 0; i < semaptr->sem_nsems; i++) {
338 			error = ds_copyout(&semaptr->sem_base[i].semval,
339 			    &arg->array[i], sizeof(arg->array[0]));
340 			if (error != 0)
341 				break;
342 		}
343 		break;
344 
345 	case GETZCNT:
346 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
347 			return (error);
348 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
349 			return (EINVAL);
350 		*retval = semaptr->sem_base[semnum].semzcnt;
351 		break;
352 
353 	case SETVAL:
354 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
355 			return (error);
356 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
357 			return (EINVAL);
358 		semaptr->sem_base[semnum].semval = arg->val;
359 		semundo_clear(ix, semnum);
360 		wakeup(&sema[ix]);
361 		break;
362 
363 	case SETALL:
364 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
365 			return (error);
366 		for (i = 0; i < semaptr->sem_nsems; i++) {
367 			error = ds_copyin(&arg->array[i],
368 			    &semaptr->sem_base[i].semval,
369 			    sizeof(arg->array[0]));
370 			if (error != 0)
371 				break;
372 		}
373 		semundo_clear(ix, -1);
374 		wakeup(&sema[ix]);
375 		break;
376 
377 	default:
378 		return (EINVAL);
379 	}
380 
381 	return (error);
382 }
383 
384 int
sys_semget(struct proc * p,void * v,register_t * retval)385 sys_semget(struct proc *p, void *v, register_t *retval)
386 {
387 	struct sys_semget_args /* {
388 		syscallarg(key_t) key;
389 		syscallarg(int) nsems;
390 		syscallarg(int) semflg;
391 	} */ *uap = v;
392 	int semid, error;
393 	int key = SCARG(uap, key);
394 	int nsems = SCARG(uap, nsems);
395 	int semflg = SCARG(uap, semflg);
396 	struct semid_ds *semaptr, *semaptr_new = NULL;
397 	struct ucred *cred = p->p_ucred;
398 
399 	DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
400 
401 	/*
402 	 * Preallocate space for the new semaphore.  If we are going
403 	 * to sleep, we want to sleep now to eliminate any race
404 	 * condition in allocating a semaphore with a specific key.
405 	 */
406 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
407 		if (nsems <= 0 || nsems > seminfo.semmsl) {
408 			DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
409 			    seminfo.semmsl));
410 			return (EINVAL);
411 		}
412 		if (nsems > seminfo.semmns - semtot) {
413 			DPRINTF(("not enough semaphores left (need %d, got %d)\n",
414 			    nsems, seminfo.semmns - semtot));
415 			return (ENOSPC);
416 		}
417 		semaptr_new = pool_get(&sema_pool, PR_WAITOK);
418 		semaptr_new->sem_base = malloc(nsems * sizeof(struct sem),
419 		    M_SEM, M_WAITOK);
420 		bzero(semaptr_new->sem_base, nsems * sizeof(struct sem));
421 	}
422 
423 	if (key != IPC_PRIVATE) {
424 		for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) {
425 			if ((semaptr = sema[semid]) != NULL &&
426 			    semaptr->sem_perm.key == key) {
427 				DPRINTF(("found public key\n"));
428 				if ((error = ipcperm(cred, &semaptr->sem_perm,
429 				    semflg & 0700)))
430 					goto error;
431 				if (nsems > 0 && semaptr->sem_nsems < nsems) {
432 					DPRINTF(("too small\n"));
433 					error = EINVAL;
434 					goto error;
435 				}
436 				if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
437 					DPRINTF(("not exclusive\n"));
438 					error = EEXIST;
439 					goto error;
440 				}
441 				goto found;
442 			}
443 		}
444 	}
445 
446 	DPRINTF(("need to allocate the semid_ds\n"));
447 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
448 		for (semid = 0; semid < seminfo.semmni; semid++) {
449 			if ((semaptr = sema[semid]) == NULL)
450 				break;
451 		}
452 		if (semid == seminfo.semmni) {
453 			DPRINTF(("no more semid_ds's available\n"));
454 			error = ENOSPC;
455 			goto error;
456 		}
457 		DPRINTF(("semid %d is available\n", semid));
458 		semaptr_new->sem_perm.key = key;
459 		semaptr_new->sem_perm.cuid = cred->cr_uid;
460 		semaptr_new->sem_perm.uid = cred->cr_uid;
461 		semaptr_new->sem_perm.cgid = cred->cr_gid;
462 		semaptr_new->sem_perm.gid = cred->cr_gid;
463 		semaptr_new->sem_perm.mode = (semflg & 0777);
464 		semaptr_new->sem_perm.seq = semseqs[semid] =
465 		    (semseqs[semid] + 1) & 0x7fff;
466 		semaptr_new->sem_nsems = nsems;
467 		semaptr_new->sem_otime = 0;
468 		semaptr_new->sem_ctime = time.tv_sec;
469 		sema[semid] = semaptr_new;
470 		semtot += nsems;
471 	} else {
472 		DPRINTF(("didn't find it and wasn't asked to create it\n"));
473 		return (ENOENT);
474 	}
475 
476 found:
477 	*retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm);
478 	return (0);
479 error:
480 	if (semaptr_new != NULL) {
481 		free(semaptr_new->sem_base, M_SEM);
482 		pool_put(&sema_pool, semaptr_new);
483 	}
484 	return (error);
485 }
486 
487 int
sys_semop(struct proc * p,void * v,register_t * retval)488 sys_semop(struct proc *p, void *v, register_t *retval)
489 {
490 	struct sys_semop_args /* {
491 		syscallarg(int) semid;
492 		syscallarg(struct sembuf *) sops;
493 		syscallarg(size_t) nsops;
494 	} */ *uap = v;
495 #define	NSOPS	8
496 	struct sembuf sopbuf[NSOPS];
497 	int semid = SCARG(uap, semid);
498 	size_t nsops = SCARG(uap, nsops);
499 	struct sembuf *sops;
500 	struct semid_ds *semaptr;
501 	struct sembuf *sopptr = NULL;
502 	struct sem *semptr = NULL;
503 	struct sem_undo *suptr = NULL;
504 	struct ucred *cred = p->p_ucred;
505 	size_t i, j;
506 	int do_wakeup, do_undos, error;
507 
508 	DPRINTF(("call to semop(%d, %p, %lu)\n", semid, SCARG(uap, sops),
509 	    (u_long)nsops));
510 
511 	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
512 
513 	if (semid < 0 || semid >= seminfo.semmni)
514 		return (EINVAL);
515 
516 	if ((semaptr = sema[semid]) == NULL ||
517 	    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
518 		return (EINVAL);
519 
520 	if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
521 		DPRINTF(("error = %d from ipaccess\n", error));
522 		return (error);
523 	}
524 
525 	if (nsops == 0) {
526 		*retval = 0;
527 		return (0);
528 	} else if (nsops > (size_t)seminfo.semopm) {
529 		DPRINTF(("too many sops (max=%d, nsops=%lu)\n", seminfo.semopm,
530 		    (u_long)nsops));
531 		return (E2BIG);
532 	}
533 
534 	if (nsops <= NSOPS)
535 		sops = sopbuf;
536 	else
537 		sops = malloc(nsops * sizeof(struct sembuf), M_SEM, M_WAITOK);
538 	error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
539 	if (error != 0) {
540 		DPRINTF(("error = %d from copyin(%p, %p, %u)\n", error,
541 		    SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
542 		goto done2;
543 	}
544 
545 	/*
546 	 * Loop trying to satisfy the vector of requests.
547 	 * If we reach a point where we must wait, any requests already
548 	 * performed are rolled back and we go to sleep until some other
549 	 * process wakes us up.  At this point, we start all over again.
550 	 *
551 	 * This ensures that from the perspective of other tasks, a set
552 	 * of requests is atomic (never partially satisfied).
553 	 */
554 	do_undos = 0;
555 
556 	for (;;) {
557 		do_wakeup = 0;
558 
559 		for (i = 0; i < nsops; i++) {
560 			sopptr = &sops[i];
561 
562 			if (sopptr->sem_num >= semaptr->sem_nsems) {
563 				error = EFBIG;
564 				goto done2;
565 			}
566 
567 			semptr = &semaptr->sem_base[sopptr->sem_num];
568 
569 			DPRINTF(("semop:  semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
570 			    semaptr, semaptr->sem_base, semptr,
571 			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
572 			    (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"));
573 
574 			if (sopptr->sem_op < 0) {
575 				if ((int)(semptr->semval +
576 					  sopptr->sem_op) < 0) {
577 					DPRINTF(("semop:  can't do it now\n"));
578 					break;
579 				} else {
580 					semptr->semval += sopptr->sem_op;
581 					if (semptr->semval == 0 &&
582 					    semptr->semzcnt > 0)
583 						do_wakeup = 1;
584 				}
585 				if (sopptr->sem_flg & SEM_UNDO)
586 					do_undos = 1;
587 			} else if (sopptr->sem_op == 0) {
588 				if (semptr->semval > 0) {
589 					DPRINTF(("semop:  not zero now\n"));
590 					break;
591 				}
592 			} else {
593 				if (semptr->semncnt > 0)
594 					do_wakeup = 1;
595 				semptr->semval += sopptr->sem_op;
596 				if (sopptr->sem_flg & SEM_UNDO)
597 					do_undos = 1;
598 			}
599 		}
600 
601 		/*
602 		 * Did we get through the entire vector?
603 		 */
604 		if (i >= nsops)
605 			goto done;
606 
607 		/*
608 		 * No ... rollback anything that we've already done
609 		 */
610 		DPRINTF(("semop:  rollback 0 through %d\n", i - 1));
611 		for (j = 0; j < i; j++)
612 			semaptr->sem_base[sops[j].sem_num].semval -=
613 			    sops[j].sem_op;
614 
615 		/*
616 		 * If the request that we couldn't satisfy has the
617 		 * NOWAIT flag set then return with EAGAIN.
618 		 */
619 		if (sopptr->sem_flg & IPC_NOWAIT) {
620 			error = EAGAIN;
621 			goto done2;
622 		}
623 
624 		if (sopptr->sem_op == 0)
625 			semptr->semzcnt++;
626 		else
627 			semptr->semncnt++;
628 
629 		DPRINTF(("semop:  good night!\n"));
630 		error = tsleep(&sema[semid], PLOCK | PCATCH,
631 		    "semwait", 0);
632 		DPRINTF(("semop:  good morning (error=%d)!\n", error));
633 
634 		suptr = NULL;	/* sem_undo may have been reallocated */
635 
636 		/*
637 		 * Make sure that the semaphore still exists
638 		 */
639 		if (sema[semid] == NULL ||
640 		    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
641 			error = EIDRM;
642 			goto done2;
643 		}
644 
645 		/*
646 		 * The semaphore is still alive.  Readjust the count of
647 		 * waiting processes.
648 		 */
649 		if (sopptr->sem_op == 0)
650 			semptr->semzcnt--;
651 		else
652 			semptr->semncnt--;
653 
654 		/*
655 		 * Is it really morning, or was our sleep interrupted?
656 		 * (Delayed check of tsleep() return code because we
657 		 * need to decrement sem[nz]cnt either way.)
658 		 */
659 		if (error != 0) {
660 			error = EINTR;
661 			goto done2;
662 		}
663 		DPRINTF(("semop:  good morning!\n"));
664 	}
665 
666 done:
667 	/*
668 	 * Process any SEM_UNDO requests.
669 	 */
670 	if (do_undos) {
671 		for (i = 0; i < nsops; i++) {
672 			/*
673 			 * We only need to deal with SEM_UNDO's for non-zero
674 			 * op's.
675 			 */
676 			int adjval;
677 
678 			if ((sops[i].sem_flg & SEM_UNDO) == 0)
679 				continue;
680 			adjval = sops[i].sem_op;
681 			if (adjval == 0)
682 				continue;
683 			error = semundo_adjust(p, &suptr, semid,
684 			    sops[i].sem_num, -adjval);
685 			if (error == 0)
686 				continue;
687 
688 			/*
689 			 * Uh-Oh!  We ran out of either sem_undo's or undo's.
690 			 * Rollback the adjustments to this point and then
691 			 * rollback the semaphore ups and down so we can return
692 			 * with an error with all structures restored.  We
693 			 * rollback the undo's in the exact reverse order that
694 			 * we applied them.  This guarantees that we won't run
695 			 * out of space as we roll things back out.
696 			 */
697 			if (i != 0) {
698 				for (j = i - 1; j >= 0; j--) {
699 					if ((sops[j].sem_flg & SEM_UNDO) == 0)
700 						continue;
701 					adjval = sops[j].sem_op;
702 					if (adjval == 0)
703 						continue;
704 					if (semundo_adjust(p, &suptr, semid,
705 					    sops[j].sem_num, adjval) != 0)
706 						panic("semop - can't undo undos");
707 				}
708 			}
709 
710 			for (j = 0; j < nsops; j++)
711 				semaptr->sem_base[sops[j].sem_num].semval -=
712 				    sops[j].sem_op;
713 
714 			DPRINTF(("error = %d from semundo_adjust\n", error));
715 			goto done2;
716 		} /* loop through the sops */
717 	} /* if (do_undos) */
718 
719 	/* We're definitely done - set the sempid's */
720 	for (i = 0; i < nsops; i++) {
721 		sopptr = &sops[i];
722 		semptr = &semaptr->sem_base[sopptr->sem_num];
723 		semptr->sempid = p->p_pid;
724 	}
725 
726 	/* Do a wakeup if any semaphore was up'd. */
727 	if (do_wakeup) {
728 		DPRINTF(("semop:  doing wakeup\n"));
729 		wakeup(&sema[semid]);
730 		DPRINTF(("semop:  back from wakeup\n"));
731 	}
732 	DPRINTF(("semop:  done\n"));
733 	*retval = 0;
734 done2:
735 	if (sops != sopbuf)
736 		free(sops, M_SEM);
737 	return (error);
738 }
739 
740 /*
741  * Go through the undo structures for this process and apply the adjustments to
742  * semaphores.
743  */
744 void
semexit(struct proc * p)745 semexit(struct proc *p)
746 {
747 	struct sem_undo *suptr;
748 	struct sem_undo **supptr;
749 
750 	/*
751 	 * Go through the chain of undo vectors looking for one associated with
752 	 * this process.
753 	 */
754 	SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, un_next) {
755 		if (suptr->un_proc == p)
756 			break;
757 	}
758 
759 	/*
760 	 * If there is no undo vector, skip to the end.
761 	 */
762 	if (suptr == NULL)
763 		return;
764 
765 	/*
766 	 * We now have an undo vector for this process.
767 	 */
768 	DPRINTF(("proc @%p has undo structure with %d entries\n", p,
769 	    suptr->un_cnt));
770 
771 	/*
772 	 * If there are any active undo elements then process them.
773 	 */
774 	if (suptr->un_cnt > 0) {
775 		int ix;
776 
777 		for (ix = 0; ix < suptr->un_cnt; ix++) {
778 			int semid = suptr->un_ent[ix].un_id;
779 			int semnum = suptr->un_ent[ix].un_num;
780 			int adjval = suptr->un_ent[ix].un_adjval;
781 			struct semid_ds *semaptr;
782 
783 			if ((semaptr = sema[semid]) == NULL)
784 				panic("semexit - semid not allocated");
785 			if (semnum >= semaptr->sem_nsems)
786 				panic("semexit - semnum out of range");
787 
788 			DPRINTF(("semexit:  %p id=%d num=%d(adj=%d) ; sem=%d\n",
789 			    suptr->un_proc, suptr->un_ent[ix].un_id,
790 			    suptr->un_ent[ix].un_num,
791 			    suptr->un_ent[ix].un_adjval,
792 			    semaptr->sem_base[semnum].semval));
793 
794 			if (adjval < 0 &&
795 			    semaptr->sem_base[semnum].semval < -adjval)
796 				semaptr->sem_base[semnum].semval = 0;
797 			else
798 				semaptr->sem_base[semnum].semval += adjval;
799 
800 			wakeup(&sema[semid]);
801 			DPRINTF(("semexit:  back from wakeup\n"));
802 		}
803 	}
804 
805 	/*
806 	 * Deallocate the undo vector.
807 	 */
808 	DPRINTF(("removing vector\n"));
809 	*supptr = SLIST_NEXT(suptr, un_next);
810 	pool_put(&semu_pool, suptr);
811 	semutot--;
812 }
813 
814 /*
815  * Userland access to struct seminfo.
816  */
817 int
sysctl_sysvsem(int * name,u_int namelen,void * oldp,size_t * oldlenp,void * newp,size_t newlen)818 sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp,
819 	void *newp, size_t newlen)
820 {
821 	int error, val;
822 	struct semid_ds **sema_new;
823 	unsigned short *newseqs;
824 
825 	if (namelen != 2) {
826 		switch (name[0]) {
827 		case KERN_SEMINFO_SEMMNI:
828 		case KERN_SEMINFO_SEMMNS:
829 		case KERN_SEMINFO_SEMMNU:
830 		case KERN_SEMINFO_SEMMSL:
831 		case KERN_SEMINFO_SEMOPM:
832 		case KERN_SEMINFO_SEMUME:
833 		case KERN_SEMINFO_SEMUSZ:
834 		case KERN_SEMINFO_SEMVMX:
835 		case KERN_SEMINFO_SEMAEM:
836 			break;
837 		default:
838                         return (ENOTDIR);       /* overloaded */
839                 }
840         }
841 
842 	switch (name[0]) {
843 	case KERN_SEMINFO_SEMMNI:
844 		val = seminfo.semmni;
845 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
846 		    val == seminfo.semmni)
847 			return (error);
848 
849 		if (val < seminfo.semmni || val > 0xffff)
850 			return (EINVAL);
851 
852 		/* Expand semsegs and semseqs arrays */
853 		sema_new = malloc(val * sizeof(struct semid_ds *),
854 		    M_SEM, M_WAITOK);
855 		bcopy(sema, sema_new,
856 		    seminfo.semmni * sizeof(struct semid_ds *));
857 		bzero(sema_new + seminfo.semmni,
858 		    (val - seminfo.semmni) * sizeof(struct semid_ds *));
859 		newseqs = malloc(val * sizeof(unsigned short), M_SEM, M_WAITOK);
860 		bcopy(semseqs, newseqs,
861 		    seminfo.semmni * sizeof(unsigned short));
862 		bzero(newseqs + seminfo.semmni,
863 		    (val - seminfo.semmni) * sizeof(unsigned short));
864 		free(sema, M_SEM);
865 		free(semseqs, M_SEM);
866 		sema = sema_new;
867 		semseqs = newseqs;
868 		seminfo.semmni = val;
869 		return (0);
870 	case KERN_SEMINFO_SEMMNS:
871 		val = seminfo.semmns;
872 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
873 		    val == seminfo.semmns)
874 			return (error);
875 		if (val < seminfo.semmns || val > 0xffff)
876 			return (EINVAL);	/* can't decrease semmns */
877 		seminfo.semmns = val;
878 		return (0);
879 	case KERN_SEMINFO_SEMMNU:
880 		val = seminfo.semmnu;
881 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
882 		    val == seminfo.semmnu)
883 			return (error);
884 		if (val < seminfo.semmnu)
885 			return (EINVAL);	/* can't decrease semmnu */
886 		seminfo.semmnu = val;
887 		return (0);
888 	case KERN_SEMINFO_SEMMSL:
889 		val = seminfo.semmsl;
890 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
891 		    val == seminfo.semmsl)
892 			return (error);
893 		if (val < seminfo.semmsl || val > 0xffff)
894 			return (EINVAL);	/* can't decrease semmsl */
895 		seminfo.semmsl = val;
896 		return (0);
897 	case KERN_SEMINFO_SEMOPM:
898 		val = seminfo.semopm;
899 		if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
900 		    val == seminfo.semopm)
901 			return (error);
902 		if (val <= 0)
903 			return (EINVAL);	/* semopm must be >= 1 */
904 		seminfo.semopm = val;
905 		return (0);
906 	case KERN_SEMINFO_SEMUME:
907 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semume));
908 	case KERN_SEMINFO_SEMUSZ:
909 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semusz));
910 	case KERN_SEMINFO_SEMVMX:
911 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semvmx));
912 	case KERN_SEMINFO_SEMAEM:
913 		return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semaem));
914 	default:
915 		return (EOPNOTSUPP);
916 	}
917 	/* NOTREACHED */
918 }
919