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