kern_udev.c: Fix a -Wold-style-definition warning.
[dragonfly.git] / sys / kern / kern_udev.c
1 /*
2  * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Alex Hornung <ahornung@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/proc.h>
38 #include <sys/buf.h>
39 #include <sys/conf.h>
40 #include <sys/poll.h>
41 #include <sys/ioccom.h>
42 #include <sys/malloc.h>
43 #include <sys/ctype.h>
44 #include <sys/syslog.h>
45 #include <sys/udev.h>
46 #include <sys/devfs.h>
47 #include <libprop/proplib.h>
48
49 MALLOC_DEFINE(M_UDEV, "udev", "udev allocs");
50
51 /* XXX: use UUIDs for identification; would need help from devfs */
52
53 static cdev_t           udev_dev;
54 static d_open_t         udev_dev_open;
55 static d_close_t        udev_dev_close;
56 static d_read_t         udev_dev_read;
57 static d_poll_t         udev_dev_poll;
58 static d_ioctl_t        udev_dev_ioctl;
59
60 static int _udev_dict_set_cstr(prop_dictionary_t, const char *, char *);
61 static int _udev_dict_set_int(prop_dictionary_t, const char *, int64_t);
62 static int _udev_dict_set_uint(prop_dictionary_t, const char *, uint64_t);
63 static int _udev_dict_delete_key(prop_dictionary_t, const char *);
64 static prop_dictionary_t udev_init_dict_event(cdev_t, const char *);
65 static int udev_init_dict(cdev_t);
66 static int udev_destroy_dict(cdev_t);
67 static void udev_event_insert(int, prop_dictionary_t);
68 static struct udev_event_kernel *udev_event_remove(void);
69 static void udev_event_free(struct udev_event_kernel *);
70 static char *udev_event_externalize(struct udev_event_kernel *);
71 static void udev_getdevs_scan_callback(cdev_t, void *);
72 static int udev_getdevs_ioctl(struct plistref *, u_long, prop_dictionary_t);
73
74 struct cmd_function {
75         const char *cmd;
76         int  (*fn)(struct plistref *, u_long, prop_dictionary_t);
77 };
78
79 struct udev_prop_ctx {
80         prop_array_t cdevs;
81         int error;
82 };
83
84 struct udev_event_kernel {
85         struct udev_event ev;
86         TAILQ_ENTRY(udev_event_kernel)  link;
87 };
88
89 struct udev_softc {
90         int opened;
91         int initiated;
92
93         struct selinfo sel;
94
95         int qlen;
96         struct lock lock;
97         TAILQ_HEAD(, udev_event_kernel) ev_queue;       /* list of thread_io */
98 } udevctx;
99
100 static struct dev_ops udev_dev_ops = {
101         { "udev", 0, 0 },
102         .d_open = udev_dev_open,
103         .d_close = udev_dev_close,
104         .d_read = udev_dev_read,
105         .d_poll = udev_dev_poll,
106         .d_ioctl = udev_dev_ioctl
107 };
108
109 struct cmd_function cmd_fn[] = {
110                 { .cmd = "getdevs", .fn = udev_getdevs_ioctl},
111                 {NULL, NULL}
112 };
113
114 static int
115 _udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str)
116 {
117         prop_string_t   ps;
118
119         KKASSERT(dict != NULL);
120
121         ps = prop_string_create_cstring(str);
122         if (ps == NULL)
123                 return ENOMEM;
124
125         if (prop_dictionary_set(dict, key, ps) == false) {
126                 prop_object_release(ps);
127                 return ENOMEM;
128         }
129
130         prop_object_release(ps);
131         return 0;
132 }
133
134 static int
135 _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val)
136 {
137         prop_number_t   pn;
138
139         KKASSERT(dict != NULL);
140
141         pn = prop_number_create_integer(val);
142         if (pn == NULL)
143                 return ENOMEM;
144
145         if (prop_dictionary_set(dict, key, pn) == false) {
146                 prop_object_release(pn);
147                 return ENOMEM;
148         }
149
150         prop_object_release(pn);
151         return 0;
152 }
153
154 static int
155 _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val)
156 {
157         prop_number_t   pn;
158
159         KKASSERT(dict != NULL);
160
161         pn = prop_number_create_unsigned_integer(val);
162         if (pn == NULL)
163                 return ENOMEM;
164
165         if (prop_dictionary_set(dict, key, pn) == false) {
166                 prop_object_release(pn);
167                 return ENOMEM;
168         }
169
170         prop_object_release(pn);
171         return 0;
172 }
173
174 static int
175 _udev_dict_delete_key(prop_dictionary_t dict, const char *key)
176 {
177         KKASSERT(dict != NULL);
178
179         prop_dictionary_remove(dict, key);
180
181         return 0;
182 }
183
184 /*
185  * Initialize an event dictionary, which contains three parameters to
186  * identify the device referred to (name, devnum, kptr) and the affected key.
187  */
188 static prop_dictionary_t
189 udev_init_dict_event(cdev_t dev, const char *key)
190 {
191         prop_dictionary_t       dict;
192         uint64_t        kptr;
193         int error;
194
195         kptr = (uint64_t)(uintptr_t)dev;
196         KKASSERT(dev != NULL);
197
198         dict = prop_dictionary_create();
199         if (dict == NULL) {
200                 log(LOG_DEBUG, "udev_init_dict_event: prop_dictionary_create() failed\n");
201                 return NULL;
202         }
203
204         if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
205                 goto error_out;
206         if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
207                 goto error_out;
208         if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
209                 goto error_out;
210         if ((error = _udev_dict_set_cstr(dict, "key", __DECONST(char *, key))))
211                 goto error_out;
212
213 error_out:
214         prop_object_release(dict);
215         return NULL;
216 }
217
218 int
219 udev_dict_set_cstr(cdev_t dev, const char *key, char *str)
220 {
221         prop_dictionary_t       dict;
222         int error;
223
224         KKASSERT(dev != NULL);
225
226         /* Queue a key update event */
227         dict = udev_init_dict_event(dev, key);
228         if (dict == NULL)
229                 return ENOMEM;
230         if ((error = _udev_dict_set_cstr(dict, "value", str))) {
231                 prop_object_release(dict);
232                 return error;
233         }
234         udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
235         prop_object_release(dict);
236
237         return _udev_dict_set_cstr(dev->si_dict, key, str);
238 }
239
240 int
241 udev_dict_set_int(cdev_t dev, const char *key, int64_t val)
242 {
243         prop_dictionary_t       dict;
244         int error;
245
246         KKASSERT(dev != NULL);
247
248         /* Queue a key update event */
249         dict = udev_init_dict_event(dev, key);
250         if (dict == NULL)
251                 return ENOMEM;
252         if ((error = _udev_dict_set_int(dict, "value", val))) {
253                 prop_object_release(dict);
254                 return error;
255         }
256         udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
257         prop_object_release(dict);
258
259         return _udev_dict_set_int(dev->si_dict, key, val);
260 }
261
262 int
263 udev_dict_set_uint(cdev_t dev, const char *key, uint64_t val)
264 {
265         prop_dictionary_t       dict;
266         int error;
267
268         KKASSERT(dev != NULL);
269
270         /* Queue a key update event */
271         dict = udev_init_dict_event(dev, key);
272         if (dict == NULL)
273                 return ENOMEM;
274         if ((error = _udev_dict_set_uint(dict, "value", val))) {
275                 prop_object_release(dict);
276                 return error;
277         }
278         udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
279         prop_object_release(dict);
280
281         return _udev_dict_set_uint(dev->si_dict, key, val);
282 }
283
284 int
285 udev_dict_delete_key(cdev_t dev, const char *key)
286 {
287         prop_dictionary_t       dict;
288
289         KKASSERT(dev != NULL);
290
291         /* Queue a key removal event */
292         dict = udev_init_dict_event(dev, key);
293         if (dict == NULL)
294                 return ENOMEM;
295         udev_event_insert(UDEV_EV_KEY_REMOVE, dict);
296         prop_object_release(dict);
297
298         return _udev_dict_delete_key(dev->si_dict, key);
299 }
300
301 static int
302 udev_init_dict(cdev_t dev)
303 {
304         prop_dictionary_t dict;
305         uint64_t        kptr;
306         int error;
307
308         kptr = (uint64_t)(uintptr_t)dev;
309
310         KKASSERT(dev != NULL);
311         dict = prop_dictionary_create();
312         if (dict == NULL) {
313                 log(LOG_DEBUG, "udev_init_dict: prop_dictionary_create() failed\n");
314                 return ENOMEM;
315         }
316
317         if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
318                 goto error_out;
319         if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
320                 goto error_out;
321         if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
322                 goto error_out;
323
324         /* XXX: The next 3 are marginallly useful, if at all */
325         if ((error = _udev_dict_set_uint(dict, "uid", dev->si_uid)))
326                 goto error_out;
327         if ((error = _udev_dict_set_uint(dict, "gid", dev->si_gid)))
328                 goto error_out;
329         if ((error = _udev_dict_set_int(dict, "mode", dev->si_perms)))
330                 goto error_out;
331
332         if ((error = _udev_dict_set_int(dict, "major", umajor(dev->si_inode))))
333                 goto error_out;
334         if ((error = _udev_dict_set_int(dict, "minor", dev->si_uminor)))
335                 goto error_out;
336
337         dev->si_dict = dict;
338         return 0;
339
340 error_out:
341         dev->si_dict = NULL;
342         prop_object_release(dict);
343         return error;
344 }
345
346 static int
347 udev_destroy_dict(cdev_t dev)
348 {
349         KKASSERT(dev != NULL);
350
351         if (dev->si_dict != NULL) {
352                 prop_object_release(dev->si_dict);
353                 dev->si_dict = NULL;
354         }
355
356         return 0;
357 }
358
359 static void
360 udev_event_insert(int ev_type, prop_dictionary_t dict)
361 {
362         struct udev_event_kernel *ev;
363
364         /* Only start queing events after client has initiated properly */
365         if (!udevctx.initiated)
366                 return;
367
368         /* XXX: use objcache eventually */
369         ev = kmalloc(sizeof(*ev), M_UDEV, M_WAITOK);
370         ev->ev.ev_dict = prop_dictionary_copy(dict);
371         if (ev->ev.ev_dict == NULL) {
372                 kfree(ev, M_UDEV);
373                 return;
374         }
375         ev->ev.ev_type = ev_type;
376
377         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
378         TAILQ_INSERT_TAIL(&udevctx.ev_queue, ev, link);
379         ++udevctx.qlen;
380         lockmgr(&udevctx.lock, LK_RELEASE);
381
382         wakeup(&udevctx);
383         selwakeup(&udevctx.sel);
384 }
385
386 static struct udev_event_kernel *
387 udev_event_remove(void)
388 {
389         struct udev_event_kernel *ev;
390
391         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
392         if (TAILQ_EMPTY(&udevctx.ev_queue)) {
393                 lockmgr(&udevctx.lock, LK_RELEASE);
394                 return NULL;
395         }
396
397         ev = TAILQ_FIRST(&udevctx.ev_queue);
398         TAILQ_REMOVE(&udevctx.ev_queue, ev, link);
399         --udevctx.qlen;
400         lockmgr(&udevctx.lock, LK_RELEASE);
401
402         return ev;
403 }
404
405 static void
406 udev_event_free(struct udev_event_kernel *ev)
407 {
408         /* XXX: use objcache eventually */
409         kfree(ev, M_UDEV);
410 }
411
412 static char *
413 udev_event_externalize(struct udev_event_kernel *ev)
414 {
415         prop_dictionary_t       dict;
416         char *xml;
417         int error;
418
419
420         dict = prop_dictionary_create();
421         if (dict == NULL) {
422                 log(LOG_DEBUG, "udev_event_externalize: prop_dictionary_create() failed\n");
423                 return NULL;
424         }
425
426         if ((error = _udev_dict_set_int(dict, "evtype", ev->ev.ev_type))) {
427                 prop_object_release(dict);
428                 return NULL;
429         }
430
431         if (prop_dictionary_set(dict, "evdict", ev->ev.ev_dict) == false) {
432                 prop_object_release(dict);
433                 return NULL;
434         }
435
436         prop_object_release(ev->ev.ev_dict);
437
438         xml = prop_dictionary_externalize(dict);
439
440         prop_object_release(dict);
441
442         return xml;
443 }
444
445 int
446 udev_event_attach(cdev_t dev, char *name, int alias)
447 {
448         prop_dictionary_t       dict;
449         int error;
450
451         KKASSERT(dev != NULL);
452
453         reference_dev(dev);
454
455         error = udev_init_dict(dev);
456         if (error)
457                 goto error_out;
458
459         if (alias) {
460                 dict = prop_dictionary_copy(dev->si_dict);
461                 if (dict == NULL)
462                         goto error_out;
463
464                 if ((error = _udev_dict_set_cstr(dict, "name", name))) {
465                         prop_object_release(dict);
466                         goto error_out;
467                 }
468
469                 _udev_dict_set_int(dict, "alias", 1);
470
471                 udev_event_insert(UDEV_EVENT_ATTACH, dict);
472                 prop_object_release(dict);
473         } else {
474                 _udev_dict_set_int(dev->si_dict, "alias", 0);
475                 udev_event_insert(UDEV_EVENT_ATTACH, dev->si_dict);
476         }
477
478 error_out:
479         release_dev(dev);
480         return error;
481 }
482
483 int
484 udev_event_detach(cdev_t dev, char *name, int alias)
485 {
486         prop_dictionary_t       dict;
487
488         KKASSERT(dev != NULL);
489
490         reference_dev(dev);
491
492         if (alias) {
493                 dict = prop_dictionary_copy(dev->si_dict);
494                 if (dict == NULL)
495                         goto error_out;
496
497                 if (_udev_dict_set_cstr(dict, "name", name)) {
498                         prop_object_release(dict);
499                         goto error_out;
500                 }
501
502                 _udev_dict_set_int(dict, "alias", 1);
503
504                 udev_event_insert(UDEV_EVENT_DETACH, dict);
505                 prop_object_release(dict);
506         } else {
507                 udev_event_insert(UDEV_EVENT_DETACH, dev->si_dict);
508         }
509
510 error_out:
511         udev_destroy_dict(dev);
512
513         release_dev(dev);
514
515         return 0;
516 }
517
518 /*
519  * dev stuff
520  */
521 static int
522 udev_dev_open(struct dev_open_args *ap)
523 {
524         if (udevctx.opened)
525                 return EBUSY;
526
527         udevctx.opened = 1;
528
529         return 0;
530 }
531
532 static int
533 udev_dev_close(struct dev_close_args *ap)
534 {
535         udevctx.opened = 0;
536         udevctx.initiated = 0;
537         wakeup(&udevctx);
538
539         return 0;
540 }
541
542 static int
543 udev_dev_poll(struct dev_poll_args *ap)
544 {
545         int revents = 0;
546
547         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
548         if (ap->a_events & (POLLIN | POLLRDNORM)) {
549                 if (!TAILQ_EMPTY(&udevctx.ev_queue))
550                         revents = ap->a_events & (POLLIN | POLLRDNORM);
551                 else
552                         selrecord(curthread, &udevctx.sel);
553         }
554         lockmgr(&udevctx.lock, LK_RELEASE);
555
556         ap->a_events = revents;
557         return 0;
558 }
559
560 static int
561 udev_dev_read(struct dev_read_args *ap)
562 {
563         struct udev_event_kernel *ev;
564         struct uio *uio = ap->a_uio;
565         char    *xml;
566         size_t  len;
567         int     error;
568
569
570         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
571
572         for (;;) {
573                 if ((ev = udev_event_remove()) != NULL) {
574                         if ((xml = udev_event_externalize(ev)) == NULL) {
575                                 lockmgr(&udevctx.lock, LK_RELEASE);
576                                 return ENOMEM;
577                         }
578
579                         len = strlen(xml) + 1; /* account for NULL-termination */
580                         if (uio->uio_resid < len) {
581                                 error = ENOMEM;
582                         } else {
583                                 error = uiomove((caddr_t)xml, len, uio);
584                         }
585
586                         kfree(xml, M_TEMP);
587                         udev_event_free(ev);
588                         lockmgr(&udevctx.lock, LK_RELEASE);
589                         return error;
590                 }
591
592                 if ((error = lksleep(&udevctx, &udevctx.lock, 0, "udevq", 0))) {
593                         lockmgr(&udevctx.lock, LK_RELEASE);
594                         return error;
595                 }
596         }
597
598         lockmgr(&udevctx.lock, LK_RELEASE);
599
600 }
601
602 static int
603 udev_dev_ioctl(struct dev_ioctl_args *ap)
604 {
605         prop_dictionary_t dict;
606         prop_object_t   po;
607         prop_string_t   ps;
608         struct plistref *pref;
609         int i, error;
610
611         error = 0;
612
613         switch(ap->a_cmd) {
614         case UDEVPROP:
615                 /* Use proplib(3) for userspace/kernel communication */
616                 pref = (struct plistref *)ap->a_data;
617                 error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd, &dict);
618                 if (error)
619                         return error;
620
621                 po = prop_dictionary_get(dict, "command");
622                 if (po == NULL || prop_object_type(po) != PROP_TYPE_STRING) {
623                         log(LOG_DEBUG, "udev: prop_dictionary_get() failed\n");
624                         prop_object_release(dict);
625                         return EINVAL;
626                 }
627
628                 ps = po;
629                 /* Handle cmd */
630                 for(i = 0; cmd_fn[i].cmd != NULL; i++) {
631                         if (prop_string_equals_cstring(ps, cmd_fn[i].cmd))
632                                 break;
633                 }
634
635                 if (cmd_fn[i].cmd != NULL) {
636                         log(LOG_DEBUG, "udev: ioctl %s called\n", cmd_fn[i].cmd);
637                         error = cmd_fn[i].fn(pref, ap->a_cmd, dict);
638                 } else {
639                         error = EINVAL;
640                 }
641
642                 //prop_object_release(po);
643                 kprintf("foo\n");
644                 prop_object_release(dict);
645                 break;
646         default:
647                 error = ENOTTY; /* Inappropriate ioctl for device */
648                 break;
649         }
650
651         return(error);
652 }
653
654 static void
655 udev_getdevs_scan_callback(cdev_t cdev, void *arg)
656 {
657         struct udev_prop_ctx *ctx = arg;
658
659         KKASSERT(arg != NULL);
660
661         if (cdev->si_dict == NULL)
662                 return;
663
664         if (prop_array_add(ctx->cdevs, cdev->si_dict) == false) {
665                 ctx->error = EINVAL;
666                 return;
667         }
668 }
669
670 static int
671 udev_getdevs_ioctl(struct plistref *pref, u_long cmd, prop_dictionary_t dict)
672 {
673         prop_dictionary_t odict;
674         struct udev_prop_ctx ctx;
675         int error;
676
677         ctx.error = 0;
678         ctx.cdevs = prop_array_create();
679         if (ctx.cdevs == NULL) {
680                 log(LOG_DEBUG, "udev_getdevs_ioctl: prop_array_create() failed\n");
681                 return EINVAL;
682         }
683
684         /* XXX: need devfs_scan_alias_callback() */
685         devfs_scan_callback(udev_getdevs_scan_callback, &ctx);
686
687         if (ctx.error != 0) {
688                 prop_object_release(ctx.cdevs);
689                 return (ctx.error);
690         }
691         udevctx.initiated = 1;
692
693         odict = prop_dictionary_create();
694         if (odict == NULL) {
695                 return ENOMEM;
696         }
697
698         if ((prop_dictionary_set(odict, "array", ctx.cdevs)) == 0) {
699                 log(LOG_DEBUG, "udev_getdevs_ioctl: prop_dictionary_set failed\n");
700                 prop_object_release(odict);
701                 return ENOMEM;
702         }
703
704         error = prop_dictionary_copyout_ioctl(pref, cmd, odict);
705
706         /* XXX: need to release ctx.cdevs? */
707         prop_object_release(odict);
708         return error;
709 }
710
711
712 /*
713  * SYSINIT stuff
714  */
715 static void
716 udev_init(void)
717 {
718         lockinit(&udevctx.lock, "udevevq", 0, LK_CANRECURSE);
719         TAILQ_INIT(&udevctx.ev_queue);
720 }
721
722 static void
723 udev_uninit(void)
724 {
725 }
726
727 static void
728 udev_dev_init(void)
729 {
730         udev_dev = make_dev(&udev_dev_ops,
731             0,
732             UID_ROOT,
733             GID_WHEEL,
734             0600,
735             "udev");
736 }
737
738 static void
739 udev_dev_uninit(void)
740 {
741         destroy_dev(udev_dev);
742 }
743
744 SYSINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_init, NULL);
745 SYSUNINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_uninit, NULL);
746 SYSINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_init, NULL);
747 SYSUNINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_uninit, NULL);