devices.cpp revision 17560f8884a01f12ac3bea15246d10c4eea19638
1/*
2 * Copyright (C) 2007-2014 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 <fnmatch.h>
19#include <stddef.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24
25#include <fcntl.h>
26#include <dirent.h>
27#include <unistd.h>
28#include <string.h>
29
30#include <sys/socket.h>
31#include <sys/un.h>
32#include <linux/netlink.h>
33
34#include <selinux/selinux.h>
35#include <selinux/label.h>
36#include <selinux/android.h>
37#include <selinux/avc.h>
38
39#include <private/android_filesystem_config.h>
40#include <sys/time.h>
41#include <sys/wait.h>
42
43#include <cutils/list.h>
44#include <cutils/uevent.h>
45
46#include "devices.h"
47#include "ueventd_parser.h"
48#include "util.h"
49#include "log.h"
50
51#define SYSFS_PREFIX    "/sys"
52static const char *firmware_dirs[] = { "/etc/firmware",
53                                       "/vendor/firmware",
54                                       "/firmware/image" };
55
56extern struct selabel_handle *sehandle;
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    unsigned short wildcard;
80};
81
82struct perm_node {
83    struct perms_ dp;
84    struct listnode plist;
85};
86
87struct platform_node {
88    char *name;
89    char *path;
90    int path_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                  unsigned short wildcard) {
102    struct perm_node *node = (perm_node*) calloc(1, sizeof(*node));
103    if (!node)
104        return -ENOMEM;
105
106    node->dp.name = strdup(name);
107    if (!node->dp.name)
108        return -ENOMEM;
109
110    if (attr) {
111        node->dp.attr = strdup(attr);
112        if (!node->dp.attr)
113            return -ENOMEM;
114    }
115
116    node->dp.perm = perm;
117    node->dp.uid = uid;
118    node->dp.gid = gid;
119    node->dp.prefix = prefix;
120    node->dp.wildcard = wildcard;
121
122    if (attr)
123        list_add_tail(&sys_perms, &node->plist);
124    else
125        list_add_tail(&dev_perms, &node->plist);
126
127    return 0;
128}
129
130void fixup_sys_perms(const char *upath)
131{
132    char buf[512];
133    struct listnode *node;
134    struct perms_ *dp;
135
136    /* upaths omit the "/sys" that paths in this list
137     * contain, so we add 4 when comparing...
138     */
139    list_for_each(node, &sys_perms) {
140        dp = &(node_to_item(node, struct perm_node, plist))->dp;
141        if (dp->prefix) {
142            if (strncmp(upath, dp->name + 4, strlen(dp->name + 4)))
143                continue;
144        } else if (dp->wildcard) {
145            if (fnmatch(dp->name + 4, upath, FNM_PATHNAME) != 0)
146                continue;
147        } else {
148            if (strcmp(upath, dp->name + 4))
149                continue;
150        }
151
152        if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf))
153            break;
154
155        snprintf(buf, sizeof(buf), "/sys%s/%s", upath, dp->attr);
156        INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
157        chown(buf, dp->uid, dp->gid);
158        chmod(buf, dp->perm);
159    }
160
161    // Now fixup SELinux file labels
162    int len = snprintf(buf, sizeof(buf), "/sys%s", upath);
163    if ((len < 0) || ((size_t) len >= sizeof(buf))) {
164        // Overflow
165        return;
166    }
167    if (access(buf, F_OK) == 0) {
168        INFO("restorecon_recursive: %s\n", buf);
169        restorecon_recursive(buf);
170    }
171}
172
173static bool perm_path_matches(const char *path, struct perms_ *dp)
174{
175    if (dp->prefix) {
176        if (strncmp(path, dp->name, strlen(dp->name)) == 0)
177            return true;
178    } else if (dp->wildcard) {
179        if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
180            return true;
181    } else {
182        if (strcmp(path, dp->name) == 0)
183            return true;
184    }
185
186    return false;
187}
188
189static mode_t get_device_perm(const char *path, const char **links,
190                unsigned *uid, unsigned *gid)
191{
192    struct listnode *node;
193    struct perm_node *perm_node;
194    struct perms_ *dp;
195
196    /* search the perms list in reverse so that ueventd.$hardware can
197     * override ueventd.rc
198     */
199    list_for_each_reverse(node, &dev_perms) {
200        bool match = false;
201
202        perm_node = node_to_item(node, struct perm_node, plist);
203        dp = &perm_node->dp;
204
205        if (perm_path_matches(path, dp)) {
206            match = true;
207        } else {
208            if (links) {
209                int i;
210                for (i = 0; links[i]; i++) {
211                    if (perm_path_matches(links[i], dp)) {
212                        match = true;
213                        break;
214                    }
215                }
216            }
217        }
218
219        if (match) {
220            *uid = dp->uid;
221            *gid = dp->gid;
222            return dp->perm;
223        }
224    }
225    /* Default if nothing found. */
226    *uid = 0;
227    *gid = 0;
228    return 0600;
229}
230
231static void make_device(const char *path,
232                        const char */*upath*/,
233                        int block, int major, int minor,
234                        const char **links)
235{
236    unsigned uid;
237    unsigned gid;
238    mode_t mode;
239    dev_t dev;
240    char *secontext = NULL;
241
242    mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
243
244    if (sehandle) {
245        selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
246        setfscreatecon(secontext);
247    }
248
249    dev = makedev(major, minor);
250    /* Temporarily change egid to avoid race condition setting the gid of the
251     * device node. Unforunately changing the euid would prevent creation of
252     * some device nodes, so the uid has to be set with chown() and is still
253     * racy. Fixing the gid race at least fixed the issue with system_server
254     * opening dynamic input devices under the AID_INPUT gid. */
255    setegid(gid);
256    mknod(path, mode, dev);
257    chown(path, uid, -1);
258    setegid(AID_ROOT);
259
260    if (secontext) {
261        freecon(secontext);
262        setfscreatecon(NULL);
263    }
264}
265
266static void add_platform_device(const char *path)
267{
268    int path_len = strlen(path);
269    struct platform_node *bus;
270    const char *name = path;
271
272    if (!strncmp(path, "/devices/", 9)) {
273        name += 9;
274        if (!strncmp(name, "platform/", 9))
275            name += 9;
276    }
277
278    INFO("adding platform device %s (%s)\n", name, path);
279
280    bus = (platform_node*) calloc(1, sizeof(struct platform_node));
281    bus->path = strdup(path);
282    bus->path_len = path_len;
283    bus->name = bus->path + (name - path);
284    list_add_tail(&platform_names, &bus->list);
285}
286
287/*
288 * given a path that may start with a platform device, find the length of the
289 * platform device prefix.  If it doesn't start with a platform device, return
290 * 0.
291 */
292static struct platform_node *find_platform_device(const char *path)
293{
294    int path_len = strlen(path);
295    struct listnode *node;
296    struct platform_node *bus;
297
298    list_for_each_reverse(node, &platform_names) {
299        bus = node_to_item(node, struct platform_node, list);
300        if ((bus->path_len < path_len) &&
301                (path[bus->path_len] == '/') &&
302                !strncmp(path, bus->path, bus->path_len))
303            return bus;
304    }
305
306    return NULL;
307}
308
309static void remove_platform_device(const char *path)
310{
311    struct listnode *node;
312    struct platform_node *bus;
313
314    list_for_each_reverse(node, &platform_names) {
315        bus = node_to_item(node, struct platform_node, list);
316        if (!strcmp(path, bus->path)) {
317            INFO("removing platform device %s\n", bus->name);
318            free(bus->path);
319            list_remove(node);
320            free(bus);
321            return;
322        }
323    }
324}
325
326/* Given a path that may start with a PCI device, populate the supplied buffer
327 * with the PCI domain/bus number and the peripheral ID and return 0.
328 * If it doesn't start with a PCI device, or there is some error, return -1 */
329static int find_pci_device_prefix(const char *path, char *buf, ssize_t buf_sz)
330{
331    const char *start, *end;
332
333    if (strncmp(path, "/devices/pci", 12))
334        return -1;
335
336    /* Beginning of the prefix is the initial "pci" after "/devices/" */
337    start = path + 9;
338
339    /* End of the prefix is two path '/' later, capturing the domain/bus number
340     * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
341    end = strchr(start, '/');
342    if (!end)
343        return -1;
344    end = strchr(end + 1, '/');
345    if (!end)
346        return -1;
347
348    /* Make sure we have enough room for the string plus null terminator */
349    if (end - start + 1 > buf_sz)
350        return -1;
351
352    strncpy(buf, start, end - start);
353    buf[end - start] = '\0';
354    return 0;
355}
356
357static void parse_event(const char *msg, struct uevent *uevent)
358{
359    uevent->action = "";
360    uevent->path = "";
361    uevent->subsystem = "";
362    uevent->firmware = "";
363    uevent->major = -1;
364    uevent->minor = -1;
365    uevent->partition_name = NULL;
366    uevent->partition_num = -1;
367    uevent->device_name = NULL;
368
369        /* currently ignoring SEQNUM */
370    while(*msg) {
371        if(!strncmp(msg, "ACTION=", 7)) {
372            msg += 7;
373            uevent->action = msg;
374        } else if(!strncmp(msg, "DEVPATH=", 8)) {
375            msg += 8;
376            uevent->path = msg;
377        } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
378            msg += 10;
379            uevent->subsystem = msg;
380        } else if(!strncmp(msg, "FIRMWARE=", 9)) {
381            msg += 9;
382            uevent->firmware = msg;
383        } else if(!strncmp(msg, "MAJOR=", 6)) {
384            msg += 6;
385            uevent->major = atoi(msg);
386        } else if(!strncmp(msg, "MINOR=", 6)) {
387            msg += 6;
388            uevent->minor = atoi(msg);
389        } else if(!strncmp(msg, "PARTN=", 6)) {
390            msg += 6;
391            uevent->partition_num = atoi(msg);
392        } else if(!strncmp(msg, "PARTNAME=", 9)) {
393            msg += 9;
394            uevent->partition_name = msg;
395        } else if(!strncmp(msg, "DEVNAME=", 8)) {
396            msg += 8;
397            uevent->device_name = msg;
398        }
399
400        /* advance to after the next \0 */
401        while(*msg++)
402            ;
403    }
404
405    if (LOG_UEVENTS) {
406        INFO("event { '%s', '%s', '%s', '%s', %d, %d }\n",
407             uevent->action, uevent->path, uevent->subsystem,
408             uevent->firmware, uevent->major, uevent->minor);
409    }
410}
411
412static char **get_character_device_symlinks(struct uevent *uevent)
413{
414    const char *parent;
415    char *slash;
416    char **links;
417    int link_num = 0;
418    int width;
419    struct platform_node *pdev;
420
421    pdev = find_platform_device(uevent->path);
422    if (!pdev)
423        return NULL;
424
425    links = (char**) malloc(sizeof(char *) * 2);
426    if (!links)
427        return NULL;
428    memset(links, 0, sizeof(char *) * 2);
429
430    /* skip "/devices/platform/<driver>" */
431    parent = strchr(uevent->path + pdev->path_len, '/');
432    if (!parent)
433        goto err;
434
435    if (!strncmp(parent, "/usb", 4)) {
436        /* skip root hub name and device. use device interface */
437        while (*++parent && *parent != '/');
438        if (*parent)
439            while (*++parent && *parent != '/');
440        if (!*parent)
441            goto err;
442        slash = strchr(++parent, '/');
443        if (!slash)
444            goto err;
445        width = slash - parent;
446        if (width <= 0)
447            goto err;
448
449        if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0)
450            link_num++;
451        else
452            links[link_num] = NULL;
453        mkdir("/dev/usb", 0755);
454    }
455    else {
456        goto err;
457    }
458
459    return links;
460err:
461    free(links);
462    return NULL;
463}
464
465static char **get_block_device_symlinks(struct uevent *uevent)
466{
467    const char *device;
468    struct platform_node *pdev;
469    char *slash;
470    const char *type;
471    char buf[256];
472    char link_path[256];
473    int link_num = 0;
474    char *p;
475
476    pdev = find_platform_device(uevent->path);
477    if (pdev) {
478        device = pdev->name;
479        type = "platform";
480    } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) {
481        device = buf;
482        type = "pci";
483    } else {
484        return NULL;
485    }
486
487    char **links = (char**) malloc(sizeof(char *) * 4);
488    if (!links)
489        return NULL;
490    memset(links, 0, sizeof(char *) * 4);
491
492    INFO("found %s device %s\n", type, device);
493
494    snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
495
496    if (uevent->partition_name) {
497        p = strdup(uevent->partition_name);
498        sanitize(p);
499        if (strcmp(uevent->partition_name, p))
500            NOTICE("Linking partition '%s' as '%s'\n", uevent->partition_name, p);
501        if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
502            link_num++;
503        else
504            links[link_num] = NULL;
505        free(p);
506    }
507
508    if (uevent->partition_num >= 0) {
509        if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
510            link_num++;
511        else
512            links[link_num] = NULL;
513    }
514
515    slash = strrchr(uevent->path, '/');
516    if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
517        link_num++;
518    else
519        links[link_num] = NULL;
520
521    return links;
522}
523
524static void handle_device(const char *action, const char *devpath,
525        const char *path, int block, int major, int minor, char **links)
526{
527    int i;
528
529    if(!strcmp(action, "add")) {
530        make_device(devpath, path, block, major, minor, (const char **)links);
531        if (links) {
532            for (i = 0; links[i]; i++)
533                make_link_init(devpath, links[i]);
534        }
535    }
536
537    if(!strcmp(action, "remove")) {
538        if (links) {
539            for (i = 0; links[i]; i++)
540                remove_link(devpath, links[i]);
541        }
542        unlink(devpath);
543    }
544
545    if (links) {
546        for (i = 0; links[i]; i++)
547            free(links[i]);
548        free(links);
549    }
550}
551
552static void handle_platform_device_event(struct uevent *uevent)
553{
554    const char *path = uevent->path;
555
556    if (!strcmp(uevent->action, "add"))
557        add_platform_device(path);
558    else if (!strcmp(uevent->action, "remove"))
559        remove_platform_device(path);
560}
561
562static const char *parse_device_name(struct uevent *uevent, unsigned int len)
563{
564    const char *name;
565
566    /* if it's not a /dev device, nothing else to do */
567    if((uevent->major < 0) || (uevent->minor < 0))
568        return NULL;
569
570    /* do we have a name? */
571    name = strrchr(uevent->path, '/');
572    if(!name)
573        return NULL;
574    name++;
575
576    /* too-long names would overrun our buffer */
577    if(strlen(name) > len) {
578        ERROR("DEVPATH=%s exceeds %u-character limit on filename; ignoring event\n",
579                name, len);
580        return NULL;
581    }
582
583    return name;
584}
585
586static void handle_block_device_event(struct uevent *uevent)
587{
588    const char *base = "/dev/block/";
589    const char *name;
590    char devpath[96];
591    char **links = NULL;
592
593    name = parse_device_name(uevent, 64);
594    if (!name)
595        return;
596
597    snprintf(devpath, sizeof(devpath), "%s%s", base, name);
598    make_dir(base, 0755);
599
600    if (!strncmp(uevent->path, "/devices/", 9))
601        links = get_block_device_symlinks(uevent);
602
603    handle_device(uevent->action, devpath, uevent->path, 1,
604            uevent->major, uevent->minor, links);
605}
606
607#define DEVPATH_LEN 96
608
609static bool assemble_devpath(char *devpath, const char *dirname,
610        const char *devname)
611{
612    int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname);
613    if (s < 0) {
614        ERROR("failed to assemble device path (%s); ignoring event\n",
615                strerror(errno));
616        return false;
617    } else if (s >= DEVPATH_LEN) {
618        ERROR("%s/%s exceeds %u-character limit on path; ignoring event\n",
619                dirname, devname, DEVPATH_LEN);
620        return false;
621    }
622    return true;
623}
624
625static void mkdir_recursive_for_devpath(const char *devpath)
626{
627    char dir[DEVPATH_LEN];
628    char *slash;
629
630    strcpy(dir, devpath);
631    slash = strrchr(dir, '/');
632    *slash = '\0';
633    mkdir_recursive(dir, 0755);
634}
635
636static void handle_generic_device_event(struct uevent *uevent)
637{
638    const char *base;
639    const char *name;
640    char devpath[DEVPATH_LEN] = {0};
641    char **links = NULL;
642
643    name = parse_device_name(uevent, 64);
644    if (!name)
645        return;
646
647    struct ueventd_subsystem *subsystem =
648            ueventd_subsystem_find_by_name(uevent->subsystem);
649
650    if (subsystem) {
651        const char *devname;
652
653        switch (subsystem->devname_src) {
654        case DEVNAME_UEVENT_DEVNAME:
655            devname = uevent->device_name;
656            break;
657
658        case DEVNAME_UEVENT_DEVPATH:
659            devname = name;
660            break;
661
662        default:
663            ERROR("%s subsystem's devpath option is not set; ignoring event\n",
664                    uevent->subsystem);
665            return;
666        }
667
668        if (!assemble_devpath(devpath, subsystem->dirname, devname))
669            return;
670        mkdir_recursive_for_devpath(devpath);
671    } else if (!strncmp(uevent->subsystem, "usb", 3)) {
672         if (!strcmp(uevent->subsystem, "usb")) {
673            if (uevent->device_name) {
674                if (!assemble_devpath(devpath, "/dev", uevent->device_name))
675                    return;
676                mkdir_recursive_for_devpath(devpath);
677             }
678             else {
679                 /* This imitates the file system that would be created
680                  * if we were using devfs instead.
681                  * Minors are broken up into groups of 128, starting at "001"
682                  */
683                 int bus_id = uevent->minor / 128 + 1;
684                 int device_id = uevent->minor % 128 + 1;
685                 /* build directories */
686                 make_dir("/dev/bus", 0755);
687                 make_dir("/dev/bus/usb", 0755);
688                 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
689                 make_dir(devpath, 0755);
690                 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
691             }
692         } else {
693             /* ignore other USB events */
694             return;
695         }
696     } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
697         base = "/dev/graphics/";
698         make_dir(base, 0755);
699     } else if (!strncmp(uevent->subsystem, "drm", 3)) {
700         base = "/dev/dri/";
701         make_dir(base, 0755);
702     } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
703         base = "/dev/oncrpc/";
704         make_dir(base, 0755);
705     } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
706         base = "/dev/adsp/";
707         make_dir(base, 0755);
708     } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
709         base = "/dev/msm_camera/";
710         make_dir(base, 0755);
711     } else if(!strncmp(uevent->subsystem, "input", 5)) {
712         base = "/dev/input/";
713         make_dir(base, 0755);
714     } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
715         base = "/dev/mtd/";
716         make_dir(base, 0755);
717     } else if(!strncmp(uevent->subsystem, "sound", 5)) {
718         base = "/dev/snd/";
719         make_dir(base, 0755);
720     } else if(!strncmp(uevent->subsystem, "misc", 4) &&
721                 !strncmp(name, "log_", 4)) {
722         INFO("kernel logger is deprecated\n");
723         base = "/dev/log/";
724         make_dir(base, 0755);
725         name += 4;
726     } else
727         base = "/dev/";
728     links = get_character_device_symlinks(uevent);
729
730     if (!devpath[0])
731         snprintf(devpath, sizeof(devpath), "%s%s", base, name);
732
733     handle_device(uevent->action, devpath, uevent->path, 0,
734             uevent->major, uevent->minor, links);
735}
736
737static void handle_device_event(struct uevent *uevent)
738{
739    if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
740        fixup_sys_perms(uevent->path);
741
742    if (!strncmp(uevent->subsystem, "block", 5)) {
743        handle_block_device_event(uevent);
744    } else if (!strncmp(uevent->subsystem, "platform", 8)) {
745        handle_platform_device_event(uevent);
746    } else {
747        handle_generic_device_event(uevent);
748    }
749}
750
751static int load_firmware(int fw_fd, int loading_fd, int data_fd)
752{
753    struct stat st;
754    long len_to_copy;
755    int ret = 0;
756
757    if(fstat(fw_fd, &st) < 0)
758        return -1;
759    len_to_copy = st.st_size;
760
761    write(loading_fd, "1", 1);  /* start transfer */
762
763    while (len_to_copy > 0) {
764        char buf[PAGE_SIZE];
765        ssize_t nr;
766
767        nr = read(fw_fd, buf, sizeof(buf));
768        if(!nr)
769            break;
770        if(nr < 0) {
771            ret = -1;
772            break;
773        }
774
775        len_to_copy -= nr;
776        while (nr > 0) {
777            ssize_t nw = 0;
778
779            nw = write(data_fd, buf + nw, nr);
780            if(nw <= 0) {
781                ret = -1;
782                goto out;
783            }
784            nr -= nw;
785        }
786    }
787
788out:
789    if(!ret)
790        write(loading_fd, "0", 1);  /* successful end of transfer */
791    else
792        write(loading_fd, "-1", 2); /* abort transfer */
793
794    return ret;
795}
796
797static int is_booting(void)
798{
799    return access("/dev/.booting", F_OK) == 0;
800}
801
802static void process_firmware_event(struct uevent *uevent)
803{
804    char *root, *loading, *data;
805    int l, loading_fd, data_fd, fw_fd;
806    size_t i;
807    int booting = is_booting();
808
809    INFO("firmware: loading '%s' for '%s'\n",
810         uevent->firmware, uevent->path);
811
812    l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
813    if (l == -1)
814        return;
815
816    l = asprintf(&loading, "%sloading", root);
817    if (l == -1)
818        goto root_free_out;
819
820    l = asprintf(&data, "%sdata", root);
821    if (l == -1)
822        goto loading_free_out;
823
824    loading_fd = open(loading, O_WRONLY|O_CLOEXEC);
825    if(loading_fd < 0)
826        goto data_free_out;
827
828    data_fd = open(data, O_WRONLY|O_CLOEXEC);
829    if(data_fd < 0)
830        goto loading_close_out;
831
832try_loading_again:
833    for (i = 0; i < ARRAY_SIZE(firmware_dirs); i++) {
834        char *file = NULL;
835        l = asprintf(&file, "%s/%s", firmware_dirs[i], uevent->firmware);
836        if (l == -1)
837            goto data_free_out;
838        fw_fd = open(file, O_RDONLY|O_CLOEXEC);
839        free(file);
840        if (fw_fd >= 0) {
841            if(!load_firmware(fw_fd, loading_fd, data_fd))
842                INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware);
843            else
844                INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware);
845            break;
846        }
847    }
848    if (fw_fd < 0) {
849        if (booting) {
850            /* If we're not fully booted, we may be missing
851             * filesystems needed for firmware, wait and retry.
852             */
853            usleep(100000);
854            booting = is_booting();
855            goto try_loading_again;
856        }
857        INFO("firmware: could not open '%s': %s\n", uevent->firmware, strerror(errno));
858        write(loading_fd, "-1", 2);
859        goto data_close_out;
860    }
861
862    close(fw_fd);
863data_close_out:
864    close(data_fd);
865loading_close_out:
866    close(loading_fd);
867data_free_out:
868    free(data);
869loading_free_out:
870    free(loading);
871root_free_out:
872    free(root);
873}
874
875static void handle_firmware_event(struct uevent *uevent)
876{
877    pid_t pid;
878
879    if(strcmp(uevent->subsystem, "firmware"))
880        return;
881
882    if(strcmp(uevent->action, "add"))
883        return;
884
885    /* we fork, to avoid making large memory allocations in init proper */
886    pid = fork();
887    if (!pid) {
888        process_firmware_event(uevent);
889        _exit(EXIT_SUCCESS);
890    } else if (pid < 0) {
891        ERROR("could not fork to process firmware event: %s\n", strerror(errno));
892    }
893}
894
895#define UEVENT_MSG_LEN  2048
896void handle_device_fd()
897{
898    char msg[UEVENT_MSG_LEN+2];
899    int n;
900    while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
901        if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */
902            continue;
903
904        msg[n] = '\0';
905        msg[n+1] = '\0';
906
907        struct uevent uevent;
908        parse_event(msg, &uevent);
909
910        if (sehandle && selinux_status_updated() > 0) {
911            struct selabel_handle *sehandle2;
912            sehandle2 = selinux_android_file_context_handle();
913            if (sehandle2) {
914                selabel_close(sehandle);
915                sehandle = sehandle2;
916            }
917        }
918
919        handle_device_event(&uevent);
920        handle_firmware_event(&uevent);
921    }
922}
923
924/* Coldboot walks parts of the /sys tree and pokes the uevent files
925** to cause the kernel to regenerate device add events that happened
926** before init's device manager was started
927**
928** We drain any pending events from the netlink socket every time
929** we poke another uevent file to make sure we don't overrun the
930** socket's buffer.
931*/
932
933static void do_coldboot(DIR *d)
934{
935    struct dirent *de;
936    int dfd, fd;
937
938    dfd = dirfd(d);
939
940    fd = openat(dfd, "uevent", O_WRONLY);
941    if(fd >= 0) {
942        write(fd, "add\n", 4);
943        close(fd);
944        handle_device_fd();
945    }
946
947    while((de = readdir(d))) {
948        DIR *d2;
949
950        if(de->d_type != DT_DIR || de->d_name[0] == '.')
951            continue;
952
953        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
954        if(fd < 0)
955            continue;
956
957        d2 = fdopendir(fd);
958        if(d2 == 0)
959            close(fd);
960        else {
961            do_coldboot(d2);
962            closedir(d2);
963        }
964    }
965}
966
967static void coldboot(const char *path)
968{
969    DIR *d = opendir(path);
970    if(d) {
971        do_coldboot(d);
972        closedir(d);
973    }
974}
975
976void device_init() {
977    sehandle = NULL;
978    if (is_selinux_enabled() > 0) {
979        sehandle = selinux_android_file_context_handle();
980        selinux_status_open(true);
981    }
982
983    /* is 256K enough? udev uses 16MB! */
984    device_fd = uevent_open_socket(256*1024, true);
985    if (device_fd == -1) {
986        return;
987    }
988    fcntl(device_fd, F_SETFL, O_NONBLOCK);
989
990    if (access(COLDBOOT_DONE, F_OK) == 0) {
991        NOTICE("Skipping coldboot, already done!\n");
992        return;
993    }
994
995    Timer t;
996    coldboot("/sys/class");
997    coldboot("/sys/block");
998    coldboot("/sys/devices");
999    close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
1000    NOTICE("Coldboot took %.2fs.\n", t.duration());
1001}
1002
1003int get_device_fd()
1004{
1005    return device_fd;
1006}
1007