]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_control.c
xnu-1699.32.7.tar.gz
[apple/xnu.git] / bsd / kern / kern_control.c
index 877fe67785b8021ab45ec4dbfb7e54f67711964f..92b15bc408655398d24ba2f7378e772ffc918418 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 1999-2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2008 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -52,7 +52,6 @@
 #include <net/if_var.h>
 
 #include <mach/vm_types.h>
 #include <net/if_var.h>
 
 #include <mach/vm_types.h>
-#include <mach/kmod.h>
 
 #include <kern/thread.h>
 
 
 #include <kern/thread.h>
 
@@ -92,11 +91,12 @@ static int ctl_peeraddr(struct socket *so, struct sockaddr **nam);
 static struct kctl *ctl_find_by_name(const char *);
 static struct kctl *ctl_find_by_id_unit(u_int32_t id, u_int32_t unit);
 
 static struct kctl *ctl_find_by_name(const char *);
 static struct kctl *ctl_find_by_id_unit(u_int32_t id, u_int32_t unit);
 
+static struct socket *kcb_find_socket(struct kctl *, u_int32_t unit);
 static struct ctl_cb *kcb_find(struct kctl *, u_int32_t unit);
 static struct ctl_cb *kcb_find(struct kctl *, u_int32_t unit);
-static void ctl_post_msg(u_long event_code, u_int32_t id);
+static void ctl_post_msg(u_int32_t event_code, u_int32_t id);
 
 
-static int ctl_lock(struct socket *, int, int);
-static int ctl_unlock(struct socket *, int, int);
+static int ctl_lock(struct socket *, int, void *);
+static int ctl_unlock(struct socket *, int, void *);
 static lck_mtx_t * ctl_getlock(struct socket *, int);
 
 static struct pr_usrreqs ctl_usrreqs =
 static lck_mtx_t * ctl_getlock(struct socket *, int);
 
 static struct pr_usrreqs ctl_usrreqs =
@@ -256,7 +256,7 @@ ctl_sofreelastref(struct socket *so)
         if ((kctl = kcb->kctl) != 0) {
             lck_mtx_lock(ctl_mtx);
             TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
         if ((kctl = kcb->kctl) != 0) {
             lck_mtx_lock(ctl_mtx);
             TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
-            lck_mtx_lock(ctl_mtx);
+            lck_mtx_unlock(ctl_mtx);
        }
        kcb_delete(kcb);
     }
        }
        kcb_delete(kcb);
     }
@@ -365,10 +365,16 @@ ctl_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p)
     error = (*kctl->connect)(kctl, &sa, &kcb->userdata);
        socket_lock(so, 0);
     if (error)
     error = (*kctl->connect)(kctl, &sa, &kcb->userdata);
        socket_lock(so, 0);
     if (error)
-               goto done;
+               goto end;
     
     soisconnected(so);
 
     
     soisconnected(so);
 
+end:
+       if (error && kctl->disconnect) {
+               socket_unlock(so, 0);
+               (*kctl->disconnect)(kctl, kcb->unit, kcb->userdata);
+               socket_lock(so, 0);
+       }
 done:
     if (error) {
         soisdisconnected(so);
 done:
     if (error) {
         soisdisconnected(so);
@@ -394,12 +400,19 @@ ctl_disconnect(struct socket *so)
             (*kctl->disconnect)(kctl, kcb->unit, kcb->userdata);
             socket_lock(so, 0);
         }
             (*kctl->disconnect)(kctl, kcb->unit, kcb->userdata);
             socket_lock(so, 0);
         }
+        
+        soisdisconnected(so);
+        
+               socket_unlock(so, 0);
         lck_mtx_lock(ctl_mtx);
         kcb->kctl = 0;
        kcb->unit = 0;
         lck_mtx_lock(ctl_mtx);
         kcb->kctl = 0;
        kcb->unit = 0;
+       while (kcb->usecount != 0) {
+               msleep(&kcb->usecount, ctl_mtx, 0, "kcb->usecount", 0);
+       }
         TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
         TAILQ_REMOVE(&kctl->kcb_head, kcb, next);
