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