+static int
+ctl_unlock(struct socket *so, int refcount, void *lr)
+{
+ void *lr_saved;
+ lck_mtx_t *mutex_held;
+
+ if (lr == NULL) {
+ lr_saved = __builtin_return_address(0);
+ } else {
+ lr_saved = lr;
+ }
+
+#if (MORE_KCTLLOCK_DEBUG && (DEVELOPMENT || DEBUG))
+ printf("ctl_unlock: so=%llx sopcb=%x lock=%llx ref=%u lr=%llx\n",
+ (uint64_t)VM_KERNEL_ADDRPERM(so),
+ (uint64_t)VM_KERNEL_ADDRPERM(so->so_pcb,
+ (uint64_t)VM_KERNEL_ADDRPERM(&((struct ctl_cb *)so->so_pcb)->mtx),
+ so->so_usecount, (uint64_t)VM_KERNEL_ADDRPERM(lr_saved));
+#endif /* (MORE_KCTLLOCK_DEBUG && (DEVELOPMENT || DEBUG)) */
+ if (refcount) {
+ so->so_usecount--;
+ }
+
+ if (so->so_usecount < 0) {
+ panic("ctl_unlock: so=%p usecount=%x lrh= %s\n",
+ so, so->so_usecount, solockhistory_nr(so));
+ /* NOTREACHED */
+ }
+ if (so->so_pcb == NULL) {
+ panic("ctl_unlock: so=%p NO PCB usecount=%x lr=%p lrh= %s\n",
+ so, so->so_usecount, (void *)lr_saved,
+ solockhistory_nr(so));
+ /* NOTREACHED */
+ }
+ mutex_held = &((struct ctl_cb *)so->so_pcb)->mtx;
+
+ lck_mtx_assert(mutex_held, LCK_MTX_ASSERT_OWNED);
+ so->unlock_lr[so->next_unlock_lr] = lr_saved;
+ so->next_unlock_lr = (so->next_unlock_lr + 1) % SO_LCKDBG_MAX;
+ lck_mtx_unlock(mutex_held);
+
+ if (so->so_usecount == 0) {
+ ctl_sofreelastref(so);
+ }
+
+ return 0;
+}
+
+static lck_mtx_t *
+ctl_getlock(struct socket *so, int flags)
+{
+#pragma unused(flags)
+ struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
+
+ if (so->so_pcb) {
+ if (so->so_usecount < 0) {
+ panic("ctl_getlock: so=%p usecount=%x lrh= %s\n",
+ so, so->so_usecount, solockhistory_nr(so));
+ }
+ return &kcb->mtx;
+ } else {
+ panic("ctl_getlock: so=%p NULL NO so_pcb %s\n",
+ so, solockhistory_nr(so));
+ return so->so_proto->pr_domain->dom_mtx;
+ }
+}
+
+__private_extern__ int
+kctl_reg_list SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error = 0;
+ u_int64_t i, n;
+ struct xsystmgen xsg;
+ void *buf = NULL;
+ struct kctl *kctl;
+ size_t item_size = ROUNDUP64(sizeof(struct xkctl_reg));
+
+ buf = kheap_alloc(KHEAP_TEMP, item_size, Z_WAITOK | Z_ZERO);
+ if (buf == NULL) {
+ return ENOMEM;
+ }
+
+ lck_mtx_lock(&ctl_mtx);
+
+ n = kctlstat.kcs_reg_count;
+
+ if (req->oldptr == USER_ADDR_NULL) {
+ req->oldidx = (size_t)(n + n / 8) * sizeof(struct xkctl_reg);
+ goto done;
+ }
+ if (req->newptr != USER_ADDR_NULL) {
+ error = EPERM;
+ goto done;
+ }
+ bzero(&xsg, sizeof(xsg));
+ xsg.xg_len = sizeof(xsg);
+ xsg.xg_count = n;
+ xsg.xg_gen = kctlstat.kcs_gencnt;
+ xsg.xg_sogen = so_gencnt;
+ error = SYSCTL_OUT(req, &xsg, sizeof(xsg));
+ if (error) {
+ goto done;
+ }
+ /*
+ * We are done if there is no pcb
+ */
+ if (n == 0) {
+ goto done;
+ }
+
+ for (i = 0, kctl = TAILQ_FIRST(&ctl_head);
+ i < n && kctl != NULL;
+ i++, kctl = TAILQ_NEXT(kctl, next)) {
+ struct xkctl_reg *xkr = (struct xkctl_reg *)buf;
+ struct ctl_cb *kcb;
+ u_int32_t pcbcount = 0;
+
+ TAILQ_FOREACH(kcb, &kctl->kcb_head, next)
+ pcbcount++;
+
+ bzero(buf, item_size);
+
+ xkr->xkr_len = sizeof(struct xkctl_reg);
+ xkr->xkr_kind = XSO_KCREG;
+ xkr->xkr_id = kctl->id;
+ xkr->xkr_reg_unit = kctl->reg_unit;
+ xkr->xkr_flags = kctl->flags;
+ xkr->xkr_kctlref = (uint64_t)(kctl->kctlref);
+ xkr->xkr_recvbufsize = kctl->recvbufsize;
+ xkr->xkr_sendbufsize = kctl->sendbufsize;
+ xkr->xkr_lastunit = kctl->lastunit;
+ xkr->xkr_pcbcount = pcbcount;
+ xkr->xkr_connect = (uint64_t)VM_KERNEL_UNSLIDE(kctl->connect);
+ xkr->xkr_disconnect =
+ (uint64_t)VM_KERNEL_UNSLIDE(kctl->disconnect);
+ xkr->xkr_send = (uint64_t)VM_KERNEL_UNSLIDE(kctl->send);
+ xkr->xkr_send_list =
+ (uint64_t)VM_KERNEL_UNSLIDE(kctl->send_list);
+ xkr->xkr_setopt = (uint64_t)VM_KERNEL_UNSLIDE(kctl->setopt);
+ xkr->xkr_getopt = (uint64_t)VM_KERNEL_UNSLIDE(kctl->getopt);
+ xkr->xkr_rcvd = (uint64_t)VM_KERNEL_UNSLIDE(kctl->rcvd);
+ strlcpy(xkr->xkr_name, kctl->name, sizeof(xkr->xkr_name));
+
+ error = SYSCTL_OUT(req, buf, item_size);
+ }
+
+ if (error == 0) {
+ /*
+ * Give the user an updated idea of our state.
+ * If the generation differs from what we told
+ * her before, she knows that something happened
+ * while we were processing this request, and it
+ * might be necessary to retry.
+ */
+ bzero(&xsg, sizeof(xsg));
+ xsg.xg_len = sizeof(xsg);
+ xsg.xg_count = n;
+ xsg.xg_gen = kctlstat.kcs_gencnt;
+ xsg.xg_sogen = so_gencnt;
+ error = SYSCTL_OUT(req, &xsg, sizeof(xsg));
+ if (error) {
+ goto done;
+ }
+ }
+
+done:
+ lck_mtx_unlock(&ctl_mtx);
+
+ kheap_free(KHEAP_TEMP, buf, item_size);
+
+ return error;
+}
+
+__private_extern__ int
+kctl_pcblist SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error = 0;
+ u_int64_t n, i;
+ struct xsystmgen xsg;
+ void *buf = NULL;
+ struct kctl *kctl;
+ size_t item_size = ROUNDUP64(sizeof(struct xkctlpcb)) +
+ ROUNDUP64(sizeof(struct xsocket_n)) +
+ 2 * ROUNDUP64(sizeof(struct xsockbuf_n)) +
+ ROUNDUP64(sizeof(struct xsockstat_n));
+
+ buf = kheap_alloc(KHEAP_TEMP, item_size, Z_WAITOK | Z_ZERO);
+ if (buf == NULL) {
+ return ENOMEM;
+ }
+
+ lck_mtx_lock(&ctl_mtx);
+
+ n = kctlstat.kcs_pcbcount;
+
+ if (req->oldptr == USER_ADDR_NULL) {
+ req->oldidx = (size_t)(n + n / 8) * item_size;
+ goto done;
+ }
+ if (req->newptr != USER_ADDR_NULL) {
+ error = EPERM;
+ goto done;
+ }
+ bzero(&xsg, sizeof(xsg));
+ xsg.xg_len = sizeof(xsg);
+ xsg.xg_count = n;
+ xsg.xg_gen = kctlstat.kcs_gencnt;
+ xsg.xg_sogen = so_gencnt;
+ error = SYSCTL_OUT(req, &xsg, sizeof(xsg));
+ if (error) {
+ goto done;
+ }
+ /*
+ * We are done if there is no pcb
+ */
+ if (n == 0) {
+ goto done;
+ }
+
+ for (i = 0, kctl = TAILQ_FIRST(&ctl_head);
+ i < n && kctl != NULL;
+ kctl = TAILQ_NEXT(kctl, next)) {
+ struct ctl_cb *kcb;
+
+ for (kcb = TAILQ_FIRST(&kctl->kcb_head);
+ i < n && kcb != NULL;
+ i++, kcb = TAILQ_NEXT(kcb, next)) {
+ struct xkctlpcb *xk = (struct xkctlpcb *)buf;
+ struct xsocket_n *xso = (struct xsocket_n *)
+ ADVANCE64(xk, sizeof(*xk));
+ struct xsockbuf_n *xsbrcv = (struct xsockbuf_n *)
+ ADVANCE64(xso, sizeof(*xso));
+ struct xsockbuf_n *xsbsnd = (struct xsockbuf_n *)
+ ADVANCE64(xsbrcv, sizeof(*xsbrcv));
+ struct xsockstat_n *xsostats = (struct xsockstat_n *)
+ ADVANCE64(xsbsnd, sizeof(*xsbsnd));
+
+ bzero(buf, item_size);
+
+ xk->xkp_len = sizeof(struct xkctlpcb);
+ xk->xkp_kind = XSO_KCB;
+ xk->xkp_unit = kcb->sac.sc_unit;
+ xk->xkp_kctpcb = (uint64_t)VM_KERNEL_ADDRPERM(kcb);
+ xk->xkp_kctlref = (uint64_t)VM_KERNEL_ADDRPERM(kctl);
+ xk->xkp_kctlid = kctl->id;
+ strlcpy(xk->xkp_kctlname, kctl->name,
+ sizeof(xk->xkp_kctlname));
+
+ sotoxsocket_n(kcb->so, xso);
+ sbtoxsockbuf_n(kcb->so ?
+ &kcb->so->so_rcv : NULL, xsbrcv);
+ sbtoxsockbuf_n(kcb->so ?
+ &kcb->so->so_snd : NULL, xsbsnd);
+ sbtoxsockstat_n(kcb->so, xsostats);
+
+ error = SYSCTL_OUT(req, buf, item_size);
+ }
+ }
+
+ if (error == 0) {
+ /*
+ * Give the user an updated idea of our state.
+ * If the generation differs from what we told
+ * her before, she knows that something happened
+ * while we were processing this request, and it
+ * might be necessary to retry.
+ */
+ bzero(&xsg, sizeof(xsg));
+ xsg.xg_len = sizeof(xsg);
+ xsg.xg_count = n;
+ xsg.xg_gen = kctlstat.kcs_gencnt;
+ xsg.xg_sogen = so_gencnt;
+ error = SYSCTL_OUT(req, &xsg, sizeof(xsg));
+ if (error) {
+ goto done;
+ }
+ }
+
+done:
+ lck_mtx_unlock(&ctl_mtx);
+
+ kheap_free(KHEAP_TEMP, buf, item_size);
+ return error;
+}
+
+int
+kctl_getstat SYSCTL_HANDLER_ARGS
+{
+#pragma unused(oidp, arg1, arg2)
+ int error = 0;
+
+ lck_mtx_lock(&ctl_mtx);
+
+ if (req->newptr != USER_ADDR_NULL) {
+ error = EPERM;
+ goto done;
+ }
+ if (req->oldptr == USER_ADDR_NULL) {
+ req->oldidx = sizeof(struct kctlstat);
+ goto done;
+ }
+
+ error = SYSCTL_OUT(req, &kctlstat,
+ MIN(sizeof(struct kctlstat), req->oldlen));
+done:
+ lck_mtx_unlock(&ctl_mtx);
+ return error;
+}
+
+void
+kctl_fill_socketinfo(struct socket *so, struct socket_info *si)
+{
+ struct ctl_cb *kcb = (struct ctl_cb *)so->so_pcb;
+ struct kern_ctl_info *kcsi =
+ &si->soi_proto.pri_kern_ctl;
+ struct kctl *kctl = kcb->kctl;
+
+ si->soi_kind = SOCKINFO_KERN_CTL;
+
+ if (kctl == 0) {
+ return;
+ }
+
+ kcsi->kcsi_id = kctl->id;
+ kcsi->kcsi_reg_unit = kctl->reg_unit;
+ kcsi->kcsi_flags = kctl->flags;
+ kcsi->kcsi_recvbufsize = kctl->recvbufsize;
+ kcsi->kcsi_sendbufsize = kctl->sendbufsize;
+ kcsi->kcsi_unit = kcb->sac.sc_unit;
+ strlcpy(kcsi->kcsi_name, kctl->name, MAX_KCTL_NAME);
+}