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