devices.c revision 605103fb553a8b872aa8dd0aef76ae8b3bfe47fa
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <errno.h>
18#include <stddef.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <sys/stat.h>
22#include <sys/types.h>
23
24#include <fcntl.h>
25#include <dirent.h>
26#include <unistd.h>
27#include <string.h>
28
29#include <sys/socket.h>
30#include <sys/un.h>
31#include <linux/netlink.h>
32
33#ifdef HAVE_SELINUX
34#include <selinux/selinux.h>
35#include <selinux/label.h>
36#include <selinux/android.h>
37#endif
38
39#include <private/android_filesystem_config.h>
40#include <sys/time.h>
41#include <asm/page.h>
42#include <sys/wait.h>
43
44#include <cutils/list.h>
45#include <cutils/uevent.h>
46
47#include "devices.h"
48#include "util.h"
49#include "log.h"
50
51#define SYSFS_PREFIX    "/sys"
52#define FIRMWARE_DIR1   "/etc/firmware"
53#define FIRMWARE_DIR2   "/vendor/firmware"
54#define FIRMWARE_DIR3   "/firmware/image"
55
56#ifdef HAVE_SELINUX
57extern struct selabel_handle *sehandle;
58#endif
59
60static int device_fd = -1;
61
62struct uevent {
63    const char *action;
64    const char *path;
65    const char *subsystem;
66    const char *firmware;
67    const char *partition_name;
68    const char *device_name;
69    int partition_num;
70    int major;
71    int minor;
72};
73
74struct perms_ {
75    char *name;
76    char *attr;
77    mode_t perm;
78    unsigned int uid;
79    unsigned int gid;
80    unsigned short prefix;
81};
82
83struct perm_node {
84    struct perms_ dp;
85    struct listnode plist;
86};
87
88struct platform_node {
89    char *name;
90    int name_len;
91    struct listnode list;
92};
93
94static list_declare(sys_perms);
95static list_declare(dev_perms);
96static list_declare(platform_names);
97
98int add_dev_perms(const char *name, const char *attr,
99                  mode_t perm, unsigned int uid, unsigned int gid,
100                  unsigned short prefix) {
101    struct perm_node *node = calloc(1, sizeof(*node));
102    if (!node)
103        return -ENOMEM;
104
105    node->dp.name = strdup(name);
106    if (!node->dp.name)
107        return -ENOMEM;
108
109    if (attr) {
110        node->dp.attr = strdup(attr);
111        if (!node->dp.attr)
112            return -ENOMEM;
113    }
114
115    node->dp.perm = perm;
116    node->dp.uid = uid;
117    node->dp.gid = gid;
118    node->dp.prefix = prefix;
119
120    if (attr)
121        list_add_tail(&sys_perms, &node->plist);
122    else
123        list_add_tail(&dev_perms, &node->plist);
124
125    return 0;
126}
127
128void fixup_sys_perms(const char *upath)
129{
130    char buf[512];
131    struct listnode *node;
132    struct perms_ *dp;
133
134        /* upaths omit the "/sys" that paths in this list
135         * contain, so we add 4 when comparing...
136         */
137    list_for_each(node, &sys_perms) {
138        dp = &(node_to_item(node, struct perm_node, plist))->dp;
139        if (dp->prefix) {
140            if (strncmp(upath, dp->name + 4, strlen(dp->name + 4)))
141                continue;
142        } else {
143            if (strcmp(upath, dp->name + 4))
144                continue;
145        }
146
147        if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf))
148            return;
149
150        sprintf(buf,"/sys%s/%s", upath, dp->attr);
151        INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
152        chown(buf, dp->uid, dp->gid);
153        chmod(buf, dp->perm);
154    }
155}
156
157static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
158{
159    mode_t perm;
160    struct listnode *node;
161    struct perm_node *perm_node;
162    struct perms_ *dp;
163
164    /* search the perms list in reverse so that ueventd.$hardware can
165     * override ueventd.rc
166     */
167    list_for_each_reverse(node, &dev_perms) {
168        perm_node = node_to_item(node, struct perm_node, plist);
169        dp = &perm_node->dp;
170
171        if (dp->prefix) {
172            if (strncmp(path, dp->name, strlen(dp->name)))
173                continue;
174        } else {
175            if (strcmp(path, dp->name))
176                continue;
177        }
178        *uid = dp->uid;
179        *gid = dp->gid;
180        return dp->perm;
181    }
182    /* Default if nothing found. */
183    *uid = 0;
184    *gid = 0;
185    return 0600;
186}
187
188static void make_device(const char *path,
189                        const char *upath,
190                        int block, int major, int minor)
191{
192    unsigned uid;
193    unsigned gid;
194    mode_t mode;
195    dev_t dev;
196#ifdef HAVE_SELINUX
197    char *secontext = NULL;
198#endif
199
200    mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
201#ifdef HAVE_SELINUX
202    if (sehandle) {
203        selabel_lookup(sehandle, &secontext, path, mode);
204        setfscreatecon(secontext);
205    }
206#endif
207    dev = makedev(major, minor);
208    /* Temporarily change egid to avoid race condition setting the gid of the
209     * device node. Unforunately changing the euid would prevent creation of
210     * some device nodes, so the uid has to be set with chown() and is still
211     * racy. Fixing the gid race at least fixed the issue with system_server
212     * opening dynamic input devices under the AID_INPUT gid. */
213    setegid(gid);
214    mknod(path, mode, dev);
215    chown(path, uid, -1);
216    setegid(AID_ROOT);
217#ifdef HAVE_SELINUX
218    if (secontext) {
219        freecon(secontext);
220        setfscreatecon(NULL);
221    }
222#endif
223}
224
225static void add_platform_device(const char *name)
226{
227    int name_len = strlen(name);
228    struct listnode *node;
229    struct platform_node *bus;
230
231    list_for_each_reverse(node, &platform_names) {
232        bus = node_to_item(node, struct platform_node, list);
233        if ((bus->name_len < name_len) &&
234                (name[bus->name_len] == '/') &&
235                !strncmp(name, bus->name, bus->name_len))
236            /* subdevice of an existing platform, ignore it */
237            return;
238    }
239
240    INFO("adding platform device %s\n", name);
241
242    bus = calloc(1, sizeof(struct platform_node));
243    bus->name = strdup(name);
244    bus->name_len = name_len;
245    list_add_tail(&platform_names, &bus->list);
246}
247
248/*
249 * given a name that may start with a platform device, find the length of the
250 * platform device prefix.  If it doesn't start with a platform device, return
251 * 0.
252 */
253static const char *find_platform_device(const char *name)
254{
255    int name_len = strlen(name);
256    struct listnode *node;
257    struct platform_node *bus;
258
259    list_for_each_reverse(node, &platform_names) {
260        bus = node_to_item(node, struct platform_node, list);
261        if ((bus->name_len < name_len) &&
262                (name[bus->name_len] == '/') &&
263                !strncmp(name, bus->name, bus->name_len))
264            return bus->name;
265    }
266
267    return NULL;
268}
269
270static void remove_platform_device(const char *name)
271{
272    struct listnode *node;
273    struct platform_node *bus;
274
275    list_for_each_reverse(node, &platform_names) {
276        bus = node_to_item(node, struct platform_node, list);
277        if (!strcmp(name, bus->name)) {
278            INFO("removing platform device %s\n", name);
279            free(bus->name);
280            list_remove(node);
281            free(bus);
282            return;
283        }
284    }
285}
286
287#if LOG_UEVENTS
288
289static inline suseconds_t get_usecs(void)
290{
291    struct timeval tv;
292    gettimeofday(&tv, 0);
293    return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec;
294}
295
296#define log_event_print(x...) INFO(x)
297
298#else
299
300#define log_event_print(fmt, args...)   do { } while (0)
301#define get_usecs()                     0
302
303#endif
304
305static void parse_event(const char *msg, struct uevent *uevent)
306{
307    uevent->action = "";
308    uevent->path = "";
309    uevent->subsystem = "";
310    uevent->firmware = "";
311    uevent->major = -1;
312    uevent->minor = -1;
313    uevent->partition_name = NULL;
314    uevent->partition_num = -1;
315    uevent->device_name = NULL;
316
317        /* currently ignoring SEQNUM */
318    while(*msg) {
319        if(!strncmp(msg, "ACTION=", 7)) {
320            msg += 7;
321            uevent->action = msg;
322        } else if(!strncmp(msg, "DEVPATH=", 8)) {
323            msg += 8;
324            uevent->path = msg;
325        } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
326            msg += 10;
327            uevent->subsystem = msg;
328        } else if(!strncmp(msg, "FIRMWARE=", 9)) {
329            msg += 9;
330            uevent->firmware = msg;
331        } else if(!strncmp(msg, "MAJOR=", 6)) {
332            msg += 6;
333            uevent->major = atoi(msg);
334        } else if(!strncmp(msg, "MINOR=", 6)) {
335            msg += 6;
336            uevent->minor = atoi(msg);
337        } else if(!strncmp(msg, "PARTN=", 6)) {
338            msg += 6;
339            uevent->partition_num = atoi(msg);
340        } else if(!strncmp(msg, "PARTNAME=", 9)) {
341            msg += 9;
342            uevent->partition_name = msg;
343        } else if(!strncmp(msg, "DEVNAME=", 8)) {
344            msg += 8;
345            uevent->device_name = msg;
346        }
347
348        /* advance to after the next \0 */
349        while(*msg++)
350            ;
351    }
352
353    log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n",
354                    uevent->action, uevent->path, uevent->subsystem,
355                    uevent->firmware, uevent->major, uevent->minor);
356}
357
358static char **get_character_device_symlinks(struct uevent *uevent)
359{
360    const char *parent;
361    char *slash;
362    char **links;
363    int link_num = 0;
364    int width;
365
366    if (strncmp(uevent->path, "/devices/platform/", 18))
367        return NULL;
368
369    links = malloc(sizeof(char *) * 2);
370    if (!links)
371        return NULL;
372    memset(links, 0, sizeof(char *) * 2);
373
374    /* skip "/devices/platform/<driver>" */
375    parent = strchr(uevent->path + 18, '/');
376    if (!*parent)
377        goto err;
378
379    if (!strncmp(parent, "/usb", 4)) {
380        /* skip root hub name and device. use device interface */
381        while (*++parent && *parent != '/');
382        if (*parent)
383            while (*++parent && *parent != '/');
384        if (!*parent)
385            goto err;
386        slash = strchr(++parent, '/');
387        if (!slash)
388            goto err;
389        width = slash - parent;
390        if (width <= 0)
391            goto err;
392
393        if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0)
394            link_num++;
395        else
396            links[link_num] = NULL;
397        mkdir("/dev/usb", 0755);
398    }
399    else {
400        goto err;
401    }
402
403    return links;
404err:
405    free(links);
406    return NULL;
407}
408
409static char **parse_platform_block_device(struct uevent *uevent)
410{
411    const char *device;
412    const char *path;
413    char *slash;
414    int width;
415    char buf[256];
416    char link_path[256];
417    int fd;
418    int link_num = 0;
419    int ret;
420    char *p;
421    unsigned int size;
422    struct stat info;
423
424    char **links = malloc(sizeof(char *) * 4);
425    if (!links)
426        return NULL;
427    memset(links, 0, sizeof(char *) * 4);
428
429    /* Drop "/devices/platform/" */
430    path = uevent->path;
431    device = path + 18;
432    device = find_platform_device(device);
433    if (!device)
434        goto err;
435
436    INFO("found platform device %s\n", device);
437
438    snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device);
439
440    if (uevent->partition_name) {
441        p = strdup(uevent->partition_name);
442        sanitize(p);
443        if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
444            link_num++;
445        else
446            links[link_num] = NULL;
447        free(p);
448    }
449
450    if (uevent->partition_num >= 0) {
451        if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
452            link_num++;
453        else
454            links[link_num] = NULL;
455    }
456
457    slash = strrchr(path, '/');
458    if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
459        link_num++;
460    else
461        links[link_num] = NULL;
462
463    return links;
464
465err:
466    free(links);
467    return NULL;
468}
469
470static void handle_device(const char *action, const char *devpath,
471        const char *path, int block, int major, int minor, char **links)
472{
473    int i;
474
475    if(!strcmp(action, "add")) {
476        make_device(devpath, path, block, major, minor);
477        if (links) {
478            for (i = 0; links[i]; i++)
479                make_link(devpath, links[i]);
480        }
481    }
482
483    if(!strcmp(action, "remove")) {
484        if (links) {
485            for (i = 0; links[i]; i++)
486                remove_link(devpath, links[i]);
487        }
488        unlink(devpath);
489    }
490
491    if (links) {
492        for (i = 0; links[i]; i++)
493            free(links[i]);
494        free(links);
495    }
496}
497
498static void handle_platform_device_event(struct uevent *uevent)
499{
500    const char *name = uevent->path + 18; /* length of /devices/platform/ */
501
502    if (!strcmp(uevent->action, "add"))
503        add_platform_device(name);
504    else if (!strcmp(uevent->action, "remove"))
505        remove_platform_device(name);
506}
507
508static const char *parse_device_name(struct uevent *uevent, unsigned int len)
509{
510    const char *name;
511
512    /* if it's not a /dev device, nothing else to do */
513    if((uevent->major < 0) || (uevent->minor < 0))
514        return NULL;
515
516    /* do we have a name? */
517    name = strrchr(uevent->path, '/');
518    if(!name)
519        return NULL;
520    name++;
521
522    /* too-long names would overrun our buffer */
523    if(strlen(name) > len)
524        return NULL;
525
526    return name;
527}
528
529static void handle_block_device_event(struct uevent *uevent)
530{
531    const char *base = "/dev/block/";
532    const char *name;
533    char devpath[96];
534    char **links = NULL;
535
536    name = parse_device_name(uevent, 64);
537    if (!name)
538        return;
539
540    snprintf(devpath, sizeof(devpath), "%s%s", base, name);
541    make_dir(base, 0755);
542
543    if (!strncmp(uevent->path, "/devices/platform/", 18))
544        links = parse_platform_block_device(uevent);
545
546    handle_device(uevent->action, devpath, uevent->path, 1,
547            uevent->major, uevent->minor, links);
548}
549
550static void handle_generic_device_event(struct uevent *uevent)
551{
552    char *base;
553    const char *name;
554    char devpath[96] = {0};
555    char **links = NULL;
556
557    name = parse_device_name(uevent, 64);
558    if (!name)
559        return;
560
561    if (!strncmp(uevent->subsystem, "usb", 3)) {
562         if (!strcmp(uevent->subsystem, "usb")) {
563            if (uevent->device_name) {
564                /*
565                 * create device node provided by kernel if present
566                 * see drivers/base/core.c
567                 */
568                char *p = devpath;
569                snprintf(devpath, sizeof(devpath), "/dev/%s", uevent->device_name);
570                /* skip leading /dev/ */
571                p += 5;
572                /* build directories */
573                while (*p) {
574                    if (*p == '/') {
575                        *p = 0;
576                        make_dir(devpath, 0755);
577                        *p = '/';
578                    }
579                    p++;
580                }
581             }
582             else {
583                 /* This imitates the file system that would be created
584                  * if we were using devfs instead.
585                  * Minors are broken up into groups of 128, starting at "001"
586                  */
587                 int bus_id = uevent->minor / 128 + 1;
588                 int device_id = uevent->minor % 128 + 1;
589                 /* build directories */
590                 make_dir("/dev/bus", 0755);
591                 make_dir("/dev/bus/usb", 0755);
592                 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
593                 make_dir(devpath, 0755);
594                 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
595             }
596         } else {
597             /* ignore other USB events */
598             return;
599         }
600     } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
601         base = "/dev/graphics/";
602         make_dir(base, 0755);
603     } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
604         base = "/dev/oncrpc/";
605         make_dir(base, 0755);
606     } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
607         base = "/dev/adsp/";
608         make_dir(base, 0755);
609     } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
610         base = "/dev/msm_camera/";
611         make_dir(base, 0755);
612     } else if(!strncmp(uevent->subsystem, "input", 5)) {
613         base = "/dev/input/";
614         make_dir(base, 0755);
615     } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
616         base = "/dev/mtd/";
617         make_dir(base, 0755);
618     } else if(!strncmp(uevent->subsystem, "sound", 5)) {
619         base = "/dev/snd/";
620         make_dir(base, 0755);
621     } else if(!strncmp(uevent->subsystem, "misc", 4) &&
622                 !strncmp(name, "log_", 4)) {
623         base = "/dev/log/";
624         make_dir(base, 0755);
625         name += 4;
626     } else
627         base = "/dev/";
628     links = get_character_device_symlinks(uevent);
629
630     if (!devpath[0])
631         snprintf(devpath, sizeof(devpath), "%s%s", base, name);
632
633     handle_device(uevent->action, devpath, uevent->path, 0,
634             uevent->major, uevent->minor, links);
635}
636
637static void handle_device_event(struct uevent *uevent)
638{
639    if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change"))
640        fixup_sys_perms(uevent->path);
641
642    if (!strncmp(uevent->subsystem, "block", 5)) {
643        handle_block_device_event(uevent);
644    } else if (!strncmp(uevent->subsystem, "platform", 8)) {
645        handle_platform_device_event(uevent);
646    } else {
647        handle_generic_device_event(uevent);
648    }
649}
650
651static int load_firmware(int fw_fd, int loading_fd, int data_fd)
652{
653    struct stat st;
654    long len_to_copy;
655    int ret = 0;
656
657    if(fstat(fw_fd, &st) < 0)
658        return -1;
659    len_to_copy = st.st_size;
660
661    write(loading_fd, "1", 1);  /* start transfer */
662
663    while (len_to_copy > 0) {
664        char buf[PAGE_SIZE];
665        ssize_t nr;
666
667        nr = read(fw_fd, buf, sizeof(buf));
668        if(!nr)
669            break;
670        if(nr < 0) {
671            ret = -1;
672            break;
673        }
674
675        len_to_copy -= nr;
676        while (nr > 0) {
677            ssize_t nw = 0;
678
679            nw = write(data_fd, buf + nw, nr);
680            if(nw <= 0) {
681                ret = -1;
682                goto out;
683            }
684            nr -= nw;
685        }
686    }
687
688out:
689    if(!ret)
690        write(loading_fd, "0", 1);  /* successful end of transfer */
691    else
692        write(loading_fd, "-1", 2); /* abort transfer */
693
694    return ret;
695}
696
697static int is_booting(void)
698{
699    return access("/dev/.booting", F_OK) == 0;
700}
701
702static void process_firmware_event(struct uevent *uevent)
703{
704    char *root, *loading, *data, *file1 = NULL, *file2 = NULL, *file3 = NULL;
705    int l, loading_fd, data_fd, fw_fd;
706    int booting = is_booting();
707
708    INFO("firmware: loading '%s' for '%s'\n",
709         uevent->firmware, uevent->path);
710
711    l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
712    if (l == -1)
713        return;
714
715    l = asprintf(&loading, "%sloading", root);
716    if (l == -1)
717        goto root_free_out;
718
719    l = asprintf(&data, "%sdata", root);
720    if (l == -1)
721        goto loading_free_out;
722
723    l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware);
724    if (l == -1)
725        goto data_free_out;
726
727    l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware);
728    if (l == -1)
729        goto data_free_out;
730
731    l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware);
732    if (l == -1)
733        goto data_free_out;
734
735    loading_fd = open(loading, O_WRONLY);
736    if(loading_fd < 0)
737        goto file_free_out;
738
739    data_fd = open(data, O_WRONLY);
740    if(data_fd < 0)
741        goto loading_close_out;
742
743try_loading_again:
744    fw_fd = open(file1, O_RDONLY);
745    if(fw_fd < 0) {
746        fw_fd = open(file2, O_RDONLY);
747        if (fw_fd < 0) {
748            fw_fd = open(file3, O_RDONLY);
749            if (fw_fd < 0) {
750                if (booting) {
751                        /* If we're not fully booted, we may be missing
752                         * filesystems needed for firmware, wait and retry.
753                         */
754                    usleep(100000);
755                    booting = is_booting();
756                    goto try_loading_again;
757                }
758                INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno);
759                write(loading_fd, "-1", 2);
760                goto data_close_out;
761            }
762        }
763    }
764
765    if(!load_firmware(fw_fd, loading_fd, data_fd))
766        INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
767    else
768        INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
769
770    close(fw_fd);
771data_close_out:
772    close(data_fd);
773loading_close_out:
774    close(loading_fd);
775file_free_out:
776    free(file1);
777    free(file2);
778data_free_out:
779    free(data);
780loading_free_out:
781    free(loading);
782root_free_out:
783    free(root);
784}
785
786static void handle_firmware_event(struct uevent *uevent)
787{
788    pid_t pid;
789    int ret;
790
791    if(strcmp(uevent->subsystem, "firmware"))
792        return;
793
794    if(strcmp(uevent->action, "add"))
795        return;
796
797    /* we fork, to avoid making large memory allocations in init proper */
798    pid = fork();
799    if (!pid) {
800        process_firmware_event(uevent);
801        exit(EXIT_SUCCESS);
802    }
803}
804
805#define UEVENT_MSG_LEN  1024
806void handle_device_fd()
807{
808    char msg[UEVENT_MSG_LEN+2];
809    int n;
810    while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
811        if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */
812            continue;
813
814        msg[n] = '\0';
815        msg[n+1] = '\0';
816
817        struct uevent uevent;
818        parse_event(msg, &uevent);
819
820        handle_device_event(&uevent);
821        handle_firmware_event(&uevent);
822    }
823}
824
825/* Coldboot walks parts of the /sys tree and pokes the uevent files
826** to cause the kernel to regenerate device add events that happened
827** before init's device manager was started
828**
829** We drain any pending events from the netlink socket every time
830** we poke another uevent file to make sure we don't overrun the
831** socket's buffer.
832*/
833
834static void do_coldboot(DIR *d)
835{
836    struct dirent *de;
837    int dfd, fd;
838
839    dfd = dirfd(d);
840
841    fd = openat(dfd, "uevent", O_WRONLY);
842    if(fd >= 0) {
843        write(fd, "add\n", 4);
844        close(fd);
845        handle_device_fd();
846    }
847
848    while((de = readdir(d))) {
849        DIR *d2;
850
851        if(de->d_type != DT_DIR || de->d_name[0] == '.')
852            continue;
853
854        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
855        if(fd < 0)
856            continue;
857
858        d2 = fdopendir(fd);
859        if(d2 == 0)
860            close(fd);
861        else {
862            do_coldboot(d2);
863            closedir(d2);
864        }
865    }
866}
867
868static void coldboot(const char *path)
869{
870    DIR *d = opendir(path);
871    if(d) {
872        do_coldboot(d);
873        closedir(d);
874    }
875}
876
877void device_init(void)
878{
879    suseconds_t t0, t1;
880    struct stat info;
881    int fd;
882#ifdef HAVE_SELINUX
883    sehandle = NULL;
884    if (is_selinux_enabled() > 0) {
885        sehandle = selinux_android_file_context_handle();
886    }
887#endif
888    /* is 64K enough? udev uses 16MB! */
889    device_fd = uevent_open_socket(64*1024, true);
890    if(device_fd < 0)
891        return;
892
893    fcntl(device_fd, F_SETFD, FD_CLOEXEC);
894    fcntl(device_fd, F_SETFL, O_NONBLOCK);
895
896    if (stat(coldboot_done, &info) < 0) {
897        t0 = get_usecs();
898        coldboot("/sys/class");
899        coldboot("/sys/block");
900        coldboot("/sys/devices");
901        t1 = get_usecs();
902        fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
903        close(fd);
904        log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
905    } else {
906        log_event_print("skipping coldboot, already done\n");
907    }
908}
909
910int get_device_fd()
911{
912    return device_fd;
913}
914