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