-        soisdisconnected(so);
         lck_mtx_unlock(ctl_mtx);
         lck_mtx_unlock(ctl_mtx);
+               socket_lock(so, 0);
     }
     return 0;
 }
     }
     return 0;
 }
@@ -431,23 +444,29 @@ ctl_peeraddr(struct socket *so, struct sockaddr **nam)
 
 static int
 ctl_send(struct socket *so, int flags, struct mbuf *m,
 
 static int
 ctl_send(struct socket *so, int flags, struct mbuf *m,
-            __unused struct sockaddr *addr, __unused struct mbuf *control,
+            __unused struct sockaddr *addr, struct mbuf *control,
             __unused struct proc *p)
 {
        int             error = 0;
        struct ctl_cb   *kcb = (struct ctl_cb *)so->so_pcb;
        struct kctl             *kctl;
        
             __unused struct proc *p)
 {
        int             error = 0;
        struct ctl_cb   *kcb = (struct ctl_cb *)so->so_pcb;
        struct kctl             *kctl;
        
+       if (control) m_freem(control);
+       
        if (kcb == NULL)        /* sanity check */
        if (kcb == NULL)        /* sanity check */
-               return(ENOTCONN);
+               error = ENOTCONN;
        
        
-       if ((kctl = kcb->kctl) == NULL)
-               return(EINVAL);
+       if (error == 0 && (kctl = kcb->kctl) == NULL)
+               error = EINVAL;
                
                
-       if (kctl->send) {
+       if (error == 0 && kctl->send) {
                socket_unlock(so, 0);
                error = (*kctl->send)(kctl, kcb->unit, kcb->userdata, m, flags);
                socket_lock(so, 0);
                socket_unlock(so, 0);
                error = (*kctl->send)(kctl, kcb->unit, kcb->userdata, m, flags);
                socket_lock(so, 0);
+       } else {
+               m_freem(m);
+               if (error == 0)
+                       error = ENOTSUP;
        }
        return error;
 }
        }
        return error;
 }
