]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/uipc_sem.c
This commit was generated by cvs2svn to compensate for changes in r168371,
[FreeBSD/FreeBSD.git] / sys / kern / uipc_sem.c
1 /*-
2  * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
3  * Copyright (c) 2003-2005 SPARTA, Inc.
4  * Copyright (c) 2005 Robert N. M. Watson
5  * All rights reserved.
6  *
7  * This software was developed for the FreeBSD Project in part by Network
8  * Associates Laboratories, the Security Research Division of Network
9  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
10  * as part of the DARPA CHATS research program.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36
37 #include "opt_mac.h"
38 #include "opt_posix.h"
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/sysproto.h>
43 #include <sys/eventhandler.h>
44 #include <sys/kernel.h>
45 #include <sys/ksem.h>
46 #include <sys/priv.h>
47 #include <sys/proc.h>
48 #include <sys/posix4.h>
49 #include <sys/lock.h>
50 #include <sys/mutex.h>
51 #include <sys/module.h>
52 #include <sys/condvar.h>
53 #include <sys/sem.h>
54 #include <sys/uio.h>
55 #include <sys/semaphore.h>
56 #include <sys/syscall.h>
57 #include <sys/stat.h>
58 #include <sys/sysent.h>
59 #include <sys/sysctl.h>
60 #include <sys/time.h>
61 #include <sys/malloc.h>
62 #include <sys/fcntl.h>
63 #include <sys/_semaphore.h>
64
65 #include <security/mac/mac_framework.h>
66
67 static int sem_count_proc(struct proc *p);
68 static struct ksem *sem_lookup_byname(const char *name);
69 static int sem_create(struct thread *td, const char *name,
70     struct ksem **ksret, mode_t mode, unsigned int value);
71 static void sem_free(struct ksem *ksnew);
72 static int sem_perm(struct thread *td, struct ksem *ks);
73 static void sem_enter(struct proc *p, struct ksem *ks);
74 static int sem_leave(struct proc *p, struct ksem *ks);
75 static void sem_exechook(void *arg, struct proc *p, struct image_params *imgp);
76 static void sem_exithook(void *arg, struct proc *p);
77 static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
78     int flags);
79 static int sem_hasopen(struct thread *td, struct ksem *ks);
80
81 static int kern_sem_close(struct thread *td, semid_t id);
82 static int kern_sem_post(struct thread *td, semid_t id);
83 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
84     struct timespec *abstime);
85 static int kern_sem_init(struct thread *td, int dir, unsigned int value,
86     semid_t *idp);
87 static int kern_sem_open(struct thread *td, int dir, const char *name,
88     int oflag, mode_t mode, unsigned int value, semid_t *idp);
89 static int kern_sem_unlink(struct thread *td, const char *name);
90
91 #ifndef SEM_MAX
92 #define SEM_MAX 30
93 #endif
94
95 #define SEM_MAX_NAMELEN 14
96
97 #define SEM_TO_ID(x)    ((intptr_t)(x))
98 #define ID_TO_SEM(x)    id_to_sem(x)
99
100 /*
101  * available semaphores go here, this includes sem_init and any semaphores
102  * created via sem_open that have not yet been unlinked.
103  */
104 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
105 /*
106  * semaphores still in use but have been sem_unlink()'d go here.
107  */
108 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
109
110 static struct mtx sem_lock;
111 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
112
113 static int nsems = 0;
114 SYSCTL_DECL(_p1003_1b);
115 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
116
117 static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
118
119 #ifdef SEM_DEBUG
120 #define DP(x)   printf x
121 #else
122 #define DP(x)
123 #endif
124
125 static __inline
126 void
127 sem_ref(struct ksem *ks)
128 {
129
130         mtx_assert(&sem_lock, MA_OWNED);
131         ks->ks_ref++;
132         DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
133 }
134
135 static __inline
136 void
137 sem_rel(struct ksem *ks)
138 {
139
140         mtx_assert(&sem_lock, MA_OWNED);
141         DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
142         if (--ks->ks_ref == 0)
143                 sem_free(ks);
144 }
145
146 static __inline struct ksem *id_to_sem(semid_t id);
147
148 static __inline
149 struct ksem *
150 id_to_sem(semid_t id)
151 {
152         struct ksem *ks;
153
154         mtx_assert(&sem_lock, MA_OWNED);
155         DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
156         LIST_FOREACH(ks, &ksem_head, ks_entry) {
157                 DP(("id_to_sem: ks = %p\n", ks));
158                 if (ks == (struct ksem *)id)
159                         return (ks);
160         }
161         return (NULL);
162 }
163
164 static struct ksem *
165 sem_lookup_byname(const char *name)
166 {
167         struct ksem *ks;
168
169         mtx_assert(&sem_lock, MA_OWNED);
170         LIST_FOREACH(ks, &ksem_head, ks_entry)
171                 if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
172                         return (ks);
173         return (NULL);
174 }
175
176 static int
177 sem_create(struct thread *td, const char *name, struct ksem **ksret,
178     mode_t mode, unsigned int value)
179 {
180         struct ksem *ret;
181         struct proc *p;
182         struct ucred *uc;
183         size_t len;
184         int error;
185
186         DP(("sem_create\n"));
187         p = td->td_proc;
188         uc = td->td_ucred;
189         if (value > SEM_VALUE_MAX)
190                 return (EINVAL);
191         ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
192         if (name != NULL) {
193                 len = strlen(name);
194                 if (len > SEM_MAX_NAMELEN) {
195                         free(ret, M_SEM);
196                         return (ENAMETOOLONG);
197                 }
198                 /* name must start with a '/' but not contain one. */
199                 if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
200                         free(ret, M_SEM);
201                         return (EINVAL);
202                 }
203                 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
204                 strcpy(ret->ks_name, name);
205         } else {
206                 ret->ks_name = NULL;
207         }
208         ret->ks_mode = mode;
209         ret->ks_value = value;
210         ret->ks_ref = 1;
211         ret->ks_waiters = 0;
212         ret->ks_uid = uc->cr_uid;
213         ret->ks_gid = uc->cr_gid;
214         ret->ks_onlist = 0;
215         cv_init(&ret->ks_cv, "sem");
216         LIST_INIT(&ret->ks_users);
217 #ifdef MAC
218         mac_init_posix_sem(ret);
219         mac_create_posix_sem(uc, ret);
220 #endif
221         if (name != NULL)
222                 sem_enter(td->td_proc, ret);
223         *ksret = ret;
224         mtx_lock(&sem_lock);
225         if (nsems >= p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
226                 sem_leave(td->td_proc, ret);
227                 sem_free(ret);
228                 error = ENFILE;
229         } else {
230                 nsems++;
231                 error = 0;
232         }
233         mtx_unlock(&sem_lock);
234         return (error);
235 }
236
237 #ifndef _SYS_SYSPROTO_H_
238 struct ksem_init_args {
239         unsigned int value;
240         semid_t *idp;
241 };
242 int ksem_init(struct thread *td, struct ksem_init_args *uap);
243 #endif
244 int
245 ksem_init(struct thread *td, struct ksem_init_args *uap)
246 {
247         int error;
248
249         error = kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp);
250         return (error);
251 }
252
253 static int
254 kern_sem_init(struct thread *td, int dir, unsigned int value, semid_t *idp)
255 {
256         struct ksem *ks;
257         semid_t id;
258         int error;
259
260         error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
261         if (error)
262                 return (error);
263         id = SEM_TO_ID(ks);
264         if (dir == UIO_USERSPACE) {
265                 error = copyout(&id, idp, sizeof(id));
266                 if (error) {
267                         mtx_lock(&sem_lock);
268                         sem_rel(ks);
269                         mtx_unlock(&sem_lock);
270                         return (error);
271                 }
272         } else {
273                 *idp = id;
274         }
275         mtx_lock(&sem_lock);
276         LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
277         ks->ks_onlist = 1;
278         mtx_unlock(&sem_lock);
279         return (error);
280 }
281
282 #ifndef _SYS_SYSPROTO_H_
283 struct ksem_open_args {
284         char *name;
285         int oflag;
286         mode_t mode;
287         unsigned int value;
288         semid_t *idp;   
289 };
290 int ksem_open(struct thread *td, struct ksem_open_args *uap);
291 #endif
292 int
293 ksem_open(struct thread *td, struct ksem_open_args *uap)
294 {
295         char name[SEM_MAX_NAMELEN + 1];
296         size_t done;
297         int error;
298
299         error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
300         if (error)
301                 return (error);
302         DP((">>> sem_open start\n"));
303         error = kern_sem_open(td, UIO_USERSPACE,
304             name, uap->oflag, uap->mode, uap->value, uap->idp);
305         DP(("<<< sem_open end\n"));
306         return (error);
307 }
308
309 static int
310 kern_sem_open(struct thread *td, int dir, const char *name, int oflag,
311     mode_t mode, unsigned int value, semid_t *idp)
312 {
313         struct ksem *ksnew, *ks;
314         int error;
315         semid_t id;
316
317         ksnew = NULL;
318         mtx_lock(&sem_lock);
319         ks = sem_lookup_byname(name);
320         /*
321          * If we found it but O_EXCL is set, error.
322          */
323         if (ks != NULL && (oflag & O_EXCL) != 0) {
324                 mtx_unlock(&sem_lock);
325                 return (EEXIST);
326         }
327         /*
328          * If we didn't find it...
329          */
330         if (ks == NULL) {
331                 /*
332                  * didn't ask for creation? error.
333                  */
334                 if ((oflag & O_CREAT) == 0) {
335                         mtx_unlock(&sem_lock);
336                         return (ENOENT);
337                 }
338                 /*
339                  * We may block during creation, so drop the lock.
340                  */
341                 mtx_unlock(&sem_lock);
342                 error = sem_create(td, name, &ksnew, mode, value);
343                 if (error != 0)
344                         return (error);
345                 id = SEM_TO_ID(ksnew);
346                 if (dir == UIO_USERSPACE) {
347                         DP(("about to copyout! %d to %p\n", id, idp));
348                         error = copyout(&id, idp, sizeof(id));
349                         if (error) {
350                                 mtx_lock(&sem_lock);
351                                 sem_leave(td->td_proc, ksnew);
352                                 sem_rel(ksnew);
353                                 mtx_unlock(&sem_lock);
354                                 return (error);
355                         }
356                 } else {
357                         DP(("about to set! %d to %p\n", id, idp));
358                         *idp = id;
359                 }
360                 /*
361                  * We need to make sure we haven't lost a race while
362                  * allocating during creation.
363                  */
364                 mtx_lock(&sem_lock);
365                 ks = sem_lookup_byname(name);
366                 if (ks != NULL) {
367                         /* we lost... */
368                         sem_leave(td->td_proc, ksnew);
369                         sem_rel(ksnew);
370                         /* we lost and we can't loose... */
371                         if ((oflag & O_EXCL) != 0) {
372                                 mtx_unlock(&sem_lock);
373                                 return (EEXIST);
374                         }
375                 } else {
376                         DP(("sem_create: about to add to list...\n"));
377                         LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry); 
378                         DP(("sem_create: setting list bit...\n"));
379                         ksnew->ks_onlist = 1;
380                         DP(("sem_create: done, about to unlock...\n"));
381                 }
382         } else {
383 #ifdef MAC
384                 error = mac_check_posix_sem_open(td->td_ucred, ks);
385                 if (error)
386                         goto err_open;
387 #endif
388                 /*
389                  * if we aren't the creator, then enforce permissions.
390                  */
391                 error = sem_perm(td, ks);
392                 if (error)
393                         goto err_open;
394                 sem_ref(ks);
395                 mtx_unlock(&sem_lock);
396                 id = SEM_TO_ID(ks);
397                 if (dir == UIO_USERSPACE) {
398                         error = copyout(&id, idp, sizeof(id));
399                         if (error) {
400                                 mtx_lock(&sem_lock);
401                                 sem_rel(ks);
402                                 mtx_unlock(&sem_lock);
403                                 return (error);
404                         }
405                 } else {
406                         *idp = id;
407                 }
408                 sem_enter(td->td_proc, ks);
409                 mtx_lock(&sem_lock);
410                 sem_rel(ks);
411         }
412 err_open:
413         mtx_unlock(&sem_lock);
414         return (error);
415 }
416
417 static int
418 sem_perm(struct thread *td, struct ksem *ks)
419 {
420         struct ucred *uc;
421
422         /*
423          * XXXRW: This permission routine appears to be incorrect.  If the
424          * user matches, we shouldn't go on to the group if the user
425          * permissions don't allow the action?  Not changed for now.  To fix,
426          * change from a series of if (); if (); to if () else if () else...
427          */
428         uc = td->td_ucred;
429         DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
430             uc->cr_uid, uc->cr_gid,
431              ks->ks_uid, ks->ks_gid, ks->ks_mode));
432         if ((uc->cr_uid == ks->ks_uid) && (ks->ks_mode & S_IWUSR) != 0)
433                 return (0);
434         if ((uc->cr_gid == ks->ks_gid) && (ks->ks_mode & S_IWGRP) != 0)
435                 return (0);
436         if ((ks->ks_mode & S_IWOTH) != 0)
437                 return (0);
438         return (priv_check(td, PRIV_SEM_WRITE));
439 }
440
441 static void
442 sem_free(struct ksem *ks)
443 {
444
445         nsems--;
446         if (ks->ks_onlist)
447                 LIST_REMOVE(ks, ks_entry);
448         if (ks->ks_name != NULL)
449                 free(ks->ks_name, M_SEM);
450         cv_destroy(&ks->ks_cv);
451         free(ks, M_SEM);
452 }
453
454 static __inline struct kuser *sem_getuser(struct proc *p, struct ksem *ks);
455
456 static __inline struct kuser *
457 sem_getuser(struct proc *p, struct ksem *ks)
458 {
459         struct kuser *k;
460
461         LIST_FOREACH(k, &ks->ks_users, ku_next)
462                 if (k->ku_pid == p->p_pid)
463                         return (k);
464         return (NULL);
465 }
466
467 static int
468 sem_hasopen(struct thread *td, struct ksem *ks)
469 {
470         
471         return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
472             || sem_getuser(td->td_proc, ks) != NULL);
473 }
474
475 static int
476 sem_leave(struct proc *p, struct ksem *ks)
477 {
478         struct kuser *k;
479
480         DP(("sem_leave: ks = %p\n", ks));
481         k = sem_getuser(p, ks);
482         DP(("sem_leave: ks = %p, k = %p\n", ks, k));
483         if (k != NULL) {
484                 LIST_REMOVE(k, ku_next);
485                 sem_rel(ks);
486                 DP(("sem_leave: about to free k\n"));
487                 free(k, M_SEM);
488                 DP(("sem_leave: returning\n"));
489                 return (0);
490         }
491         return (EINVAL);
492 }
493
494 static void
495 sem_enter(p, ks)
496         struct proc *p;
497         struct ksem *ks;
498 {
499         struct kuser *ku, *k;
500
501         ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
502         ku->ku_pid = p->p_pid;
503         mtx_lock(&sem_lock);
504         k = sem_getuser(p, ks);
505         if (k != NULL) {
506                 mtx_unlock(&sem_lock);
507                 free(ku, M_TEMP);
508                 return;
509         }
510         LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
511         sem_ref(ks);
512         mtx_unlock(&sem_lock);
513 }
514
515 #ifndef _SYS_SYSPROTO_H_
516 struct ksem_unlink_args {
517         char *name;
518 };
519 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
520 #endif
521 int
522 ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
523 {
524         char name[SEM_MAX_NAMELEN + 1];
525         size_t done;
526         int error;
527
528         error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
529         return (error ? error :
530             kern_sem_unlink(td, name));
531 }
532
533 static int
534 kern_sem_unlink(struct thread *td, const char *name)
535 {
536         struct ksem *ks;
537         int error;
538
539         mtx_lock(&sem_lock);
540         ks = sem_lookup_byname(name);
541         if (ks != NULL) {
542 #ifdef MAC
543                 error = mac_check_posix_sem_unlink(td->td_ucred, ks);
544                 if (error) {
545                         mtx_unlock(&sem_lock);
546                         return (error);
547                 }
548 #endif
549                 error = sem_perm(td, ks);
550         } else
551                 error = ENOENT;
552         DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
553         if (error == 0) {
554                 LIST_REMOVE(ks, ks_entry);
555                 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry); 
556                 sem_rel(ks);
557         }
558         mtx_unlock(&sem_lock);
559         return (error);
560 }
561
562 #ifndef _SYS_SYSPROTO_H_
563 struct ksem_close_args {
564         semid_t id;
565 };
566 int ksem_close(struct thread *td, struct ksem_close_args *uap);
567 #endif
568 int
569 ksem_close(struct thread *td, struct ksem_close_args *uap)
570 {
571
572         return (kern_sem_close(td, uap->id));
573 }
574
575 static int
576 kern_sem_close(struct thread *td, semid_t id)
577 {
578         struct ksem *ks;
579         int error;
580
581         error = EINVAL;
582         mtx_lock(&sem_lock);
583         ks = ID_TO_SEM(id);
584         /* this is not a valid operation for unnamed sems */
585         if (ks != NULL && ks->ks_name != NULL)
586                 error = sem_leave(td->td_proc, ks);
587         mtx_unlock(&sem_lock);
588         return (error);
589 }
590
591 #ifndef _SYS_SYSPROTO_H_
592 struct ksem_post_args {
593         semid_t id;
594 };
595 int ksem_post(struct thread *td, struct ksem_post_args *uap);
596 #endif
597 int
598 ksem_post(struct thread *td, struct ksem_post_args *uap)
599 {
600
601         return (kern_sem_post(td, uap->id));
602 }
603
604 static int
605 kern_sem_post(struct thread *td, semid_t id)
606 {
607         struct ksem *ks;
608         int error;
609
610         mtx_lock(&sem_lock);
611         ks = ID_TO_SEM(id);
612         if (ks == NULL || !sem_hasopen(td, ks)) {
613                 error = EINVAL;
614                 goto err;
615         }
616 #ifdef MAC
617         error = mac_check_posix_sem_post(td->td_ucred, ks);
618         if (error)
619                 goto err;
620 #endif
621         if (ks->ks_value == SEM_VALUE_MAX) {
622                 error = EOVERFLOW;
623                 goto err;
624         }
625         ++ks->ks_value;
626         if (ks->ks_waiters > 0)
627                 cv_signal(&ks->ks_cv);
628         error = 0;
629 err:
630         mtx_unlock(&sem_lock);
631         return (error);
632 }
633
634 #ifndef _SYS_SYSPROTO_H_
635 struct ksem_wait_args {
636         semid_t id;
637 };
638 int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
639 #endif
640 int
641 ksem_wait(struct thread *td, struct ksem_wait_args *uap)
642 {
643
644         return (kern_sem_wait(td, uap->id, 0, NULL));
645 }
646
647 #ifndef _SYS_SYSPROTO_H_
648 struct ksem_timedwait_args {
649         semid_t id;
650         const struct timespec *abstime;
651 };
652 int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
653 #endif
654 int
655 ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
656 {
657         struct timespec abstime;
658         struct timespec *ts;
659         int error;
660
661         /* We allow a null timespec (wait forever). */
662         if (uap->abstime == NULL)
663                 ts = NULL;
664         else {
665                 error = copyin(uap->abstime, &abstime, sizeof(abstime));
666                 if (error != 0)
667                         return (error);
668                 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
669                         return (EINVAL);
670                 ts = &abstime;
671         }
672         return (kern_sem_wait(td, uap->id, 0, ts));
673 }
674
675 #ifndef _SYS_SYSPROTO_H_
676 struct ksem_trywait_args {
677         semid_t id;
678 };
679 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
680 #endif
681 int
682 ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
683 {
684
685         return (kern_sem_wait(td, uap->id, 1, NULL));
686 }
687
688 static int
689 kern_sem_wait(struct thread *td, semid_t id, int tryflag,
690     struct timespec *abstime)
691 {
692         struct timespec ts1, ts2;
693         struct timeval tv;
694         struct ksem *ks;
695         int error;
696
697         DP((">>> kern_sem_wait entered!\n"));
698         mtx_lock(&sem_lock);
699         ks = ID_TO_SEM(id);
700         if (ks == NULL) {
701                 DP(("kern_sem_wait ks == NULL\n"));
702                 error = EINVAL;
703                 goto err;
704         }
705         sem_ref(ks);
706         if (!sem_hasopen(td, ks)) {
707                 DP(("kern_sem_wait hasopen failed\n"));
708                 error = EINVAL;
709                 goto err;
710         }
711 #ifdef MAC
712         error = mac_check_posix_sem_wait(td->td_ucred, ks);
713         if (error) {
714                 DP(("kern_sem_wait mac failed\n"));
715                 goto err;
716         }
717 #endif
718         DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
719         if (ks->ks_value == 0) {
720                 ks->ks_waiters++;
721                 if (tryflag != 0)
722                         error = EAGAIN;
723                 else if (abstime == NULL)
724                         error = cv_wait_sig(&ks->ks_cv, &sem_lock);
725                 else {
726                         for (;;) {
727                                 ts1 = *abstime;
728                                 getnanotime(&ts2);
729                                 timespecsub(&ts1, &ts2);
730                                 TIMESPEC_TO_TIMEVAL(&tv, &ts1);
731                                 if (tv.tv_sec < 0) {
732                                         error = ETIMEDOUT;
733                                         break;
734                                 }
735                                 error = cv_timedwait_sig(&ks->ks_cv,
736                                     &sem_lock, tvtohz(&tv));
737                                 if (error != EWOULDBLOCK)
738                                         break;
739                         }
740                 }
741                 ks->ks_waiters--;
742                 if (error)
743                         goto err;
744         }
745         ks->ks_value--;
746         error = 0;
747 err:
748         if (ks != NULL)
749                 sem_rel(ks);
750         mtx_unlock(&sem_lock);
751         DP(("<<< kern_sem_wait leaving, error = %d\n", error));
752         return (error);
753 }
754
755 #ifndef _SYS_SYSPROTO_H_
756 struct ksem_getvalue_args {
757         semid_t id;
758         int *val;
759 };
760 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
761 #endif
762 int
763 ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
764 {
765         struct ksem *ks;
766         int error, val;
767
768         mtx_lock(&sem_lock);
769         ks = ID_TO_SEM(uap->id);
770         if (ks == NULL || !sem_hasopen(td, ks)) {
771                 mtx_unlock(&sem_lock);
772                 return (EINVAL);
773         }
774 #ifdef MAC
775         error = mac_check_posix_sem_getvalue(td->td_ucred, ks);
776         if (error) {
777                 mtx_unlock(&sem_lock);
778                 return (error);
779         }
780 #endif
781         val = ks->ks_value;
782         mtx_unlock(&sem_lock);
783         error = copyout(&val, uap->val, sizeof(val));
784         return (error);
785 }
786
787 #ifndef _SYS_SYSPROTO_H_
788 struct ksem_destroy_args {
789         semid_t id;
790 };
791 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
792 #endif
793 int
794 ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
795 {
796         struct ksem *ks;
797         int error;
798
799         mtx_lock(&sem_lock);
800         ks = ID_TO_SEM(uap->id);
801         if (ks == NULL || !sem_hasopen(td, ks) ||
802             ks->ks_name != NULL) {
803                 error = EINVAL;
804                 goto err;
805         }
806 #ifdef MAC
807         error = mac_check_posix_sem_destroy(td->td_ucred, ks);
808         if (error)
809                 goto err;
810 #endif
811         if (ks->ks_waiters != 0) {
812                 error = EBUSY;
813                 goto err;
814         }
815         sem_rel(ks);
816         error = 0;
817 err:
818         mtx_unlock(&sem_lock);
819         return (error);
820 }
821
822 /*
823  * Count the number of kusers associated with a proc, so as to guess at how
824  * many to allocate when forking.
825  */
826 static int
827 sem_count_proc(struct proc *p)
828 {
829         struct ksem *ks;
830         struct kuser *ku;
831         int count;
832
833         mtx_assert(&sem_lock, MA_OWNED);
834
835         count = 0;
836         LIST_FOREACH(ks, &ksem_head, ks_entry) {
837                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
838                         if (ku->ku_pid == p->p_pid)
839                                 count++;
840                 }
841         }
842         LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
843                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
844                         if (ku->ku_pid == p->p_pid)
845                                 count++;
846                 }
847         }
848         return (count);
849 }
850
851 /*
852  * When a process forks, the child process must gain a reference to each open
853  * semaphore in the parent process, whether it is unlinked or not.  This
854  * requires allocating a kuser structure for each semaphore reference in the
855  * new process.  Because the set of semaphores in the parent can change while
856  * the fork is in progress, we have to handle races -- first we attempt to
857  * allocate enough storage to acquire references to each of the semaphores,
858  * then we enter the semaphores and release the temporary references.
859  */
860 static void
861 sem_forkhook(void *arg, struct proc *p1, struct proc *p2, int flags)
862 {
863         struct ksem *ks, **sem_array;
864         int count, i, new_count;
865         struct kuser *ku;
866
867         mtx_lock(&sem_lock);
868         count = sem_count_proc(p1);
869         if (count == 0) {
870                 mtx_unlock(&sem_lock);
871                 return;
872         }
873 race_lost:
874         mtx_assert(&sem_lock, MA_OWNED);
875         mtx_unlock(&sem_lock);
876         sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
877         mtx_lock(&sem_lock);
878         new_count = sem_count_proc(p1);
879         if (count < new_count) {
880                 /* Lost race, repeat and allocate more storage. */
881                 free(sem_array, M_TEMP);
882                 count = new_count;
883                 goto race_lost;
884         }
885         /*
886          * Given an array capable of storing an adequate number of semaphore
887          * references, now walk the list of semaphores and acquire a new
888          * reference for any semaphore opened by p1.
889          */
890         count = new_count;
891         i = 0;
892         LIST_FOREACH(ks, &ksem_head, ks_entry) {
893                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
894                         if (ku->ku_pid == p1->p_pid) {
895                                 sem_ref(ks);
896                                 sem_array[i] = ks;
897                                 i++;
898                                 break;
899                         }
900                 }
901         }
902         LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
903                 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
904                         if (ku->ku_pid == p1->p_pid) {
905                                 sem_ref(ks);
906                                 sem_array[i] = ks;
907                                 i++;
908                                 break;
909                         }
910                 }
911         }
912         mtx_unlock(&sem_lock);
913         KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
914         /*
915          * Now cause p2 to enter each of the referenced semaphores, then
916          * release our temporary reference.  This is pretty inefficient.
917          * Finally, free our temporary array.
918          */
919         for (i = 0; i < count; i++) {
920                 sem_enter(p2, sem_array[i]);
921                 mtx_lock(&sem_lock);
922                 sem_rel(sem_array[i]);
923                 mtx_unlock(&sem_lock);
924         }
925         free(sem_array, M_TEMP);
926 }
927
928 static void
929 sem_exechook(void *arg, struct proc *p, struct image_params *imgp __unused)
930 {
931         sem_exithook(arg, p);           
932 }
933
934 static void
935 sem_exithook(void *arg, struct proc *p)
936 {
937         struct ksem *ks, *ksnext;
938
939         mtx_lock(&sem_lock);
940         ks = LIST_FIRST(&ksem_head);
941         while (ks != NULL) {
942                 ksnext = LIST_NEXT(ks, ks_entry);
943                 sem_leave(p, ks);
944                 ks = ksnext;
945         }
946         ks = LIST_FIRST(&ksem_deadhead);
947         while (ks != NULL) {
948                 ksnext = LIST_NEXT(ks, ks_entry);
949                 sem_leave(p, ks);
950                 ks = ksnext;
951         }
952         mtx_unlock(&sem_lock);
953 }
954
955 static int
956 sem_modload(struct module *module, int cmd, void *arg)
957 {
958         int error = 0;
959
960         switch (cmd) {
961         case MOD_LOAD:
962                 mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
963                 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
964                 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
965                 sem_exit_tag = EVENTHANDLER_REGISTER(process_exit, sem_exithook,
966                     NULL, EVENTHANDLER_PRI_ANY);
967                 sem_exec_tag = EVENTHANDLER_REGISTER(process_exec, sem_exechook,
968                     NULL, EVENTHANDLER_PRI_ANY);
969                 sem_fork_tag = EVENTHANDLER_REGISTER(process_fork, sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
970                 break;
971         case MOD_UNLOAD:
972                 if (nsems != 0) {
973                         error = EOPNOTSUPP;
974                         break;
975                 }
976                 EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
977                 EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
978                 EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
979                 mtx_destroy(&sem_lock);
980                 break;
981         case MOD_SHUTDOWN:
982                 break;
983         default:
984                 error = EINVAL;
985                 break;
986         }
987         return (error);
988 }
989
990 static moduledata_t sem_mod = {
991         "sem",
992         &sem_modload,
993         NULL
994 };
995
996 SYSCALL_MODULE_HELPER(ksem_init);
997 SYSCALL_MODULE_HELPER(ksem_open);
998 SYSCALL_MODULE_HELPER(ksem_unlink);
999 SYSCALL_MODULE_HELPER(ksem_close);
1000 SYSCALL_MODULE_HELPER(ksem_post);
1001 SYSCALL_MODULE_HELPER(ksem_wait);
1002 SYSCALL_MODULE_HELPER(ksem_timedwait);
1003 SYSCALL_MODULE_HELPER(ksem_trywait);
1004 SYSCALL_MODULE_HELPER(ksem_getvalue);
1005 SYSCALL_MODULE_HELPER(ksem_destroy);
1006
1007 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
1008 MODULE_VERSION(sem, 1);