• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
45 static int device_fd = -1;
46 
47 struct 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 
open_uevent_socket(void)58 static 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 
85 struct 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 
94 struct perm_node {
95     struct perms_ dp;
96     struct listnode plist;
97 };
98 
99 static list_declare(sys_perms);
100 static list_declare(dev_perms);
101 
add_dev_perms(const char * name,const char * attr,mode_t perm,unsigned int uid,unsigned int gid,unsigned short prefix)102 int 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 
fixup_sys_perms(const char * upath)132 void 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 
get_device_perm(const char * path,unsigned * uid,unsigned * gid)161 static 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 
make_device(const char * path,const char * upath,int block,int major,int minor)192 static 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 
get_usecs(void)216 static 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 
parse_event(const char * msg,struct uevent * uevent)232 static 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 
parse_platform_block_device(struct uevent * uevent)281 static 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 
339 err:
340     free(links);
341     return NULL;
342 }
343 
handle_device_event(struct uevent * uevent)344 static 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 
load_firmware(int fw_fd,int loading_fd,int data_fd)455 static 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 
492 out:
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 
process_firmware_event(struct uevent * uevent)501 static 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);
550 data_close_out:
551     close(data_fd);
552 loading_close_out:
553     close(loading_fd);
554 file_free_out:
555     free(file1);
556     free(file2);
557 data_free_out:
558     free(data);
559 loading_free_out:
560     free(loading);
561 root_free_out:
562     free(root);
563 }
564 
handle_firmware_event(struct uevent * uevent)565 static 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
handle_device_fd()590 void 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 
do_coldboot(DIR * d)644 static 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 
coldboot(const char * path)678 static void coldboot(const char *path)
679 {
680     DIR *d = opendir(path);
681     if(d) {
682         do_coldboot(d);
683         closedir(d);
684     }
685 }
686 
device_init(void)687 void 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 
get_device_fd()714 int get_device_fd()
715 {
716     return device_fd;
717 }
718