devices.c revision bc57d4ce925a62f14c28c55e0ff28af1114f12be
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 <stdio.h>
19#include <stdlib.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22
23#include <fcntl.h>
24#include <dirent.h>
25#include <unistd.h>
26#include <string.h>
27
28#include <sys/socket.h>
29#include <sys/un.h>
30#include <linux/netlink.h>
31#include <private/android_filesystem_config.h>
32#include <sys/time.h>
33#include <asm/page.h>
34#include <sys/wait.h>
35
36#include "devices.h"
37#include "util.h"
38#include "log.h"
39#include "list.h"
40
41#define SYSFS_PREFIX    "/sys"
42#define FIRMWARE_DIR1   "/etc/firmware"
43#define FIRMWARE_DIR2   "/vendor/firmware"
44
45static int device_fd = -1;
46
47struct uevent {
48    const char *action;
49    const char *path;
50    const char *subsystem;
51    const char *firmware;
52    const char *partition_name;
53    int partition_num;
54    int major;
55    int minor;
56};
57
58static int open_uevent_socket(void)
59{
60    struct sockaddr_nl addr;
61    int sz = 64*1024; // XXX larger? udev uses 16MB!
62    int on = 1;
63    int s;
64
65    memset(&addr, 0, sizeof(addr));
66    addr.nl_family = AF_NETLINK;
67    addr.nl_pid = getpid();
68    addr.nl_groups = 0xffffffff;
69
70    s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
71    if(s < 0)
72        return -1;
73
74    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
75    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
76
77    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
78        close(s);
79        return -1;
80    }
81
82    return s;
83}
84
85struct perms_ {
86    char *name;
87    char *attr;
88    mode_t perm;
89    unsigned int uid;
90    unsigned int gid;
91    unsigned short prefix;
92};
93
94struct perm_node {
95    struct perms_ dp;
96    struct listnode plist;
97};
98
99static list_declare(sys_perms);
100static list_declare(dev_perms);
101
102int add_dev_perms(const char *name, const char *attr,
103                  mode_t perm, unsigned int uid, unsigned int gid,
104                  unsigned short prefix) {
105    struct perm_node *node = calloc(1, sizeof(*node));
106    if (!node)
107        return -ENOMEM;
108
109    node->dp.name = strdup(name);
110    if (!node->dp.name)
111        return -ENOMEM;
112
113    if (attr) {
114        node->dp.attr = strdup(attr);
115        if (!node->dp.attr)
116            return -ENOMEM;
117    }
118
119    node->dp.perm = perm;
120    node->dp.uid = uid;
121    node->dp.gid = gid;
122    node->dp.prefix = prefix;
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 {
147            if (strcmp(upath, dp->name + 4))
148                continue;
149        }
150
151        if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf))
152            return;
153
154        sprintf(buf,"/sys%s/%s", upath, dp->attr);
155        INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
156        chown(buf, dp->uid, dp->gid);
157        chmod(buf, dp->perm);
158    }
159}
160
161static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
162{
163    mode_t perm;
164    struct listnode *node;
165    struct perm_node *perm_node;
166    struct perms_ *dp;
167
168    /* search the perms list in reverse so that ueventd.$hardware can
169     * override ueventd.rc
170     */
171    list_for_each_reverse(node, &dev_perms) {
172        perm_node = node_to_item(node, struct perm_node, plist);
173        dp = &perm_node->dp;
174
175        if (dp->prefix) {
176            if (strncmp(path, dp->name, strlen(dp->name)))
177                continue;
178        } else {
179            if (strcmp(path, dp->name))
180                continue;
181        }
182        *uid = dp->uid;
183        *gid = dp->gid;
184        return dp->perm;
185    }
186    /* Default if nothing found. */
187    *uid = 0;
188    *gid = 0;
189    return 0600;
190}
191
192static void make_device(const char *path,
193                        const char *upath,
194                        int block, int major, int minor)
195{
196    unsigned uid;
197    unsigned gid;
198    mode_t mode;
199    dev_t dev;
200
201    mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
202    dev = makedev(major, minor);
203    /* Temporarily change egid to avoid race condition setting the gid of the
204     * device node. Unforunately changing the euid would prevent creation of
205     * some device nodes, so the uid has to be set with chown() and is still
206     * racy. Fixing the gid race at least fixed the issue with system_server
207     * opening dynamic input devices under the AID_INPUT gid. */
208    setegid(gid);
209    mknod(path, mode, dev);
210    chown(path, uid, -1);
211    setegid(AID_ROOT);
212}
213
214#if LOG_UEVENTS
215
216static inline suseconds_t get_usecs(void)
217{
218    struct timeval tv;
219    gettimeofday(&tv, 0);
220    return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec;
221}
222
223#define log_event_print(x...) INFO(x)
224
225#else
226
227#define log_event_print(fmt, args...)   do { } while (0)
228#define get_usecs()                     0
229
230#endif
231
232static void parse_event(const char *msg, struct uevent *uevent)
233{
234    uevent->action = "";
235    uevent->path = "";
236    uevent->subsystem = "";
237    uevent->firmware = "";
238    uevent->major = -1;
239    uevent->minor = -1;
240    uevent->partition_name = NULL;
241    uevent->partition_num = -1;
242
243        /* currently ignoring SEQNUM */
244    while(*msg) {
245        if(!strncmp(msg, "ACTION=", 7)) {
246            msg += 7;
247            uevent->action = msg;
248        } else if(!strncmp(msg, "DEVPATH=", 8)) {
249            msg += 8;
250            uevent->path = msg;
251        } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
252            msg += 10;
253            uevent->subsystem = msg;
254        } else if(!strncmp(msg, "FIRMWARE=", 9)) {
255            msg += 9;
256            uevent->firmware = msg;
257        } else if(!strncmp(msg, "MAJOR=", 6)) {
258            msg += 6;
259            uevent->major = atoi(msg);
260        } else if(!strncmp(msg, "MINOR=", 6)) {
261            msg += 6;
262            uevent->minor = atoi(msg);
263        } else if(!strncmp(msg, "PARTN=", 6)) {
264            msg += 6;
265            uevent->partition_num = atoi(msg);
266        } else if(!strncmp(msg, "PARTNAME=", 9)) {
267            msg += 9;
268            uevent->partition_name = msg;
269        }
270
271            /* advance to after the next \0 */
272        while(*msg++)
273            ;
274    }
275
276    log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n",
277                    uevent->action, uevent->path, uevent->subsystem,
278                    uevent->firmware, uevent->major, uevent->minor);
279}
280
281static char **parse_platform_block_device(struct uevent *uevent)
282{
283    const char *driver;
284    const char *path;
285    char *slash;
286    int width;
287    char buf[256];
288    char link_path[256];
289    int fd;
290    int link_num = 0;
291    int ret;
292    char *p;
293    unsigned int size;
294    struct stat info;
295
296    char **links = malloc(sizeof(char *) * 4);
297    if (!links)
298        return NULL;
299    memset(links, 0, sizeof(char *) * 4);
300
301    /* Drop "/devices/platform/" */
302    path = uevent->path;
303    driver = path + 18;
304    slash = strchr(driver, '/');
305    if (!slash)
306        goto err;
307    width = slash - driver;
308    if (width <= 0)
309        goto err;
310
311    snprintf(link_path, sizeof(link_path), "/dev/block/platform/%.*s",
312             width, driver);
313
314    if (uevent->partition_name) {
315        p = strdup(uevent->partition_name);
316        sanitize(p);
317        if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
318            link_num++;
319        else
320            links[link_num] = NULL;
321        free(p);
322    }
323
324    if (uevent->partition_num >= 0) {
325        if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
326            link_num++;
327        else
328            links[link_num] = NULL;
329    }
330
331    slash = strrchr(path, '/');
332    if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
333        link_num++;
334    else
335        links[link_num] = NULL;
336
337    return links;
338
339err:
340    free(links);
341    return NULL;
342}
343
344static void handle_device_event(struct uevent *uevent)
345{
346    char devpath[96];
347    int devpath_ready = 0;
348    char *base, *name;
349    char **links = NULL;
350    int block;
351    int i;
352
353    if (!strcmp(uevent->action,"add"))
354        fixup_sys_perms(uevent->path);
355
356        /* if it's not a /dev device, nothing else to do */
357    if((uevent->major < 0) || (uevent->minor < 0))
358        return;
359
360        /* do we have a name? */
361    name = strrchr(uevent->path, '/');
362    if(!name)
363        return;
364    name++;
365
366        /* too-long names would overrun our buffer */
367    if(strlen(name) > 64)
368        return;
369
370        /* are we block or char? where should we live? */
371    if(!strncmp(uevent->subsystem, "block", 5)) {
372        block = 1;
373        base = "/dev/block/";
374        mkdir(base, 0755);
375        if (!strncmp(uevent->path, "/devices/platform/", 18))
376            links = parse_platform_block_device(uevent);
377    } else {
378        block = 0;
379            /* this should probably be configurable somehow */
380        if (!strncmp(uevent->subsystem, "usb", 3)) {
381            if (!strcmp(uevent->subsystem, "usb")) {
382                /* This imitates the file system that would be created
383                 * if we were using devfs instead.
384                 * Minors are broken up into groups of 128, starting at "001"
385                 */
386                int bus_id = uevent->minor / 128 + 1;
387                int device_id = uevent->minor % 128 + 1;
388                /* build directories */
389                mkdir("/dev/bus", 0755);
390                mkdir("/dev/bus/usb", 0755);
391                snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
392                mkdir(devpath, 0755);
393                snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
394                devpath_ready = 1;
395            } else {
396                /* ignore other USB events */
397                return;
398            }
399        } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
400            base = "/dev/graphics/";
401            mkdir(base, 0755);
402        } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
403            base = "/dev/oncrpc/";
404            mkdir(base, 0755);
405        } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
406            base = "/dev/adsp/";
407            mkdir(base, 0755);
408        } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
409            base = "/dev/msm_camera/";
410            mkdir(base, 0755);
411        } else if(!strncmp(uevent->subsystem, "input", 5)) {
412            base = "/dev/input/";
413            mkdir(base, 0755);
414        } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
415            base = "/dev/mtd/";
416            mkdir(base, 0755);
417        } else if(!strncmp(uevent->subsystem, "sound", 5)) {
418            base = "/dev/snd/";
419            mkdir(base, 0755);
420        } else if(!strncmp(uevent->subsystem, "misc", 4) &&
421                    !strncmp(name, "log_", 4)) {
422            base = "/dev/log/";
423            mkdir(base, 0755);
424            name += 4;
425        } else
426            base = "/dev/";
427    }
428
429    if (!devpath_ready)
430        snprintf(devpath, sizeof(devpath), "%s%s", base, name);
431
432    if(!strcmp(uevent->action, "add")) {
433        make_device(devpath, uevent->path, block, uevent->major, uevent->minor);
434        if (links) {
435            for (i = 0; links[i]; i++)
436                make_link(devpath, links[i]);
437        }
438    }
439
440    if(!strcmp(uevent->action, "remove")) {
441        if (links) {
442            for (i = 0; links[i]; i++)
443                remove_link(devpath, links[i]);
444        }
445        unlink(devpath);
446    }
447
448    if (links) {
449        for (i = 0; links[i]; i++)
450            free(links[i]);
451        free(links);
452    }
453}
454
455static int load_firmware(int fw_fd, int loading_fd, int data_fd)
456{
457    struct stat st;
458    long len_to_copy;
459    int ret = 0;
460
461    if(fstat(fw_fd, &st) < 0)
462        return -1;
463    len_to_copy = st.st_size;
464
465    write(loading_fd, "1", 1);  /* start transfer */
466
467    while (len_to_copy > 0) {
468        char buf[PAGE_SIZE];
469        ssize_t nr;
470
471        nr = read(fw_fd, buf, sizeof(buf));
472        if(!nr)
473            break;
474        if(nr < 0) {
475            ret = -1;
476            break;
477        }
478
479        len_to_copy -= nr;
480        while (nr > 0) {
481            ssize_t nw = 0;
482
483            nw = write(data_fd, buf + nw, nr);
484            if(nw <= 0) {
485                ret = -1;
486                goto out;
487            }
488            nr -= nw;
489        }
490    }
491
492out:
493    if(!ret)
494        write(loading_fd, "0", 1);  /* successful end of transfer */
495    else
496        write(loading_fd, "-1", 2); /* abort transfer */
497
498    return ret;
499}
500
501static void process_firmware_event(struct uevent *uevent)
502{
503    char *root, *loading, *data, *file1 = NULL, *file2 = NULL;
504    int l, loading_fd, data_fd, fw_fd;
505
506    log_event_print("firmware event { '%s', '%s' }\n",
507                    uevent->path, uevent->firmware);
508
509    l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
510    if (l == -1)
511        return;
512
513    l = asprintf(&loading, "%sloading", root);
514    if (l == -1)
515        goto root_free_out;
516
517    l = asprintf(&data, "%sdata", root);
518    if (l == -1)
519        goto loading_free_out;
520
521    l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware);
522    if (l == -1)
523        goto data_free_out;
524
525    l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware);
526    if (l == -1)
527        goto data_free_out;
528
529    loading_fd = open(loading, O_WRONLY);
530    if(loading_fd < 0)
531        goto file_free_out;
532
533    data_fd = open(data, O_WRONLY);
534    if(data_fd < 0)
535        goto loading_close_out;
536
537    fw_fd = open(file1, O_RDONLY);
538    if(fw_fd < 0) {
539        fw_fd = open(file2, O_RDONLY);
540        if(fw_fd < 0)
541            goto data_close_out;
542    }
543
544    if(!load_firmware(fw_fd, loading_fd, data_fd))
545        log_event_print("firmware copy success { '%s', '%s' }\n", root, uevent->firmware);
546    else
547        log_event_print("firmware copy failure { '%s', '%s' }\n", root, uevent->firmware);
548
549    close(fw_fd);
550data_close_out:
551    close(data_fd);
552loading_close_out:
553    close(loading_fd);
554file_free_out:
555    free(file1);
556    free(file2);
557data_free_out:
558    free(data);
559loading_free_out:
560    free(loading);
561root_free_out:
562    free(root);
563}
564
565static void handle_firmware_event(struct uevent *uevent)
566{
567    pid_t pid;
568    int status;
569    int ret;
570
571    if(strcmp(uevent->subsystem, "firmware"))
572        return;
573
574    if(strcmp(uevent->action, "add"))
575        return;
576
577    /* we fork, to avoid making large memory allocations in init proper */
578    pid = fork();
579    if (!pid) {
580        process_firmware_event(uevent);
581        exit(EXIT_SUCCESS);
582    } else {
583        do {
584            ret = waitpid(pid, &status, 0);
585        } while (ret == -1 && errno == EINTR);
586    }
587}
588
589#define UEVENT_MSG_LEN  1024
590void handle_device_fd()
591{
592    for(;;) {
593        char msg[UEVENT_MSG_LEN+2];
594        char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
595        struct iovec iov = {msg, sizeof(msg)};
596        struct sockaddr_nl snl;
597        struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0};
598
599        ssize_t n = recvmsg(device_fd, &hdr, 0);
600        if (n <= 0) {
601            break;
602        }
603
604        if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) {
605            /* ignoring non-kernel netlink multicast message */
606            continue;
607        }
608
609        struct cmsghdr * cmsg = CMSG_FIRSTHDR(&hdr);
610        if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
611            /* no sender credentials received, ignore message */
612            continue;
613        }
614
615        struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg);
616        if (cred->uid != 0) {
617            /* message from non-root user, ignore */
618            continue;
619        }
620
621        if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */
622            continue;
623
624        msg[n] = '\0';
625        msg[n+1] = '\0';
626
627        struct uevent uevent;
628        parse_event(msg, &uevent);
629
630        handle_device_event(&uevent);
631        handle_firmware_event(&uevent);
632    }
633}
634
635/* Coldboot walks parts of the /sys tree and pokes the uevent files
636** to cause the kernel to regenerate device add events that happened
637** before init's device manager was started
638**
639** We drain any pending events from the netlink socket every time
640** we poke another uevent file to make sure we don't overrun the
641** socket's buffer.
642*/
643
644static void do_coldboot(DIR *d)
645{
646    struct dirent *de;
647    int dfd, fd;
648
649    dfd = dirfd(d);
650
651    fd = openat(dfd, "uevent", O_WRONLY);
652    if(fd >= 0) {
653        write(fd, "add\n", 4);
654        close(fd);
655        handle_device_fd();
656    }
657
658    while((de = readdir(d))) {
659        DIR *d2;
660
661        if(de->d_type != DT_DIR || de->d_name[0] == '.')
662            continue;
663
664        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
665        if(fd < 0)
666            continue;
667
668        d2 = fdopendir(fd);
669        if(d2 == 0)
670            close(fd);
671        else {
672            do_coldboot(d2);
673            closedir(d2);
674        }
675    }
676}
677
678static void coldboot(const char *path)
679{
680    DIR *d = opendir(path);
681    if(d) {
682        do_coldboot(d);
683        closedir(d);
684    }
685}
686
687void device_init(void)
688{
689    suseconds_t t0, t1;
690    struct stat info;
691    int fd;
692
693    device_fd = open_uevent_socket();
694    if(device_fd < 0)
695        return;
696
697    fcntl(device_fd, F_SETFD, FD_CLOEXEC);
698    fcntl(device_fd, F_SETFL, O_NONBLOCK);
699
700    if (stat(coldboot_done, &info) < 0) {
701        t0 = get_usecs();
702        coldboot("/sys/class");
703        coldboot("/sys/block");
704        coldboot("/sys/devices");
705        t1 = get_usecs();
706        fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
707        close(fd);
708        log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
709    } else {
710        log_event_print("skipping coldboot, already done\n");
711    }
712}
713
714int get_device_fd()
715{
716    return device_fd;
717}
718