@@ -455,23 +474,18 @@ ctl_send(struct socket *so, int flags, struct mbuf *m,
 errno_t
 ctl_enqueuembuf(void *kctlref, u_int32_t unit, struct mbuf *m, u_int32_t flags)
 {
 errno_t
 ctl_enqueuembuf(void *kctlref, u_int32_t unit, struct mbuf *m, u_int32_t flags)
 {
-       struct ctl_cb   *kcb;
        struct socket   *so;
        errno_t                 error = 0;
        struct kctl             *kctl = (struct kctl *)kctlref;
        
        if (kctl == NULL)
                return EINVAL;
        struct socket   *so;
        errno_t                 error = 0;
        struct kctl             *kctl = (struct kctl *)kctlref;
        
        if (kctl == NULL)
                return EINVAL;
-               
-       kcb = kcb_find(kctl, unit);
-       if (kcb == NULL)
-               return EINVAL;
        
        
-       so = (struct socket *)kcb->so;
-       if (so == NULL) 
+       so = kcb_find_socket(kctl, unit);
+       
+       if (so == NULL)
                return EINVAL;
        
                return EINVAL;
        
-       socket_lock(so, 1);
        if (sbspace(&so->so_rcv) < m->m_pkthdr.len) {
                error = ENOBUFS;
                goto bye;
        if (sbspace(&so->so_rcv) < m->m_pkthdr.len) {
                error = ENOBUFS;
                goto bye;
@@ -488,7 +502,6 @@ bye:
 errno_t
 ctl_enqueuedata(void *kctlref, u_int32_t unit, void *data, size_t len, u_int32_t flags)
 {
 errno_t
 ctl_enqueuedata(void *kctlref, u_int32_t unit, void *data, size_t len, u_int32_t flags)
 {
-       struct ctl_cb   *kcb;
        struct socket   *so;
        struct mbuf     *m;
        errno_t                 error = 0;
        struct socket   *so;
        struct mbuf     *m;
        errno_t                 error = 0;
@@ -500,16 +513,11 @@ ctl_enqueuedata(void *kctlref, u_int32_t unit, void *data, size_t len, u_int32_t
        if (kctlref == NULL)
                return EINVAL;
                
        if (kctlref == NULL)
                return EINVAL;
                
-       kcb = kcb_find(kctl, unit);
-       if (kcb == NULL)
-               return EINVAL;
-       
-       so = (struct socket *)kcb->so;
-       if (so == NULL) 
+       so = kcb_find_socket(kctl, unit);
+       if (so == NULL)
                return EINVAL;
        
                return EINVAL;
        
-       socket_lock(so, 1);
-       if (sbspace(&so->so_rcv) < (long)len) {
+       if (sbspace(&so->so_rcv) < (int)len) {
                error = ENOBUFS;
                goto bye;
        }
                error = ENOBUFS;
                goto bye;
        }
@@ -546,27 +554,21 @@ bye:
 errno_t 
 ctl_getenqueuespace(kern_ctl_ref kctlref, u_int32_t unit, size_t *space)
 {
 errno_t 
 ctl_getenqueuespace(kern_ctl_ref kctlref, u_int32_t unit, size_t *space)
 {
-       struct ctl_cb   *kcb;
        struct kctl             *kctl = (struct kctl *)kctlref;
        struct socket   *so;
        long avail;
        
        if (kctlref == NULL || space == NULL)
                return EINVAL;
        struct kctl             *kctl = (struct kctl *)kctlref;
        struct socket   *so;
        long avail;
        
        if (kctlref == NULL || space == NULL)
                return EINVAL;
-               
-       kcb = kcb_find(kctl, unit);
-       if (kcb == NULL)
-               return EINVAL;
        
        
-       so = (struct socket *)kcb->so;
-       if (so == NULL) 
+       so = kcb_find_socket(kctl, unit);
+       if (so == NULL)
                return EINVAL;
        
                return EINVAL;
        
-       socket_lock(so, 1);
        avail = sbspace(&so->so_rcv);
        *space = (avail < 0) ? 0 : avail;
        socket_unlock(so, 1);
        avail = sbspace(&so->so_rcv);
        *space = (avail < 0) ? 0 : avail;
        socket_unlock(so, 1);
-
+       
        return 0;
 }
 
        return 0;
 }
 
@@ -625,6 +627,9 @@ ctl_ctloutput(struct socket *so, struct sockopt *sopt)
                        socket_unlock(so, 0);
                        error = (*kctl->getopt)(kcb->kctl, kcb->unit, kcb->userdata, sopt->sopt_name, 
                                                data, &len);
                        socket_unlock(so, 0);
                        error = (*kctl->getopt)(kcb->kctl, kcb->unit, kcb->userdata, sopt->sopt_name, 
                                                data, &len);
+                       if (data != NULL && len > sopt->sopt_valsize)
+                               panic_plain("ctl_ctloutput: ctl %s returned len (%lu) > sopt_valsize (%lu)\n",
+                                       kcb->kctl->name, len, sopt->sopt_valsize);
                        socket_lock(so, 0);    
                        if (error == 0) {
                                if (data != NULL)
                        socket_lock(so, 0);    
                        if (error == 0) {
                                if (data != NULL)
@@ -859,6 +864,46 @@ ctl_find_by_name(const char *name)
     return NULL;
 }
 
     return NULL;
 }
 
+u_int32_t
+ctl_id_by_name(const char *name)
+{
+       u_int32_t       ctl_id = 0;
+       
+       lck_mtx_lock(ctl_mtx);
+       struct kctl *kctl = ctl_find_by_name(name);
+       if (kctl) ctl_id = kctl->id;
+       lck_mtx_unlock(ctl_mtx);
+       
+       return ctl_id;
+}
+
+errno_t
+ctl_name_by_id(
+       u_int32_t id,
+       char    *out_name,
+       size_t  maxsize)
+{
+       int             found = 0;
+       
+       lck_mtx_lock(ctl_mtx);
+       struct kctl *kctl;
+    TAILQ_FOREACH(kctl, &ctl_head, next) {
+        if (kctl->id == id)
+            break;
+    }
+    
+    if (kctl && kctl->name)
+    {
+       if (maxsize > MAX_KCTL_NAME)
+               maxsize = MAX_KCTL_NAME;
+       strlcpy(out_name, kctl->name, maxsize);
+       found = 1;
+    }
+       lck_mtx_unlock(ctl_mtx);
+       
+       return found ? 0 : ENOENT;
+}
+
 /*
  * Must be called with global ctl_mtx lock taked
  *
 /*
  * Must be called with global ctl_mtx lock taked
  *
@@ -886,21 +931,58 @@ kcb_find(struct kctl *kctl, u_int32_t unit)
     struct ctl_cb      *kcb;
 
     TAILQ_FOREACH(kcb, &kctl->kcb_head, next)
     struct ctl_cb      *kcb;
 
     TAILQ_FOREACH(kcb, &kctl->kcb_head, next)
-        if ((kcb->unit == unit))
+        if (kcb->unit == unit)
             return kcb;
 
     return NULL;
 }
 
             return kcb;
 
     return NULL;
 }
 
-/*
- * Must be called witout lock
- */
+static struct socket *
+kcb_find_socket(struct kctl *kctl, u_int32_t unit)
+{
+       struct socket *so = NULL;
+       
+       lck_mtx_lock(ctl_mtx);
+       struct ctl_cb   *kcb = kcb_find(kctl, unit);
+       if (kcb && kcb->kctl == kctl) {
+               so = kcb->so;
+               if (so) {
+                       kcb->usecount++;
+               }
+       }
+       lck_mtx_unlock(ctl_mtx);
+       
+       if (so == NULL) {
+               return NULL;
+       }
+       
+       socket_lock(so, 1);
+       
+       lck_mtx_lock(ctl_mtx);
+       if (kcb->kctl == NULL)
+       {
+               lck_mtx_unlock(ctl_mtx);
+               socket_unlock(so, 1);
+               so = NULL;
+               lck_mtx_lock(ctl_mtx);
+       }
+       kcb->usecount--;
+       if (kcb->usecount == 0)
+               wakeup((event_t)&kcb->usecount);
+       lck_mtx_unlock(ctl_mtx);
+       
+       return so;
+}
+
 static void 
 static void 
-ctl_post_msg(u_long event_code, u_int32_t id) 
+ctl_post_msg(u_int32_t event_code, u_int32_t id) 
 {
     struct ctl_event_data      ctl_ev_data;
     struct kev_msg             ev_msg;
     
 {
     struct ctl_event_data      ctl_ev_data;
     struct kev_msg             ev_msg;
     
+    lck_mtx_assert(ctl_mtx, LCK_MTX_ASSERT_NOTOWNED);
+   
+    bzero(&ev_msg, sizeof(struct kev_msg)); 
     ev_msg.vendor_code    = KEV_VENDOR_APPLE;
     
     ev_msg.kev_class      = KEV_SYSTEM_CLASS;
     ev_msg.vendor_code    = KEV_VENDOR_APPLE;
     
     ev_msg.kev_class      = KEV_SYSTEM_CLASS;
@@ -919,24 +1001,29 @@ ctl_post_msg(u_long event_code, u_int32_t id)
 }
 
 static int
 }
 
 static int
-ctl_lock(struct socket *so, int refcount, int lr)
- {
-       uint32_t lr_saved;
-       if (lr == 0) 
-               lr_saved = (unsigned int) __builtin_return_address(0);
-       else lr_saved = lr;
-       
-       if (so->so_pcb) {
+ctl_lock(struct socket *so, int refcount, void *lr)
+{
+       void *lr_saved;
+
+       if (lr == NULL)
+               lr_saved = __builtin_return_address(0);
+       else
+               lr_saved = lr;
+
+       if (so->so_pcb != NULL) {
                lck_mtx_lock(((struct ctl_cb *)so->so_pcb)->mtx);
        } else  {
                lck_mtx_lock(((struct ctl_cb *)so->so_pcb)->mtx);
        } else  {
-               panic("ctl_lock: so=%p NO PCB! lr=%x\n", so, lr_saved);
-               lck_mtx_lock(so->so_proto->pr_domain->dom_mtx);
+               panic("ctl_lock: so=%p NO PCB! lr=%p lrh= %s\n", 
+                   so, lr_saved, solockhistory_nr(so));
+               /* NOTREACHED */
        }
        }
-       
-       if (so->so_usecount < 0)
-               panic("ctl_lock: so=%p so_pcb=%p lr=%x ref=%x\n",
-               so, so->so_pcb, lr_saved, so->so_usecount);
-       
+
+       if (so->so_usecount < 0) {
+               panic("ctl_lock: so=%p so_pcb=%p lr=%p ref=%x lrh= %s\n",
+                   so, so->so_pcb, lr_saved, so->so_usecount, solockhistory_nr(so));
+               /* NOTREACHED */
+       }
+
        if (refcount)
                so->so_usecount++;
 
        if (refcount)
                so->so_usecount++;
 
@@ -946,38 +1033,44 @@ ctl_lock(struct socket *so, int refcount, int lr)
 }
 
 static int
 }
 
 static int
-ctl_unlock(struct socket *so, int refcount, int lr)
+ctl_unlock(struct socket *so, int refcount, void *lr)
 {
 {
-       uint32_t lr_saved;
-       lck_mtx_t * mutex_held;
-       
-       if (lr == 0) 
-               lr_saved = (unsigned int) __builtin_return_address(0);
-       else lr_saved = lr;
-       
+       void *lr_saved;
+       lck_mtx_t *mutex_held;
+
+       if (lr == NULL)
+               lr_saved = __builtin_return_address(0);
+       else
+               lr_saved = lr;
+
 #ifdef MORE_KCTLLOCK_DEBUG
 #ifdef MORE_KCTLLOCK_DEBUG
-       printf("ctl_unlock: so=%x sopcb=%x lock=%x ref=%x lr=%x\n",
-                       so, so->so_pcb, ((struct ctl_cb *)so->so_pcb)->mtx, so->so_usecount, lr_saved);
+       printf("ctl_unlock: so=%x sopcb=%x lock=%x ref=%x lr=%p\n",
+           so, so->so_pcb, ((struct ctl_cb *)so->so_pcb)->mtx,
+           so->so_usecount, lr_saved);
 #endif
        if (refcount)
                so->so_usecount--;
 #endif
        if (refcount)
                so->so_usecount--;
-       
-       if (so->so_usecount < 0)
-               panic("ctl_unlock: so=%p usecount=%x\n", so, 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) {
        if (so->so_pcb == NULL) {
-               panic("ctl_unlock: so=%p NO PCB usecount=%x lr=%x\n", so, so->so_usecount, lr_saved);
-               mutex_held = so->so_proto->pr_domain->dom_mtx;
-       } else {
-               mutex_held = ((struct ctl_cb *)so->so_pcb)->mtx;
+               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);
        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);
        if (so->so_usecount == 0)
                ctl_sofreelastref(so);
-       
+
        return (0);
 }
 
        return (0);
 }
 
@@ -988,10 +1081,12 @@ ctl_getlock(struct socket *so, __unused int locktype)
        
        if (so->so_pcb)  {
                if (so->so_usecount < 0)
        
        if (so->so_pcb)  {
                if (so->so_usecount < 0)
-                       panic("ctl_getlock: so=%p usecount=%x\n", so, so->so_usecount);
+                       panic("ctl_getlock: so=%p usecount=%x lrh= %s\n", 
+                           so, so->so_usecount, solockhistory_nr(so));
                return(kcb->mtx);
        } else {
                return(kcb->mtx);
        } else {
-               panic("ctl_getlock: so=%p NULL so_pcb\n", so);
+               panic("ctl_getlock: so=%p NULL NO so_pcb %s\n", 
+                   so, solockhistory_nr(so));
                return (so->so_proto->pr_domain->dom_mtx);
        }
 }
                return (so->so_proto->pr_domain->dom_mtx);
        }
 }