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, "drm", 3)) {
604         base = "/dev/dri/";
605         make_dir(base, 0755);
606     } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
607         base = "/dev/oncrpc/";
608         make_dir(base, 0755);
609     } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
610         base = "/dev/adsp/";
611         make_dir(base, 0755);
612     } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
613         base = "/dev/msm_camera/";
614         make_dir(base, 0755);
615     } else if(!strncmp(uevent->subsystem, "input", 5)) {
616         base = "/dev/input/";
617         make_dir(base, 0755);
618     } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
619         base = "/dev/mtd/";
620         make_dir(base, 0755);
621     } else if(!strncmp(uevent->subsystem, "sound", 5)) {
622         base = "/dev/snd/";
623         make_dir(base, 0755);
624     } else if(!strncmp(uevent->subsystem, "misc", 4) &&
625                 !strncmp(name, "log_", 4)) {
626         base = "/dev/log/";
627         make_dir(base, 0755);
628         name += 4;
629     } else
630         base = "/dev/";
631     links = get_character_device_symlinks(uevent);
632
633     if (!devpath[0])
634         snprintf(devpath, sizeof(devpath), "%s%s", base, name);
635
636     handle_device(uevent->action, devpath, uevent->path, 0,
637             uevent->major, uevent->minor, links);
638}
639
640static void handle_device_event(struct uevent *uevent)
641{
642    if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change"))
643        fixup_sys_perms(uevent->path);
644
645    if (!strncmp(uevent->subsystem, "block", 5)) {
646        handle_block_device_event(uevent);
647    } else if (!strncmp(uevent->subsystem, "platform", 8)) {
648        handle_platform_device_event(uevent);
649    } else {
650        handle_generic_device_event(uevent);
651    }
652}
653
654static int load_firmware(int fw_fd, int loading_fd, int data_fd)
655{
656    struct stat st;
657    long len_to_copy;
658    int ret = 0;
659
660    if(fstat(fw_fd, &st) < 0)
661        return -1;
662    len_to_copy = st.st_size;
663
664    write(loading_fd, "1", 1);  /* start transfer */
665
666    while (len_to_copy > 0) {
667        char buf[PAGE_SIZE];
668        ssize_t nr;
669
670        nr = read(fw_fd, buf, sizeof(buf));
671        if(!nr)
672            break;
673        if(nr < 0) {
674            ret = -1;
675            break;
676        }
677
678        len_to_copy -= nr;
679        while (nr > 0) {
680            ssize_t nw = 0;
681
682            nw = write(data_fd, buf + nw, nr);
683            if(nw <= 0) {
684                ret = -1;
685                goto out;
686            }
687            nr -= nw;
688        }
689    }
690
691out:
692    if(!ret)
693        write(loading_fd, "0", 1);  /* successful end of transfer */
694    else
695        write(loading_fd, "-1", 2); /* abort transfer */
696
697    return ret;
698}
699
700static int is_booting(void)
701{
702    return access("/dev/.booting", F_OK) == 0;
703}
704
705static void process_firmware_event(struct uevent *uevent)
706{
707    char *root, *loading, *data, *file1 = NULL, *file2 = NULL, *file3 = NULL;
708    int l, loading_fd, data_fd, fw_fd;
709    int booting = is_booting();
710
711    INFO("firmware: loading '%s' for '%s'\n",
712         uevent->firmware, uevent->path);
713
714    l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
715    if (l == -1)
716        return;
717
718    l = asprintf(&loading, "%sloading", root);
719    if (l == -1)
720        goto root_free_out;
721
722    l = asprintf(&data, "%sdata", root);
723    if (l == -1)
724        goto loading_free_out;
725
726    l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware);
727    if (l == -1)
728        goto data_free_out;
729
730    l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware);
731    if (l == -1)
732        goto data_free_out;
733
734    l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware);
735    if (l == -1)
736        goto data_free_out;
737
738    loading_fd = open(loading, O_WRONLY);
739    if(loading_fd < 0)
740        goto file_free_out;
741
742    data_fd = open(data, O_WRONLY);
743    if(data_fd < 0)
744        goto loading_close_out;
745
746try_loading_again:
747    fw_fd = open(file1, O_RDONLY);
748    if(fw_fd < 0) {
749        fw_fd = open(file2, O_RDONLY);
750        if (fw_fd < 0) {
751            fw_fd = open(file3, O_RDONLY);
752            if (fw_fd < 0) {
753                if (booting) {
754                        /* If we're not fully booted, we may be missing
755                         * filesystems needed for firmware, wait and retry.
756                         */
757                    usleep(100000);
758                    booting = is_booting();
759                    goto try_loading_again;
760                }
761                INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno);
762                write(loading_fd, "-1", 2);
763                goto data_close_out;
764            }
765        }
766    }
767
768    if(!load_firmware(fw_fd, loading_fd, data_fd))
769        INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
770    else
771        INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
772
773    close(fw_fd);
774data_close_out:
775    close(data_fd);
776loading_close_out:
777    close(loading_fd);
778file_free_out:
779    free(file1);
780    free(file2);
781data_free_out:
782    free(data);
783loading_free_out:
784    free(loading);
785root_free_out:
786    free(root);
787}
788
789static void handle_firmware_event(struct uevent *uevent)
790{
791    pid_t pid;
792    int ret;
793
794    if(strcmp(uevent->subsystem, "firmware"))
795        return;
796
797    if(strcmp(uevent->action, "add"))
798        return;
799
800    /* we fork, to avoid making large memory allocations in init proper */
801    pid = fork();
802    if (!pid) {
803        process_firmware_event(uevent);
804        exit(EXIT_SUCCESS);
805    }
806}
807
808#define UEVENT_MSG_LEN  1024
809void handle_device_fd()
810{
811    char msg[UEVENT_MSG_LEN+2];
812    int n;
813    while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
814        if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */
815            continue;
816
817        msg[n] = '\0';
818        msg[n+1] = '\0';
819
820        struct uevent uevent;
821        parse_event(msg, &uevent);
822
823        handle_device_event(&uevent);
824        handle_firmware_event(&uevent);
825    }
826}
827
828/* Coldboot walks parts of the /sys tree and pokes the uevent files
829** to cause the kernel to regenerate device add events that happened
830** before init's device manager was started
831**
832** We drain any pending events from the netlink socket every time
833** we poke another uevent file to make sure we don't overrun the
834** socket's buffer.
835*/
836
837static void do_coldboot(DIR *d)
838{
839    struct dirent *de;
840    int dfd, fd;
841
842    dfd = dirfd(d);
843
844    fd = openat(dfd, "uevent", O_WRONLY);
845    if(fd >= 0) {
846        write(fd, "add\n", 4);
847        close(fd);
848        handle_device_fd();
849    }
850
851    while((de = readdir(d))) {
852        DIR *d2;
853
854        if(de->d_type != DT_DIR || de->d_name[0] == '.')
855            continue;
856
857        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
858        if(fd < 0)
859            continue;
860
861        d2 = fdopendir(fd);
862        if(d2 == 0)
863            close(fd);
864        else {
865            do_coldboot(d2);
866            closedir(d2);
867        }
868    }
869}
870
871static void coldboot(const char *path)
872{
873    DIR *d = opendir(path);
874    if(d) {
875        do_coldboot(d);
876        closedir(d);
877    }
878}
879
880void device_init(void)
881{
882    suseconds_t t0, t1;
883    struct stat info;
884    int fd;
885#ifdef HAVE_SELINUX
886    sehandle = NULL;
887    if (is_selinux_enabled() > 0) {
888        sehandle = selinux_android_file_context_handle();
889    }
890#endif
891    /* is 64K enough? udev uses 16MB! */
892    device_fd = uevent_open_socket(64*1024, true);
893    if(device_fd < 0)
894        return;
895
896    fcntl(device_fd, F_SETFD, FD_CLOEXEC);
897    fcntl(device_fd, F_SETFL, O_NONBLOCK);
898
899    if (stat(coldboot_done, &info) < 0) {
900        t0 = get_usecs();
901        coldboot("/sys/class");
902        coldboot("/sys/block");
903        coldboot("/sys/devices");
904        t1 = get_usecs();
905        fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
906        close(fd);
907        log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
908    } else {
909        log_event_print("skipping coldboot, already done\n");
910    }
911}
912
913int get_device_fd()
914{
915    return device_fd;
916}
917