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