blob: 21e1ee31382e38261224042d2d20a1ade9ee5750 [file] [log] [blame]
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -07001/*
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>
Colin Cross982a8152010-06-03 12:21:01 -070034#include <sys/wait.h>
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070035
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070036#include "devices.h"
Colin Crossb0ab94b2010-04-08 16:16:20 -070037#include "util.h"
Colin Crossed8a7d82010-04-19 17:05:34 -070038#include "log.h"
39#include "list.h"
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070040
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070041#define SYSFS_PREFIX "/sys"
42#define FIRMWARE_DIR "/etc/firmware"
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070043
Colin Cross0dd7ca62010-04-13 19:25:51 -070044static int device_fd = -1;
45
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070046struct uevent {
47 const char *action;
48 const char *path;
49 const char *subsystem;
50 const char *firmware;
Colin Crossb0ab94b2010-04-08 16:16:20 -070051 const char *partition_name;
52 int partition_num;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070053 int major;
54 int minor;
55};
56
57static int open_uevent_socket(void)
58{
59 struct sockaddr_nl addr;
60 int sz = 64*1024; // XXX larger? udev uses 16MB!
Nick Kralevich5f5d5c82010-07-19 14:31:20 -070061 int on = 1;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070062 int s;
63
64 memset(&addr, 0, sizeof(addr));
65 addr.nl_family = AF_NETLINK;
66 addr.nl_pid = getpid();
67 addr.nl_groups = 0xffffffff;
68
69 s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
70 if(s < 0)
71 return -1;
72
73 setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz));
Nick Kralevich5f5d5c82010-07-19 14:31:20 -070074 setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070075
76 if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
77 close(s);
78 return -1;
79 }
80
81 return s;
82}
83
84struct perms_ {
85 char *name;
86 mode_t perm;
87 unsigned int uid;
88 unsigned int gid;
89 unsigned short prefix;
90};
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070091
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070092struct perm_node {
93 struct perms_ dp;
94 struct listnode plist;
95};
Colin Cross44b65d02010-04-20 14:32:50 -070096static list_declare(dev_perms);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -070097
98/*
99 * Permission override when in emulator mode, must be parsed before
100 * system properties is initalized.
101 */
Colin Cross44b65d02010-04-20 14:32:50 -0700102int add_dev_perms(const char *name, mode_t perm, unsigned int uid,
103 unsigned int gid, unsigned short prefix) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700104 int size;
Colin Cross44b65d02010-04-20 14:32:50 -0700105 char *tmp = 0;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700106 struct perm_node *node = malloc(sizeof (struct perm_node));
107 if (!node)
108 return -ENOMEM;
109
110 size = strlen(name) + 1;
111 if ((node->dp.name = malloc(size)) == NULL)
112 return -ENOMEM;
113
114 memcpy(node->dp.name, name, size);
115 node->dp.perm = perm;
116 node->dp.uid = uid;
117 node->dp.gid = gid;
118 node->dp.prefix = prefix;
119
Colin Cross44b65d02010-04-20 14:32:50 -0700120 list_add_tail(&dev_perms, &node->plist);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700121 return 0;
122}
123
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700124static int get_device_perm_inner(struct perms_ *perms, const char *path,
125 unsigned *uid, unsigned *gid, mode_t *perm)
126{
127 int i;
128 for(i = 0; perms[i].name; i++) {
129
130 if(perms[i].prefix) {
131 if(strncmp(path, perms[i].name, strlen(perms[i].name)))
132 continue;
133 } else {
134 if(strcmp(path, perms[i].name))
135 continue;
136 }
137 *uid = perms[i].uid;
138 *gid = perms[i].gid;
139 *perm = perms[i].perm;
140 return 0;
141 }
142 return -1;
143}
144
145/* First checks for emulator specific permissions specified in /proc/cmdline. */
146static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
147{
148 mode_t perm;
Colin Cross44b65d02010-04-20 14:32:50 -0700149 struct listnode *node;
150 struct perm_node *perm_node;
151 struct perms_ *dp;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700152
Colin Cross44b65d02010-04-20 14:32:50 -0700153 /* search the perms list in reverse so that ueventd.$hardware can
154 * override ueventd.rc
155 */
156 list_for_each_reverse(node, &dev_perms) {
157 perm_node = node_to_item(node, struct perm_node, plist);
158 dp = &perm_node->dp;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700159
Colin Cross44b65d02010-04-20 14:32:50 -0700160 if (dp->prefix) {
161 if (strncmp(path, dp->name, strlen(dp->name)))
162 continue;
163 } else {
164 if (strcmp(path, dp->name))
165 continue;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700166 }
Colin Cross44b65d02010-04-20 14:32:50 -0700167 *uid = dp->uid;
168 *gid = dp->gid;
169 return dp->perm;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700170 }
Colin Cross44b65d02010-04-20 14:32:50 -0700171 /* Default if nothing found. */
172 *uid = 0;
173 *gid = 0;
174 return 0600;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700175}
176
177static void make_device(const char *path, int block, int major, int minor)
178{
179 unsigned uid;
180 unsigned gid;
181 mode_t mode;
182 dev_t dev;
183
184 if(major > 255 || minor > 255)
185 return;
186
187 mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
188 dev = (major << 8) | minor;
Nick Pelly6405c692010-01-21 18:13:39 -0800189 /* Temporarily change egid to avoid race condition setting the gid of the
190 * device node. Unforunately changing the euid would prevent creation of
191 * some device nodes, so the uid has to be set with chown() and is still
192 * racy. Fixing the gid race at least fixed the issue with system_server
193 * opening dynamic input devices under the AID_INPUT gid. */
194 setegid(gid);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700195 mknod(path, mode, dev);
Nick Pelly6405c692010-01-21 18:13:39 -0800196 chown(path, uid, -1);
197 setegid(AID_ROOT);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700198}
199
Chuck Tuffli1e070842008-12-15 14:26:56 -0800200#if LOG_UEVENTS
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700201
202static inline suseconds_t get_usecs(void)
203{
204 struct timeval tv;
205 gettimeofday(&tv, 0);
206 return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec;
207}
208
209#define log_event_print(x...) INFO(x)
210
211#else
212
213#define log_event_print(fmt, args...) do { } while (0)
214#define get_usecs() 0
215
216#endif
217
218static void parse_event(const char *msg, struct uevent *uevent)
219{
220 uevent->action = "";
221 uevent->path = "";
222 uevent->subsystem = "";
223 uevent->firmware = "";
224 uevent->major = -1;
225 uevent->minor = -1;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700226 uevent->partition_name = NULL;
227 uevent->partition_num = -1;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700228
229 /* currently ignoring SEQNUM */
230 while(*msg) {
231 if(!strncmp(msg, "ACTION=", 7)) {
232 msg += 7;
233 uevent->action = msg;
234 } else if(!strncmp(msg, "DEVPATH=", 8)) {
235 msg += 8;
236 uevent->path = msg;
237 } else if(!strncmp(msg, "SUBSYSTEM=", 10)) {
238 msg += 10;
239 uevent->subsystem = msg;
240 } else if(!strncmp(msg, "FIRMWARE=", 9)) {
241 msg += 9;
242 uevent->firmware = msg;
243 } else if(!strncmp(msg, "MAJOR=", 6)) {
244 msg += 6;
245 uevent->major = atoi(msg);
246 } else if(!strncmp(msg, "MINOR=", 6)) {
247 msg += 6;
248 uevent->minor = atoi(msg);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700249 } else if(!strncmp(msg, "PARTN=", 6)) {
250 msg += 6;
251 uevent->partition_num = atoi(msg);
252 } else if(!strncmp(msg, "PARTNAME=", 9)) {
253 msg += 9;
254 uevent->partition_name = msg;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700255 }
256
257 /* advance to after the next \0 */
258 while(*msg++)
259 ;
260 }
261
262 log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n",
263 uevent->action, uevent->path, uevent->subsystem,
264 uevent->firmware, uevent->major, uevent->minor);
265}
266
Benoit Gobyd2278632010-08-03 14:36:02 -0700267static char **get_character_device_symlinks(struct uevent *uevent)
268{
269 const char *parent;
270 char *slash;
271 char **links;
272 int link_num = 0;
273 int width;
274
275 if (strncmp(uevent->path, "/devices/platform/", 18))
276 return NULL;
277
278 links = malloc(sizeof(char *) * 2);
279 if (!links)
280 return NULL;
281 memset(links, 0, sizeof(char *) * 2);
282
283 /* skip "/devices/platform/<driver>" */
284 parent = strchr(uevent->path + 18, '/');
285 if (!*parent)
286 goto err;
287
288 if (!strncmp(parent, "/usb", 4)) {
289 /* skip root hub name and device. use device interface */
290 while (*++parent && *parent != '/');
291 if (*parent)
292 while (*++parent && *parent != '/');
293 if (!*parent)
294 goto err;
295 slash = strchr(++parent, '/');
296 if (!slash)
297 goto err;
298 width = slash - parent;
299 if (width <= 0)
300 goto err;
301
302 if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0)
303 link_num++;
304 else
305 links[link_num] = NULL;
306 mkdir("/dev/usb", 0755);
307 }
308 else {
309 goto err;
310 }
311
312 return links;
313err:
314 free(links);
315 return NULL;
316}
317
Colin Crossb0ab94b2010-04-08 16:16:20 -0700318static char **parse_platform_block_device(struct uevent *uevent)
319{
320 const char *driver;
321 const char *path;
322 char *slash;
323 int width;
324 char buf[256];
325 char link_path[256];
326 int fd;
327 int link_num = 0;
328 int ret;
329 char *p;
330 unsigned int size;
331 struct stat info;
332
333 char **links = malloc(sizeof(char *) * 4);
334 if (!links)
335 return NULL;
336 memset(links, 0, sizeof(char *) * 4);
337
338 /* Drop "/devices/platform/" */
339 path = uevent->path;
340 driver = path + 18;
341 slash = strchr(driver, '/');
342 if (!slash)
343 goto err;
344 width = slash - driver;
345 if (width <= 0)
346 goto err;
347
348 snprintf(link_path, sizeof(link_path), "/dev/block/platform/%.*s",
349 width, driver);
350
351 if (uevent->partition_name) {
352 p = strdup(uevent->partition_name);
353 sanitize(p);
354 if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0)
355 link_num++;
356 else
357 links[link_num] = NULL;
358 free(p);
359 }
360
361 if (uevent->partition_num >= 0) {
362 if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0)
363 link_num++;
364 else
365 links[link_num] = NULL;
366 }
367
368 slash = strrchr(path, '/');
369 if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0)
370 link_num++;
371 else
372 links[link_num] = NULL;
373
374 return links;
375
376err:
377 free(links);
378 return NULL;
379}
380
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700381static void handle_device_event(struct uevent *uevent)
382{
383 char devpath[96];
Mike Lockwood93ac1552010-05-06 13:30:09 -0400384 int devpath_ready = 0;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700385 char *base, *name;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700386 char **links = NULL;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700387 int block;
Colin Crossb0ab94b2010-04-08 16:16:20 -0700388 int i;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700389
390 /* if it's not a /dev device, nothing to do */
391 if((uevent->major < 0) || (uevent->minor < 0))
392 return;
393
394 /* do we have a name? */
395 name = strrchr(uevent->path, '/');
396 if(!name)
397 return;
398 name++;
399
400 /* too-long names would overrun our buffer */
401 if(strlen(name) > 64)
402 return;
403
404 /* are we block or char? where should we live? */
The Android Open Source Project35237d12008-12-17 18:08:08 -0800405 if(!strncmp(uevent->subsystem, "block", 5)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700406 block = 1;
407 base = "/dev/block/";
408 mkdir(base, 0755);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700409 if (!strncmp(uevent->path, "/devices/platform/", 18))
410 links = parse_platform_block_device(uevent);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700411 } else {
412 block = 0;
413 /* this should probably be configurable somehow */
Mike Lockwood93ac1552010-05-06 13:30:09 -0400414 if (!strncmp(uevent->subsystem, "usb", 3)) {
415 if (!strcmp(uevent->subsystem, "usb")) {
416 /* This imitates the file system that would be created
417 * if we were using devfs instead.
418 * Minors are broken up into groups of 128, starting at "001"
419 */
420 int bus_id = uevent->minor / 128 + 1;
421 int device_id = uevent->minor % 128 + 1;
422 /* build directories */
423 mkdir("/dev/bus", 0755);
424 mkdir("/dev/bus/usb", 0755);
425 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id);
426 mkdir(devpath, 0755);
427 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id);
428 devpath_ready = 1;
429 } else {
430 /* ignore other USB events */
431 return;
432 }
433 } else if (!strncmp(uevent->subsystem, "graphics", 8)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700434 base = "/dev/graphics/";
435 mkdir(base, 0755);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800436 } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700437 base = "/dev/oncrpc/";
438 mkdir(base, 0755);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800439 } else if (!strncmp(uevent->subsystem, "adsp", 4)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700440 base = "/dev/adsp/";
441 mkdir(base, 0755);
Iliyan Malchevfc0182e2009-05-01 10:25:05 -0700442 } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) {
443 base = "/dev/msm_camera/";
444 mkdir(base, 0755);
445 } else if(!strncmp(uevent->subsystem, "input", 5)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700446 base = "/dev/input/";
447 mkdir(base, 0755);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800448 } else if(!strncmp(uevent->subsystem, "mtd", 3)) {
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700449 base = "/dev/mtd/";
450 mkdir(base, 0755);
Xiaopeng Yang5bb44c82008-11-25 16:20:07 -0800451 } else if(!strncmp(uevent->subsystem, "sound", 5)) {
452 base = "/dev/snd/";
453 mkdir(base, 0755);
The Android Open Source Project35237d12008-12-17 18:08:08 -0800454 } else if(!strncmp(uevent->subsystem, "misc", 4) &&
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700455 !strncmp(name, "log_", 4)) {
456 base = "/dev/log/";
457 mkdir(base, 0755);
458 name += 4;
459 } else
460 base = "/dev/";
Benoit Gobyd2278632010-08-03 14:36:02 -0700461 links = get_character_device_symlinks(uevent);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700462 }
463
Mike Lockwood93ac1552010-05-06 13:30:09 -0400464 if (!devpath_ready)
465 snprintf(devpath, sizeof(devpath), "%s%s", base, name);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700466
467 if(!strcmp(uevent->action, "add")) {
468 make_device(devpath, block, uevent->major, uevent->minor);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700469 if (links) {
470 for (i = 0; links[i]; i++)
471 make_link(devpath, links[i]);
472 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700473 }
474
475 if(!strcmp(uevent->action, "remove")) {
Colin Crossb0ab94b2010-04-08 16:16:20 -0700476 if (links) {
477 for (i = 0; links[i]; i++)
478 remove_link(devpath, links[i]);
479 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700480 unlink(devpath);
Colin Crossb0ab94b2010-04-08 16:16:20 -0700481 }
482
483 if (links) {
484 for (i = 0; links[i]; i++)
485 free(links[i]);
486 free(links);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700487 }
488}
489
490static int load_firmware(int fw_fd, int loading_fd, int data_fd)
491{
492 struct stat st;
493 long len_to_copy;
494 int ret = 0;
495
496 if(fstat(fw_fd, &st) < 0)
497 return -1;
498 len_to_copy = st.st_size;
499
500 write(loading_fd, "1", 1); /* start transfer */
501
502 while (len_to_copy > 0) {
503 char buf[PAGE_SIZE];
504 ssize_t nr;
505
506 nr = read(fw_fd, buf, sizeof(buf));
507 if(!nr)
508 break;
509 if(nr < 0) {
510 ret = -1;
511 break;
512 }
513
514 len_to_copy -= nr;
515 while (nr > 0) {
516 ssize_t nw = 0;
517
518 nw = write(data_fd, buf + nw, nr);
519 if(nw <= 0) {
520 ret = -1;
521 goto out;
522 }
523 nr -= nw;
524 }
525 }
526
527out:
528 if(!ret)
529 write(loading_fd, "0", 1); /* successful end of transfer */
530 else
531 write(loading_fd, "-1", 2); /* abort transfer */
532
533 return ret;
534}
535
536static void process_firmware_event(struct uevent *uevent)
537{
538 char *root, *loading, *data, *file;
539 int l, loading_fd, data_fd, fw_fd;
540
541 log_event_print("firmware event { '%s', '%s' }\n",
542 uevent->path, uevent->firmware);
543
544 l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
545 if (l == -1)
546 return;
547
548 l = asprintf(&loading, "%sloading", root);
549 if (l == -1)
550 goto root_free_out;
551
552 l = asprintf(&data, "%sdata", root);
553 if (l == -1)
554 goto loading_free_out;
555
556 l = asprintf(&file, FIRMWARE_DIR"/%s", uevent->firmware);
557 if (l == -1)
558 goto data_free_out;
559
560 loading_fd = open(loading, O_WRONLY);
561 if(loading_fd < 0)
562 goto file_free_out;
563
564 data_fd = open(data, O_WRONLY);
565 if(data_fd < 0)
566 goto loading_close_out;
567
568 fw_fd = open(file, O_RDONLY);
569 if(fw_fd < 0)
570 goto data_close_out;
571
572 if(!load_firmware(fw_fd, loading_fd, data_fd))
573 log_event_print("firmware copy success { '%s', '%s' }\n", root, file);
574 else
575 log_event_print("firmware copy failure { '%s', '%s' }\n", root, file);
576
577 close(fw_fd);
578data_close_out:
579 close(data_fd);
580loading_close_out:
581 close(loading_fd);
582file_free_out:
583 free(file);
584data_free_out:
585 free(data);
586loading_free_out:
587 free(loading);
588root_free_out:
589 free(root);
590}
591
592static void handle_firmware_event(struct uevent *uevent)
593{
594 pid_t pid;
Colin Cross982a8152010-06-03 12:21:01 -0700595 int status;
596 int ret;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700597
598 if(strcmp(uevent->subsystem, "firmware"))
599 return;
600
601 if(strcmp(uevent->action, "add"))
602 return;
603
604 /* we fork, to avoid making large memory allocations in init proper */
605 pid = fork();
606 if (!pid) {
607 process_firmware_event(uevent);
608 exit(EXIT_SUCCESS);
Colin Cross982a8152010-06-03 12:21:01 -0700609 } else {
610 do {
611 ret = waitpid(pid, &status, 0);
612 } while (ret == -1 && errno == EINTR);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700613 }
614}
615
616#define UEVENT_MSG_LEN 1024
Colin Cross0dd7ca62010-04-13 19:25:51 -0700617void handle_device_fd()
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700618{
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700619 for(;;) {
620 char msg[UEVENT_MSG_LEN+2];
621 char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
622 struct iovec iov = {msg, sizeof(msg)};
623 struct sockaddr_nl snl;
624 struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0};
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700625
Nick Kralevichfad72042010-07-19 15:53:05 -0700626 ssize_t n = recvmsg(device_fd, &hdr, 0);
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700627 if (n <= 0) {
628 break;
629 }
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700630
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700631 if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) {
632 /* ignoring non-kernel netlink multicast message */
633 continue;
634 }
635
636 struct cmsghdr * cmsg = CMSG_FIRSTHDR(&hdr);
637 if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
638 /* no sender credentials received, ignore message */
639 continue;
640 }
641
642 struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg);
643 if (cred->uid != 0) {
644 /* message from non-root user, ignore */
645 continue;
646 }
647
648 if(n >= UEVENT_MSG_LEN) /* overflow -- discard */
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700649 continue;
650
651 msg[n] = '\0';
652 msg[n+1] = '\0';
653
Nick Kralevich5f5d5c82010-07-19 14:31:20 -0700654 struct uevent uevent;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700655 parse_event(msg, &uevent);
656
657 handle_device_event(&uevent);
658 handle_firmware_event(&uevent);
659 }
660}
661
662/* Coldboot walks parts of the /sys tree and pokes the uevent files
663** to cause the kernel to regenerate device add events that happened
664** before init's device manager was started
665**
666** We drain any pending events from the netlink socket every time
667** we poke another uevent file to make sure we don't overrun the
668** socket's buffer.
669*/
670
Colin Cross0dd7ca62010-04-13 19:25:51 -0700671static void do_coldboot(DIR *d)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700672{
673 struct dirent *de;
674 int dfd, fd;
675
676 dfd = dirfd(d);
677
678 fd = openat(dfd, "uevent", O_WRONLY);
679 if(fd >= 0) {
680 write(fd, "add\n", 4);
681 close(fd);
Colin Cross0dd7ca62010-04-13 19:25:51 -0700682 handle_device_fd();
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700683 }
684
685 while((de = readdir(d))) {
686 DIR *d2;
687
688 if(de->d_type != DT_DIR || de->d_name[0] == '.')
689 continue;
690
691 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
692 if(fd < 0)
693 continue;
694
695 d2 = fdopendir(fd);
696 if(d2 == 0)
697 close(fd);
698 else {
Colin Cross0dd7ca62010-04-13 19:25:51 -0700699 do_coldboot(d2);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700700 closedir(d2);
701 }
702 }
703}
704
Colin Cross0dd7ca62010-04-13 19:25:51 -0700705static void coldboot(const char *path)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700706{
707 DIR *d = opendir(path);
708 if(d) {
Colin Cross0dd7ca62010-04-13 19:25:51 -0700709 do_coldboot(d);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700710 closedir(d);
711 }
712}
713
Colin Cross0dd7ca62010-04-13 19:25:51 -0700714void device_init(void)
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700715{
716 suseconds_t t0, t1;
Colin Crossf83d0b92010-04-21 12:04:20 -0700717 struct stat info;
718 int fd;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700719
Colin Cross0dd7ca62010-04-13 19:25:51 -0700720 device_fd = open_uevent_socket();
721 if(device_fd < 0)
722 return;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700723
Colin Cross0dd7ca62010-04-13 19:25:51 -0700724 fcntl(device_fd, F_SETFD, FD_CLOEXEC);
725 fcntl(device_fd, F_SETFL, O_NONBLOCK);
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700726
Colin Crossf83d0b92010-04-21 12:04:20 -0700727 if (stat(coldboot_done, &info) < 0) {
728 t0 = get_usecs();
729 coldboot("/sys/class");
730 coldboot("/sys/block");
731 coldboot("/sys/devices");
732 t1 = get_usecs();
733 fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
734 close(fd);
735 log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
736 } else {
737 log_event_print("skipping coldboot, already done\n");
738 }
Colin Cross0dd7ca62010-04-13 19:25:51 -0700739}
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700740
Colin Cross0dd7ca62010-04-13 19:25:51 -0700741int get_device_fd()
742{
743 return device_fd;
The Android Open Source Project4f6e8d72008-10-21 07:00:00 -0700744}