blob: 6b724d03f84b907b0078fb205445d81ded649e53 [file] [log] [blame]
The Android Open Source Project8ac3a132009-01-20 14:04:01 -08001
2/*
3 * Copyright (C) 2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <string.h>
19#include <stdlib.h>
20#include <errno.h>
21
22#include <sys/types.h>
23#include <sys/socket.h>
24
25#include "vold.h"
26#include "uevent.h"
27#include "mmc.h"
28#include "blkdev.h"
29#include "volmgr.h"
30#include "media.h"
31
32#define DEBUG_UEVENT 0
33
34#define UEVENT_PARAMS_MAX 32
35
36enum uevent_action { action_add, action_remove, action_change };
37
38struct uevent {
39 char *path;
40 enum uevent_action action;
41 char *subsystem;
42 char *param[UEVENT_PARAMS_MAX];
43 unsigned int seqnum;
44};
45
46struct uevent_dispatch {
47 char *subsystem;
48 int (* dispatch) (struct uevent *);
49};
50
51static void dump_uevent(struct uevent *);
52static int dispatch_uevent(struct uevent *event);
53static void free_uevent(struct uevent *event);
54static char *get_uevent_param(struct uevent *event, char *param_name);
55
56static int handle_powersupply_event(struct uevent *event);
57static int handle_switch_event(struct uevent *);
58static int handle_battery_event(struct uevent *);
59static int handle_mmc_event(struct uevent *);
60static int handle_block_event(struct uevent *);
61static int handle_bdi_event(struct uevent *);
62static void _cb_blkdev_ok_to_destroy(blkdev_t *dev);
63
64static struct uevent_dispatch dispatch_table[] = {
65 { "switch", handle_switch_event },
66 { "battery", handle_battery_event },
67 { "mmc", handle_mmc_event },
68 { "block", handle_block_event },
69 { "bdi", handle_bdi_event },
70 { "power_supply", handle_powersupply_event },
71 { NULL, NULL }
72};
73
74int process_uevent_message(int socket)
75{
76 char buffer[64 * 1024]; // Thank god we're not in the kernel :)
77 int count;
78 char *s = buffer;
79 char *end;
80 struct uevent *event;
81 int param_idx = 0;
82 int i;
83 int first = 1;
84 int rc = 0;
85
86 if ((count = recv(socket, buffer, sizeof(buffer), 0)) < 0) {
87 LOGE("Error receiving uevent (%s)\n", strerror(errno));
88 return -errno;
89 }
90
91 if (!(event = malloc(sizeof(struct uevent)))) {
92 LOGE("Error allocating memory (%s)\n", strerror(errno));
93 return -errno;
94 }
95
96 memset(event, 0, sizeof(struct uevent));
97
98 end = s + count;
99 while (s < end) {
100 if (first) {
101 char *p;
102 for (p = s; *p != '@'; p++);
103 p++;
104 event->path = strdup(p);
105 first = 0;
106 } else {
107 if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
108 char *a = s + strlen("ACTION=");
109
110 if (!strcmp(a, "add"))
111 event->action = action_add;
112 else if (!strcmp(a, "change"))
113 event->action = action_change;
114 else if (!strcmp(a, "remove"))
115 event->action = action_remove;
116 } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM=")))
117 event->seqnum = atoi(s + strlen("SEQNUM="));
118 else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM=")))
119 event->subsystem = strdup(s + strlen("SUBSYSTEM="));
120 else
121 event->param[param_idx++] = strdup(s);
122 }
123 s+= (strlen(s) + 1);
124 }
125
126 rc = dispatch_uevent(event);
127
128 free_uevent(event);
129 return rc;
130}
131
132int simulate_uevent(char *subsys, char *path, char *action, char **params)
133{
134 struct uevent *event;
135 char tmp[255];
136 int i, rc;
137
138 if (!(event = malloc(sizeof(struct uevent)))) {
139 LOGE("Error allocating memory (%s)\n", strerror(errno));
140 return -errno;
141 }
142
143 memset(event, 0, sizeof(struct uevent));
144
145 event->subsystem = strdup(subsys);
146
147 if (!strcmp(action, "add"))
148 event->action = action_add;
149 else if (!strcmp(action, "change"))
150 event->action = action_change;
151 else if (!strcmp(action, "remove"))
152 event->action = action_remove;
153 else {
154 LOGE("Invalid action '%s'\n", action);
155 return -1;
156 }
157
158 event->path = strdup(path);
159
160 for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
161 if (!params[i])
162 break;
163 event->param[i] = strdup(params[i]);
164 }
165
166 rc = dispatch_uevent(event);
167 free_uevent(event);
168 return rc;
169}
170
171static int dispatch_uevent(struct uevent *event)
172{
173 int i;
174
175 for (i = 0; dispatch_table[i].subsystem != NULL; i++) {
176 if (!strcmp(dispatch_table[i].subsystem, event->subsystem))
177 return dispatch_table[i].dispatch(event);
178 }
179
180#if DEBUG_UEVENT
181 LOG_VOL("No uevent handlers registered for '%s' subsystem\n", event->subsystem);
182#endif
183 return 0;
184}
185
186static void dump_uevent(struct uevent *event)
187{
188 int i;
189
190 LOG_VOL("[UEVENT] Sq: %u S: %s A: %d P: %s\n",
191 event->seqnum, event->subsystem, event->action, event->path);
192 for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
193 if (!event->param[i])
194 break;
195 LOG_VOL("%s\n", event->param[i]);
196 }
197}
198
199static void free_uevent(struct uevent *event)
200{
201 int i;
202 free(event->path);
203 free(event->subsystem);
204 for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
205 if (!event->param[i])
206 break;
207 free(event->param[i]);
208 }
209 free(event);
210}
211
212static char *get_uevent_param(struct uevent *event, char *param_name)
213{
214 int i;
215
216 for (i = 0; i < UEVENT_PARAMS_MAX; i++) {
217 if (!event->param[i])
218 break;
219 if (!strncmp(event->param[i], param_name, strlen(param_name)))
220 return &event->param[i][strlen(param_name) + 1];
221 }
222
223 LOGE("get_uevent_param(): No parameter '%s' found\n", param_name);
224 return NULL;
225}
226
227/*
228 * ---------------
229 * Uevent Handlers
230 * ---------------
231 */
232
233static int handle_powersupply_event(struct uevent *event)
234{
235 dump_uevent(event);
236 return 0;
237}
238
239static int handle_switch_event(struct uevent *event)
240{
241 char *name = get_uevent_param(event, "SWITCH_NAME");
242 char *state = get_uevent_param(event, "SWITCH_STATE");
243
244 if (!strcmp(name, "usb_mass_storage")) {
245 if (!strcmp(state, "online")) {
246 ums_hostconnected_set(true);
247 } else {
248 ums_hostconnected_set(false);
249 }
250 } else
251 LOG_VOL("handle_switch_event(): Ignoring switch '%s'\n", name);
252
253 return 0;
254}
255
256static int handle_battery_event(struct uevent *event)
257{
258 dump_uevent(event);
259 return 0;
260}
261
262static int handle_block_event(struct uevent *event)
263{
264 char mediapath[255];
265 media_t *media;
266 int n;
267 int maj, min;
268 blkdev_t *blkdev;
269
270 /*
271 * Look for backing media for this block device
272 */
273 if (!strcmp(get_uevent_param(event, "DEVTYPE"), "disk"))
274 n = 2;
275 else if (!strcmp(get_uevent_param(event, "DEVTYPE"), "partition"))
276 n = 3;
277 else {
278 LOGE("Bad blockdev type '%s'\n", get_uevent_param(event, "DEVTYPE"));
279 return -EINVAL;
280 }
281
282 truncate_sysfs_path(event->path, n, mediapath);
283
284 if (!(media = media_lookup_by_path(mediapath, false))) {
285#if DEBUG_UEVENT
286 LOG_VOL("No backend media found @ device path '%s'\n", mediapath);
287#endif
288 return 0;
289 }
290
291 maj = atoi(get_uevent_param(event, "MAJOR"));
292 min = atoi(get_uevent_param(event, "MINOR"));
293
294 if (event->action == action_add) {
295 blkdev_t *disk;
296 boolean pending = false;
297
298 /*
299 * If there isn't a disk already its because *we*
300 * are the disk
301 */
302 disk = blkdev_lookup_by_devno(maj, 0);
303
304 /*
305 * It is possible that there is already a blkdev
306 * for this device (created by blkdev_create_pending_partition())
307 */
308
309 if ((blkdev = blkdev_lookup_by_devno(maj, min))) {
310 blkdev_devpath_set(blkdev, event->path);
311 pending = true;
312 } else {
313 if (!(blkdev = blkdev_create(disk,
314 event->path,
315 maj,
316 min,
317 media,
318 get_uevent_param(event, "DEVTYPE")))) {
319 LOGE("Unable to allocate new blkdev (%m)\n");
320 return -1;
321 }
322 }
323
324 /*
325 * Add the blkdev to media
326 */
327 int rc;
328 if ((rc = media_add_blkdev(media, blkdev)) < 0) {
329 LOGE("Unable to add blkdev to card (%d)\n", rc);
330 return rc;
331 }
332
333 LOG_VOL("New blkdev %d.%d on media %s, media path %s\n", blkdev->major, blkdev->minor, media->name, mediapath);
334
335 if (pending) {
336 /*
337 * This blkdev already has its dev_fspath set so
338 * if all partitions are read, pass it off to
339 * the volume manager
340 */
341 LOG_VOL("Pending disk '%d.%d' has %d pending partitions\n",
342 blkdev->disk->major, blkdev->disk->minor,
343 blkdev_get_num_pending_partitions(blkdev->disk));
344
345 if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) {
346 if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) {
347 LOGE("Volmgr failed to handle pending device (%d)\n", rc);
348 return rc;
349 }
350 }
351 }
352 } else if (event->action == action_remove) {
353 int rc;
354
355 if (!(blkdev = blkdev_lookup_by_devno(maj, min))) {
356#if DEBUG_UEVENT
357 LOG_VOL("We aren't handling blkdev @ %s\n", event->path);
358#endif
359 return 0;
360 }
361
362 LOG_VOL("Destroying blkdev %d.%d @ %s on media %s\n", blkdev->major, blkdev->minor, blkdev->devpath, media->name);
363 if ((rc = volmgr_notify_eject(blkdev, _cb_blkdev_ok_to_destroy)) < 0)
364 LOGE("Error notifying volmgr of eject\n");
365 } else {
366#if DEBUG_UEVENT
367 LOG_VOL("No handler implemented for action %d\n", event->action);
368#endif
369 }
370 return 0;
371}
372
373static void _cb_blkdev_ok_to_destroy(blkdev_t *dev)
374{
375 media_t *media = media_lookup_by_dev(dev);
376 if (media)
377 media_remove_blkdev(media, dev);
378 blkdev_destroy(dev);
379}
380
381static int handle_bdi_event(struct uevent *event)
382{
383 return 0;
384}
385
386static int handle_mmc_event(struct uevent *event)
387{
388 if (event->action == action_add) {
389 media_t *media;
390 char serial[80];
391 char *type;
392
393 /*
394 * Pull card information from sysfs
395 */
396 type = get_uevent_param(event, "MMC_TYPE");
397 if (strcmp(type, "SD") && strcmp(type, "MMC"))
398 return 0;
399
400 read_sysfs_var(serial, sizeof(serial), event->path, "serial");
401 if (!(media = media_create(event->path,
402 get_uevent_param(event, "MMC_NAME"),
403 serial,
404 media_mmc))) {
405 LOGE("Unable to allocate new media (%m)\n");
406 return -1;
407 }
408 LOG_VOL("New MMC card '%s' (serial %u) added @ %s\n", media->name,
409 media->serial, media->devpath);
410 } else if (event->action == action_remove) {
411 media_t *media;
412
413 if (!(media = media_lookup_by_path(event->path, false))) {
414 LOGE("Unable to lookup media '%s'\n", event->path);
415 return -1;
416 }
417
418 LOG_VOL("MMC card '%s' (serial %u) @ %s removed\n", media->name,
419 media->serial, media->devpath);
420 /*
421 * If this media is still mounted, then we have an unsafe removal
422 */
423 media_destroy(media);
424 } else {
425#if DEBUG_UEVENT
426 LOG_VOL("No handler implemented for action %d\n", event->action);
427#endif
428 }
429
430 return 0;
431}