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