+errno_t
+ctl_register(struct kern_ctl_reg *userkctl, kern_ctl_ref *kctlref)
+{
+ struct kctl *kctl = NULL;
+ struct kctl *kctl_next = NULL;
+ u_int32_t id = 1;
+ size_t name_len;
+
+ if (userkctl == NULL) /* sanity check */
+ return(EINVAL);
+ if (userkctl->ctl_connect == NULL)
+ return(EINVAL);
+ name_len = strlen(userkctl->ctl_name);
+ if (name_len == 0 || name_len + 1 > MAX_KCTL_NAME)
+ return(EINVAL);
+
+ MALLOC(kctl, struct kctl *, sizeof(*kctl), M_TEMP, M_WAITOK);
+ if (kctl == NULL)
+ return(ENOMEM);
+ bzero((char *)kctl, sizeof(*kctl));
+
+ lck_mtx_lock(ctl_mtx);
+
+ /*
+ * Kernel Control IDs
+ *
+ * CTL_FLAG_REG_ID_UNIT indicates the control ID and unit number are
+ * static. If they do not exist, add them to the list in order. If the
+ * flag is not set, we must find a new unique value. We assume the
+ * list is in order. We find the last item in the list and add one. If
+ * this leads to wrapping the id around, we start at the front of the
+ * list and look for a gap.
+ */
+
+ if ((userkctl->ctl_flags & CTL_FLAG_REG_ID_UNIT) == 0) {
+ /* Must dynamically assign an unused ID */
+
+ /* Verify the same name isn't already registered */
+ if (ctl_find_by_name(userkctl->ctl_name) != NULL) {
+ lck_mtx_unlock(ctl_mtx);
+ FREE(kctl, M_TEMP);
+ return(EEXIST);
+ }
+
+ /* Start with 1 in case the list is empty */
+ id = 1;
+ kctl_next = TAILQ_LAST(&ctl_head, kctl_list);
+
+ if (kctl_next != NULL) {
+ /* List was not empty, add one to the last item in the list */
+ id = kctl_next->id + 1;
+ kctl_next = NULL;
+
+ /*
+ * If this wrapped the id number, start looking at the front
+ * of the list for an unused id.
+ */
+ if (id == 0) {
+ /* Find the next unused ID */
+ id = 1;
+
+ TAILQ_FOREACH(kctl_next, &ctl_head, next) {
+ if (kctl_next->id > id) {
+ /* We found a gap */
+ break;
+ }
+
+ id = kctl_next->id + 1;
+ }
+ }
+ }
+
+ userkctl->ctl_id = id;
+ kctl->id = id;
+ kctl->reg_unit = -1;
+ } else {
+ TAILQ_FOREACH(kctl_next, &ctl_head, next) {
+ if (kctl_next->id > userkctl->ctl_id)
+ break;
+ }
+
+ if (ctl_find_by_id_unit(userkctl->ctl_id, userkctl->ctl_unit) != NULL) {
+ lck_mtx_unlock(ctl_mtx);
+ FREE(kctl, M_TEMP);
+ return(EEXIST);
+ }
+ kctl->id = userkctl->ctl_id;
+ kctl->reg_unit = userkctl->ctl_unit;
+ }
+ strlcpy(kctl->name, userkctl->ctl_name, MAX_KCTL_NAME);
+ kctl->flags = userkctl->ctl_flags;
+
+ /* Let the caller know the default send and receive sizes */
+ if (userkctl->ctl_sendsize == 0)
+ userkctl->ctl_sendsize = CTL_SENDSIZE;
+ kctl->sendbufsize = userkctl->ctl_sendsize;
+
+ if (userkctl->ctl_recvsize == 0)
+ userkctl->ctl_recvsize = CTL_RECVSIZE;
+ kctl->recvbufsize = userkctl->ctl_recvsize;
+
+ kctl->connect = userkctl->ctl_connect;
+ kctl->disconnect = userkctl->ctl_disconnect;
+ kctl->send = userkctl->ctl_send;
+ kctl->setopt = userkctl->ctl_setopt;
+ kctl->getopt = userkctl->ctl_getopt;
+
+ TAILQ_INIT(&kctl->kcb_head);
+
+ if (kctl_next)
+ TAILQ_INSERT_BEFORE(kctl_next, kctl, next);
+ else
+ TAILQ_INSERT_TAIL(&ctl_head, kctl, next);
+
+ lck_mtx_unlock(ctl_mtx);
+
+ *kctlref = kctl;
+
+ ctl_post_msg(KEV_CTL_REGISTERED, kctl->id);
+ return(0);
+}
+
+errno_t
+ctl_deregister(void *kctlref)