]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_control.c
af6ae8e3b942f075faf9dbfdd5a2e5fde067002c
[apple/xnu.git] / bsd / kern / kern_control.c
1 /*
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30
31 /*
32 * Kernel Control domain - allows control connections to
33 * and to read/write data.
34 *
35 * Vincent Lubet, 040506
36 * Christophe Allie, 010928
37 * Justin C. Walker, 990319
38 */
39
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/syslog.h>
44 #include <sys/socket.h>
45 #include <sys/socketvar.h>
46 #include <sys/protosw.h>
47 #include <sys/domain.h>
48 #include <sys/malloc.h>
49 #include <sys/mbuf.h>
50 #include <sys/sys_domain.h>
51 #include <sys/kern_event.h>
52 #include <sys/kern_control.h>
53 #include <net/if_var.h>
54
55 #include <mach/vm_types.h>
56 #include <mach/kmod.h>
57
58 #include <kern/thread.h>
59
60 /*
61 * Definitions and vars for we support
62 */
63
64 #define CTL_SENDSIZE (2 * 1024) /* default buffer size */
65 #define CTL_RECVSIZE (8 * 1024) /* default buffer size */
66
67 /*
68 * Definitions and vars for we support
69 */
70
71 static u_int32_t ctl_last_id = 0;
72 static u_int32_t ctl_max = 256;
73 static u_int32_t ctl_maxunit = 65536;
74 static lck_grp_attr_t *ctl_lck_grp_attr = 0;
75 static lck_attr_t *ctl_lck_attr = 0;
76 static lck_grp_t *ctl_lck_grp = 0;
77 static lck_mtx_t *ctl_mtx;
78
79
80 /* all the controllers are chained */
81 TAILQ_HEAD(, kctl) ctl_head;
82
83 static int ctl_attach(struct socket *, int, struct proc *);
84 static int ctl_detach(struct socket *);
85 static int ctl_sofreelastref(struct socket *so);
86 static int ctl_connect(struct socket *, struct sockaddr *, struct proc *);
87 static int ctl_disconnect(struct socket *);
88 static int ctl_ioctl(struct socket *so, u_long cmd, caddr_t data,
89 struct ifnet *ifp, struct proc *p);
90 static int ctl_send(struct socket *, int, struct mbuf *,
91 struct sockaddr *, struct mbuf *, struct proc *);
92 static int ctl_ctloutput(struct socket *, struct sockopt *);
93 static int ctl_peeraddr(struct socket *so, struct sockaddr **nam);
94
95 static struct kctl *ctl_find_by_id(u_int32_t);
96 static struct kctl *ctl_find_by_name(const char *);
97 static struct kctl *ctl_find_by_id_unit(u_int32_t id, u_int32_t unit);
98
99 static struct ctl_cb *kcb_find(struct kctl *, u_int32_t unit);
100 static void ctl_post_msg(u_long event_code, u_int32_t id);
101
102 static int ctl_lock(struct socket *, int, int);
103 static int ctl_unlock(struct socket *, int, int);
104 static lck_mtx_t * ctl_getlock(struct socket *, int);
105
106 static struct pr_usrreqs ctl_usrreqs =
107 {
108 pru_abort_notsupp, pru_accept_notsupp, ctl_attach, pru_bind_notsupp,
109 ctl_connect, pru_connect2_notsupp, ctl_ioctl, ctl_detach,
110 ctl_disconnect, pru_listen_notsupp, ctl_peeraddr,
111 pru_rcvd_notsupp, pru_rcvoob_notsupp, ctl_send,
112 pru_sense_null, pru_shutdown_notsupp, pru_sockaddr_notsupp,
113 sosend, soreceive, pru_sopoll_notsupp
114 };
115
116 static struct protosw kctlswk_dgram =
117 {
118 SOCK_DGRAM, &systemdomain, SYSPROTO_CONTROL,
119 PR_ATOMIC|PR_CONNREQUIRED|PR_PCBLOCK,
120 NULL, NULL, NULL, ctl_ctloutput,
121 NULL, NULL,
122 NULL, NULL, NULL, NULL, &ctl_usrreqs,
123 ctl_lock, ctl_unlock, ctl_getlock, { 0, 0 } , 0, { 0 }
124 };
125
126 static struct protosw kctlswk_stream =
127 {
128 SOCK_STREAM, &systemdomain, SYSPROTO_CONTROL,
129 PR_CONNREQUIRED|PR_PCBLOCK,
130 NULL, NULL, NULL, ctl_ctloutput,
131 NULL, NULL,
132 NULL, NULL, NULL, NULL, &ctl_usrreqs,
133 ctl_lock, ctl_unlock, ctl_getlock, { 0, 0 } , 0, { 0 }
134 };
135
136
137 /*
138 * Install the protosw's for the Kernel Control manager.
139 */
140 __private_extern__ int
141 kern_control_init(void)
142 {
143 int error = 0;
144
145 ctl_lck_grp_attr = lck_grp_attr_alloc_init();
146 if (ctl_lck_grp_attr == 0) {
147 printf(": lck_grp_attr_alloc_init failed\n");
148 error = ENOMEM;
149 goto done;
150 }
151
152 ctl_lck_grp = lck_grp_alloc_init("Kernel Control Protocol", ctl_lck_grp_attr);
153 if (ctl_lck_grp == 0) {
154 printf("kern_control_init: lck_grp_alloc_init failed\n");
155 error = ENOMEM;
156 goto done;
157 }
158
159 ctl_lck_attr = lck_attr_alloc_init();
160 if (ctl_lck_attr == 0) {
161 printf("kern_control_init: lck_attr_alloc_init failed\n");
162 error = ENOMEM;
163 goto done;
164 }
165
166 ctl_mtx = lck_mtx_alloc_init(ctl_lck_grp, ctl_lck_attr);
167 if (ctl_mtx == 0) {
168 printf("kern_control_init: lck_mtx_alloc_init failed\n");
169 error = ENOMEM;
170 goto done;
171 }
172 TAILQ_INIT(&ctl_head);
173
174 error = net_add_proto(&kctlswk_dgram, &systemdomain);
175 if (error) {
176 log(LOG_WARNING, "kern_control_init: net_add_proto dgram failed (%d)\n", error);
177 }
178 error = net_add_proto(&kctlswk_stream, &systemdomain);
179 if (error) {
180 log(LOG_WARNING, "kern_control_init: net_add_proto stream failed (%d)\n", error);
181 }
182
183 done:
184 if (error != 0) {
185 if (ctl_mtx) {
186 lck_mtx_free(ctl_mtx, ctl_lck_grp);
187 ctl_mtx = 0;
188 }
189 if (ctl_lck_grp) {
190 lck_grp_free(ctl_lck_grp);
191 ctl_lck_grp = 0;
192 }
193 if (ctl_lck_grp_attr) {
194 lck_grp_attr_free(ctl_lck_grp_attr);
195 ctl_lck_grp_attr = 0;
196 }
197 if (ctl_lck_attr) {
198 lck_attr_free(ctl_lck_attr);
199 ctl_lck_attr = 0;
200 }
201 }
202 return error;
203 }
204
205 static void
206 kcb_delete(struct ctl_cb *kcb)
207 {
208 if (kcb != 0) {
209 if (kcb->mtx != 0)
210 lck_mtx_free(kcb->mtx, ctl_lck_grp);
211 FREE(kcb, M_TEMP);
212 }
213 }
214
215
216 /*
217 * Kernel Controller user-request functions
218 * attach function must exist and succeed
219 * detach not necessary
220 * we need a pcb for the per socket mutex
221 */
222 static int
223 ctl_attach(__unused struct socket *so, __unused int proto, __unused struct proc *p)
224 {
225 int error = 0;
226 struct ctl_cb *kcb = 0;
227
228 MALLOC(kcb, struct ctl_cb *, sizeof(struct ctl_cb), M_TEMP, M_WAITOK);
229 if (kcb == NULL) {
230 error = ENOMEM;
231 goto quit;
232 }
233 bzero(kcb, sizeof(struct ctl_cb));
234
235 kcb->mtx = lck_mtx_alloc_init(ctl_lck_grp, ctl_lck_attr);
236 if (kcb->mtx == NULL) {
237 error = ENOMEM;
238 goto quit;
239 }
240 kcb->so = so;
241 so->so_pcb = (caddr_t)kcb;
242
243 quit:
244 if (error != 0) {
245 kcb_delete(kcb);
246 kcb = 0;
247 }
248 return error;
249 }
250
251 static int
252 ctl_sofreelastref(struct socket *so)
253 {
254 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
255
256 so->so_pcb = 0;
257
258 if (kcb != 0) {
259 struct kctl *kctl;
260 if ((kctl = kcb->kctl) != 0) {
261 lck_mtx_lock(ctl_mtx);
262 TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
263 lck_mtx_lock(ctl_mtx);
264 }
265 kcb_delete(kcb);
266 }
267 return 0;
268 }
269
270 static int
271 ctl_detach(struct socket *so)
272 {
273 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
274
275 if (kcb == 0)
276 return 0;
277
278 soisdisconnected(so);
279 so->so_flags |= SOF_PCBCLEARING;
280 return 0;
281 }
282
283
284 static int
285 ctl_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p)
286 {
287 struct kctl *kctl;
288 int error = 0;
289 struct sockaddr_ctl sa;
290 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
291
292 if (kcb == 0)
293 panic("ctl_connect so_pcb null\n");
294
295 if (nam->sa_len != sizeof(struct sockaddr_ctl))
296 return(EINVAL);
297
298 bcopy(nam, &sa, sizeof(struct sockaddr_ctl));
299
300 lck_mtx_lock(ctl_mtx);
301 kctl = ctl_find_by_id_unit(sa.sc_id, sa.sc_unit);
302 if (kctl == NULL) {
303 lck_mtx_unlock(ctl_mtx);
304 return ENOENT;
305 }
306
307 if (((kctl->flags & CTL_FLAG_REG_SOCK_STREAM) && (so->so_type != SOCK_STREAM)) ||
308 (!(kctl->flags & CTL_FLAG_REG_SOCK_STREAM) && (so->so_type != SOCK_DGRAM))) {
309 lck_mtx_unlock(ctl_mtx);
310 return EPROTOTYPE;
311 }
312
313 if (kctl->flags & CTL_FLAG_PRIVILEGED) {
314 if (p == 0) {
315 lck_mtx_unlock(ctl_mtx);
316 return(EINVAL);
317 }
318 if ((error = proc_suser(p))) {
319 lck_mtx_unlock(ctl_mtx);
320 return error;
321 }
322 }
323
324 if ((kctl->flags & CTL_FLAG_REG_ID_UNIT) || sa.sc_unit != 0) {
325 if (kcb_find(kctl, sa.sc_unit) != NULL) {
326 lck_mtx_unlock(ctl_mtx);
327 return EBUSY;
328 }
329 } else {
330 u_int32_t unit = kctl->lastunit + 1;
331
332 while (1) {
333 if (unit == ctl_maxunit)
334 unit = 1;
335 if (kcb_find(kctl, unit) == NULL) {
336 kctl->lastunit = sa.sc_unit = unit;
337 break;
338 }
339 if (unit++ == kctl->lastunit) {
340 lck_mtx_unlock(ctl_mtx);
341 return EBUSY;
342 }
343 }
344 }
345
346 kcb->unit = sa.sc_unit;
347 kcb->kctl = kctl;
348 TAILQ_INSERT_TAIL(&kctl->kcb_head, kcb, next);
349 lck_mtx_unlock(ctl_mtx);
350
351 error = soreserve(so, kctl->sendbufsize, kctl->recvbufsize);
352 if (error)
353 goto done;
354 soisconnecting(so);
355
356 socket_unlock(so, 0);
357 error = (*kctl->connect)(kctl, &sa, &kcb->userdata);
358 socket_lock(so, 0);
359 if (error)
360 goto done;
361
362 soisconnected(so);
363
364 done:
365 if (error) {
366 soisdisconnected(so);
367 lck_mtx_lock(ctl_mtx);
368 kcb->kctl = 0;
369 kcb->unit = 0;
370 TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
371 lck_mtx_unlock(ctl_mtx);
372 }
373 return error;
374 }
375
376 static int
377 ctl_disconnect(struct socket *so)
378 {
379 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
380
381 if ((kcb = (struct ctl_cb *)so->so_pcb)) {
382 struct kctl *kctl = kcb->kctl;
383
384 if (kctl && kctl->disconnect) {
385 socket_unlock(so, 0);
386 (*kctl->disconnect)(kctl, kcb->unit, kcb->userdata);
387 socket_lock(so, 0);
388 }
389 lck_mtx_lock(ctl_mtx);
390 kcb->kctl = 0;
391 kcb->unit = 0;
392 TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
393 soisdisconnected(so);
394 lck_mtx_unlock(ctl_mtx);
395 }
396 return 0;
397 }
398
399 static int
400 ctl_peeraddr(struct socket *so, struct sockaddr **nam)
401 {
402 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
403 struct kctl *kctl;
404 struct sockaddr_ctl sc;
405
406 if (kcb == NULL) /* sanity check */
407 return(ENOTCONN);
408
409 if ((kctl = kcb->kctl) == NULL)
410 return(EINVAL);
411
412 bzero(&sc, sizeof(struct sockaddr_ctl));
413 sc.sc_len = sizeof(struct sockaddr_ctl);
414 sc.sc_family = AF_SYSTEM;
415 sc.ss_sysaddr = AF_SYS_CONTROL;
416 sc.sc_id = kctl->id;
417 sc.sc_unit = kcb->unit;
418
419 *nam = dup_sockaddr((struct sockaddr *)&sc, 1);
420
421 return 0;
422 }
423
424 static int
425 ctl_send(struct socket *so, int flags, struct mbuf *m,
426 __unused struct sockaddr *addr, __unused struct mbuf *control,
427 __unused struct proc *p)
428 {
429 int error = 0;
430 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
431 struct kctl *kctl;
432
433 if (kcb == NULL) /* sanity check */
434 return(ENOTCONN);
435
436 if ((kctl = kcb->kctl) == NULL)
437 return(EINVAL);
438
439 if (kctl->send) {
440 socket_unlock(so, 0);
441 error = (*kctl->send)(kctl, kcb->unit, kcb->userdata, m, flags);
442 socket_lock(so, 0);
443 }
444 return error;
445 }
446
447 errno_t
448 ctl_enqueuembuf(void *kctlref, u_int32_t unit, struct mbuf *m, u_int32_t flags)
449 {
450 struct ctl_cb *kcb;
451 struct socket *so;
452 errno_t error = 0;
453 struct kctl *kctl = (struct kctl *)kctlref;
454
455 if (kctl == NULL)
456 return EINVAL;
457
458 kcb = kcb_find(kctl, unit);
459 if (kcb == NULL)
460 return EINVAL;
461
462 so = (struct socket *)kcb->so;
463 if (so == NULL)
464 return EINVAL;
465
466 socket_lock(so, 1);
467 if (sbspace(&so->so_rcv) < m->m_pkthdr.len) {
468 error = ENOBUFS;
469 goto bye;
470 }
471 if ((flags & CTL_DATA_EOR))
472 m->m_flags |= M_EOR;
473 if (sbappend(&so->so_rcv, m) && (flags & CTL_DATA_NOWAKEUP) == 0)
474 sorwakeup(so);
475 bye:
476 socket_unlock(so, 1);
477 return error;
478 }
479
480 errno_t
481 ctl_enqueuedata(void *kctlref, u_int32_t unit, void *data, size_t len, u_int32_t flags)
482 {
483 struct ctl_cb *kcb;
484 struct socket *so;
485 struct mbuf *m;
486 errno_t error = 0;
487 struct kctl *kctl = (struct kctl *)kctlref;
488 unsigned int num_needed;
489 struct mbuf *n;
490 size_t curlen = 0;
491
492 if (kctlref == NULL)
493 return EINVAL;
494
495 kcb = kcb_find(kctl, unit);
496 if (kcb == NULL)
497 return EINVAL;
498
499 so = (struct socket *)kcb->so;
500 if (so == NULL)
501 return EINVAL;
502
503 socket_lock(so, 1);
504 if ((size_t)sbspace(&so->so_rcv) < len) {
505 error = ENOBUFS;
506 goto bye;
507 }
508
509 num_needed = 1;
510 m = m_allocpacket_internal(&num_needed, len, NULL, M_NOWAIT, 1, 0);
511 if (m == NULL) {
512 printf("ctl_enqueuedata: m_allocpacket_internal(%lu) failed\n", len);
513 error = ENOBUFS;
514 goto bye;
515 }
516
517 for (n = m; n != NULL; n = n->m_next) {
518 size_t mlen = mbuf_maxlen(n);
519
520 if (mlen + curlen > len)
521 mlen = len - curlen;
522 n->m_len = mlen;
523 bcopy((char *)data + curlen, n->m_data, mlen);
524 curlen += mlen;
525 }
526 mbuf_pkthdr_setlen(m, curlen);
527
528 if ((flags & CTL_DATA_EOR))
529 m->m_flags |= M_EOR;
530 if (sbappend(&so->so_rcv, m) && (flags & CTL_DATA_NOWAKEUP) == 0)
531 sorwakeup(so);
532 bye:
533 socket_unlock(so, 1);
534 return error;
535 }
536
537
538 errno_t
539 ctl_getenqueuespace(kern_ctl_ref kctlref, u_int32_t unit, size_t *space)
540 {
541 struct ctl_cb *kcb;
542 struct kctl *kctl = (struct kctl *)kctlref;
543 struct socket *so;
544
545 if (kctlref == NULL || space == NULL)
546 return EINVAL;
547
548 kcb = kcb_find(kctl, unit);
549 if (kcb == NULL)
550 return EINVAL;
551
552 so = (struct socket *)kcb->so;
553 if (so == NULL)
554 return EINVAL;
555
556 socket_lock(so, 1);
557 *space = sbspace(&so->so_rcv);
558 socket_unlock(so, 1);
559
560 return 0;
561 }
562
563 static int
564 ctl_ctloutput(struct socket *so, struct sockopt *sopt)
565 {
566 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
567 struct kctl *kctl;
568 int error = 0;
569 void *data;
570 size_t len;
571
572 if (sopt->sopt_level != SYSPROTO_CONTROL) {
573 return(EINVAL);
574 }
575
576 if (kcb == NULL) /* sanity check */
577 return(ENOTCONN);
578
579 if ((kctl = kcb->kctl) == NULL)
580 return(EINVAL);
581
582 switch (sopt->sopt_dir) {
583 case SOPT_SET:
584 if (kctl->setopt == NULL)
585 return(ENOTSUP);
586 MALLOC(data, void *, sopt->sopt_valsize, M_TEMP, M_WAITOK);
587 if (data == NULL)
588 return(ENOMEM);
589 error = sooptcopyin(sopt, data, sopt->sopt_valsize, sopt->sopt_valsize);
590 if (error == 0) {
591 socket_unlock(so, 0);
592 error = (*kctl->setopt)(kcb->kctl, kcb->unit, kcb->userdata, sopt->sopt_name,
593 data, sopt->sopt_valsize);
594 socket_lock(so, 0);
595 }
596 FREE(data, M_TEMP);
597 break;
598
599 case SOPT_GET:
600 if (kctl->getopt == NULL)
601 return(ENOTSUP);
602 data = NULL;
603 if (sopt->sopt_valsize && sopt->sopt_val) {
604 MALLOC(data, void *, sopt->sopt_valsize, M_TEMP, M_WAITOK);
605 if (data == NULL)
606 return(ENOMEM);
607 /* 4108337 - copy in data for get socket option */
608 error = sooptcopyin(sopt, data, sopt->sopt_valsize, sopt->sopt_valsize);
609 }
610 len = sopt->sopt_valsize;
611 socket_unlock(so, 0);
612 error = (*kctl->getopt)(kcb->kctl, kcb->unit, kcb->userdata, sopt->sopt_name,
613 data, &len);
614 socket_lock(so, 0);
615 if (error == 0) {
616 if (data != NULL)
617 error = sooptcopyout(sopt, data, len);
618 else
619 sopt->sopt_valsize = len;
620 }
621 if (data != NULL)
622 FREE(data, M_TEMP);
623 break;
624 }
625 return error;
626 }
627
628 static int
629 ctl_ioctl(__unused struct socket *so, u_long cmd, caddr_t data,
630 __unused struct ifnet *ifp, __unused struct proc *p)
631 {
632 int error = ENOTSUP;
633
634 switch (cmd) {
635 /* get the number of controllers */
636 case CTLIOCGCOUNT: {
637 struct kctl *kctl;
638 int n = 0;
639
640 lck_mtx_lock(ctl_mtx);
641 TAILQ_FOREACH(kctl, &ctl_head, next)
642 n++;
643 lck_mtx_unlock(ctl_mtx);
644
645 *(u_int32_t *)data = n;
646 error = 0;
647 break;
648 }
649 case CTLIOCGINFO: {
650 struct ctl_info *ctl_info = (struct ctl_info *)data;
651 struct kctl *kctl = 0;
652 size_t name_len = strlen(ctl_info->ctl_name);
653
654 if (name_len == 0 || name_len + 1 > MAX_KCTL_NAME) {
655 error = EINVAL;
656 break;
657 }
658 lck_mtx_lock(ctl_mtx);
659 kctl = ctl_find_by_name(ctl_info->ctl_name);
660 lck_mtx_unlock(ctl_mtx);
661 if (kctl == 0) {
662 error = ENOENT;
663 break;
664 }
665 ctl_info->ctl_id = kctl->id;
666 error = 0;
667 break;
668 }
669
670 /* add controls to get list of NKEs */
671
672 }
673
674 return error;
675 }
676
677 /*
678 * Register/unregister a NKE
679 */
680 errno_t
681 ctl_register(struct kern_ctl_reg *userkctl, kern_ctl_ref *kctlref)
682 {
683 struct kctl *kctl = 0;
684 u_int32_t id = -1;
685 u_int32_t n;
686 size_t name_len;
687
688 if (userkctl == NULL) /* sanity check */
689 return(EINVAL);
690 if (userkctl->ctl_connect == NULL)
691 return(EINVAL);
692 name_len = strlen(userkctl->ctl_name);
693 if (name_len == 0 || name_len + 1 > MAX_KCTL_NAME)
694 return(EINVAL);
695
696 MALLOC(kctl, struct kctl *, sizeof(*kctl), M_TEMP, M_WAITOK);
697 if (kctl == NULL)
698 return(ENOMEM);
699 bzero((char *)kctl, sizeof(*kctl));
700
701 lck_mtx_lock(ctl_mtx);
702
703 if ((userkctl->ctl_flags & CTL_FLAG_REG_ID_UNIT) == 0) {
704 if (ctl_find_by_name(userkctl->ctl_name) != NULL) {
705 lck_mtx_unlock(ctl_mtx);
706 FREE(kctl, M_TEMP);
707 return(EEXIST);
708 }
709 for (n = 0, id = ctl_last_id + 1; n < ctl_max; id++, n++) {
710 if (id == 0) {
711 n--;
712 continue;
713 }
714 if (ctl_find_by_id(id) == 0)
715 break;
716 }
717 if (id == ctl_max) {
718 lck_mtx_unlock(ctl_mtx);
719 FREE(kctl, M_TEMP);
720 return(ENOBUFS);
721 }
722 userkctl->ctl_id =id;
723 kctl->id = id;
724 kctl->reg_unit = -1;
725 } else {
726 if (ctl_find_by_id_unit(userkctl->ctl_id, userkctl->ctl_unit) != NULL) {
727 lck_mtx_unlock(ctl_mtx);
728 FREE(kctl, M_TEMP);
729 return(EEXIST);
730 }
731 kctl->id = userkctl->ctl_id;
732 kctl->reg_unit = userkctl->ctl_unit;
733 }
734 strcpy(kctl->name, userkctl->ctl_name);
735 kctl->flags = userkctl->ctl_flags;
736
737 /* Let the caller know the default send and receive sizes */
738 if (userkctl->ctl_sendsize == 0)
739 userkctl->ctl_sendsize = CTL_SENDSIZE;
740 kctl->sendbufsize = userkctl->ctl_sendsize;
741
742 if (userkctl->ctl_recvsize == 0)
743 userkctl->ctl_recvsize = CTL_RECVSIZE;
744 kctl->recvbufsize = userkctl->ctl_recvsize;
745
746 kctl->connect = userkctl->ctl_connect;
747 kctl->disconnect = userkctl->ctl_disconnect;
748 kctl->send = userkctl->ctl_send;
749 kctl->setopt = userkctl->ctl_setopt;
750 kctl->getopt = userkctl->ctl_getopt;
751
752 TAILQ_INIT(&kctl->kcb_head);
753
754 TAILQ_INSERT_TAIL(&ctl_head, kctl, next);
755 ctl_max++;
756
757 lck_mtx_unlock(ctl_mtx);
758
759 *kctlref = kctl;
760
761 ctl_post_msg(KEV_CTL_REGISTERED, kctl->id);
762 return(0);
763 }
764
765 errno_t
766 ctl_deregister(void *kctlref)
767 {
768 struct kctl *kctl;
769
770 if (kctlref == NULL) /* sanity check */
771 return(EINVAL);
772
773 lck_mtx_lock(ctl_mtx);
774 TAILQ_FOREACH(kctl, &ctl_head, next) {
775 if (kctl == (struct kctl *)kctlref)
776 break;
777 }
778 if (kctl != (struct kctl *)kctlref) {
779 lck_mtx_unlock(ctl_mtx);
780 return EINVAL;
781 }
782 if (!TAILQ_EMPTY(&kctl->kcb_head)) {
783 lck_mtx_unlock(ctl_mtx);
784 return EBUSY;
785 }
786
787 TAILQ_REMOVE(&ctl_head, kctl, next);
788 ctl_max--;
789
790 lck_mtx_unlock(ctl_mtx);
791
792 ctl_post_msg(KEV_CTL_DEREGISTERED, kctl->id);
793 FREE(kctl, M_TEMP);
794 return(0);
795 }
796
797 /*
798 * Must be called with global lock taked
799 */
800 static struct kctl *
801 ctl_find_by_id(u_int32_t id)
802 {
803 struct kctl *kctl;
804
805 TAILQ_FOREACH(kctl, &ctl_head, next)
806 if (kctl->id == id)
807 return kctl;
808
809 return NULL;
810 }
811
812 /*
813 * Must be called with global ctl_mtx lock taked
814 */
815 static struct kctl *
816 ctl_find_by_name(const char *name)
817 {
818 struct kctl *kctl;
819
820 TAILQ_FOREACH(kctl, &ctl_head, next)
821 if (strcmp(kctl->name, name) == 0)
822 return kctl;
823
824 return NULL;
825 }
826
827 /*
828 * Must be called with global ctl_mtx lock taked
829 *
830 */
831 static struct kctl *
832 ctl_find_by_id_unit(u_int32_t id, u_int32_t unit)
833 {
834 struct kctl *kctl;
835
836 TAILQ_FOREACH(kctl, &ctl_head, next) {
837 if (kctl->id == id && (kctl->flags & CTL_FLAG_REG_ID_UNIT) == 0)
838 return kctl;
839 else if (kctl->id == id && kctl->reg_unit == unit)
840 return kctl;
841 }
842 return NULL;
843 }
844
845 /*
846 * Must be called with kernel controller lock taken
847 */
848 static struct ctl_cb *
849 kcb_find(struct kctl *kctl, u_int32_t unit)
850 {
851 struct ctl_cb *kcb;
852
853 TAILQ_FOREACH(kcb, &kctl->kcb_head, next)
854 if ((kcb->unit == unit))
855 return kcb;
856
857 return NULL;
858 }
859
860 /*
861 * Must be called witout lock
862 */
863 static void
864 ctl_post_msg(u_long event_code, u_int32_t id)
865 {
866 struct ctl_event_data ctl_ev_data;
867 struct kev_msg ev_msg;
868
869 ev_msg.vendor_code = KEV_VENDOR_APPLE;
870
871 ev_msg.kev_class = KEV_SYSTEM_CLASS;
872 ev_msg.kev_subclass = KEV_CTL_SUBCLASS;
873 ev_msg.event_code = event_code;
874
875 /* common nke subclass data */
876 bzero(&ctl_ev_data, sizeof(ctl_ev_data));
877 ctl_ev_data.ctl_id = id;
878 ev_msg.dv[0].data_ptr = &ctl_ev_data;
879 ev_msg.dv[0].data_length = sizeof(ctl_ev_data);
880
881 ev_msg.dv[1].data_length = 0;
882
883 kev_post_msg(&ev_msg);
884 }
885
886 static int
887 ctl_lock(struct socket *so, int refcount, int lr)
888 {
889 int lr_saved;
890 if (lr == 0)
891 lr_saved = (unsigned int) __builtin_return_address(0);
892 else lr_saved = lr;
893
894 if (so->so_pcb) {
895 lck_mtx_lock(((struct ctl_cb *)so->so_pcb)->mtx);
896 } else {
897 panic("ctl_lock: so=%x NO PCB! lr=%x\n", so, lr_saved);
898 lck_mtx_lock(so->so_proto->pr_domain->dom_mtx);
899 }
900
901 if (so->so_usecount < 0)
902 panic("ctl_lock: so=%x so_pcb=%x lr=%x ref=%x\n",
903 so, so->so_pcb, lr_saved, so->so_usecount);
904
905 if (refcount)
906 so->so_usecount++;
907
908 so->lock_lr[so->next_lock_lr] = (void *)lr_saved;
909 so->next_lock_lr = (so->next_lock_lr+1) % SO_LCKDBG_MAX;
910 return (0);
911 }
912
913 static int
914 ctl_unlock(struct socket *so, int refcount, int lr)
915 {
916 int lr_saved;
917 lck_mtx_t * mutex_held;
918
919 if (lr == 0)
920 lr_saved = (unsigned int) __builtin_return_address(0);
921 else lr_saved = lr;
922
923 #ifdef MORE_KCTLLOCK_DEBUG
924 printf("ctl_unlock: so=%x sopcb=%x lock=%x ref=%x lr=%x\n",
925 so, so->so_pcb, ((struct ctl_cb *)so->so_pcb)->mtx, so->so_usecount, lr_saved);
926 #endif
927 if (refcount)
928 so->so_usecount--;
929
930 if (so->so_usecount < 0)
931 panic("ctl_unlock: so=%x usecount=%x\n", so, so->so_usecount);
932 if (so->so_pcb == NULL) {
933 panic("ctl_unlock: so=%x NO PCB usecount=%x lr=%x\n", so, so->so_usecount, lr_saved);
934 mutex_held = so->so_proto->pr_domain->dom_mtx;
935 } else {
936 mutex_held = ((struct ctl_cb *)so->so_pcb)->mtx;
937 }
938 lck_mtx_assert(mutex_held, LCK_MTX_ASSERT_OWNED);
939 so->unlock_lr[so->next_unlock_lr] = (void *)lr_saved;
940 so->next_unlock_lr = (so->next_unlock_lr+1) % SO_LCKDBG_MAX;
941 lck_mtx_unlock(mutex_held);
942
943 if (so->so_usecount == 0)
944 ctl_sofreelastref(so);
945
946 return (0);
947 }
948
949 static lck_mtx_t *
950 ctl_getlock(struct socket *so, __unused int locktype)
951 {
952 struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
953
954 if (so->so_pcb) {
955 if (so->so_usecount < 0)
956 panic("ctl_getlock: so=%x usecount=%x\n", so, so->so_usecount);
957 return(kcb->mtx);
958 } else {
959 panic("ctl_getlock: so=%x NULL so_pcb\n", so);
960 return (so->so_proto->pr_domain->dom_mtx);
961 }
962 }