4b56de48177c0561f6f78b223e52599c7879cb5f
[petitboot] / discover / user-event.c
1 /*
2  *  Copyright (C) 2009 Sony Computer Entertainment Inc.
3  *  Copyright 2009 Sony Corp.
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; version 2 of the License.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 #if defined(HAVE_CONFIG_H)
20 #include "config.h"
21 #endif
22
23 #define _GNU_SOURCE
24 #include <assert.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <sys/socket.h>
28 #include <sys/types.h>
29 #include <sys/un.h>
30
31 #include <log/log.h>
32 #include <url/url.h>
33 #include <types/types.h>
34 #include <talloc/talloc.h>
35 #include <waiter/waiter.h>
36
37 #include "device-handler.h"
38 #include "resource.h"
39 #include "event.h"
40 #include "user-event.h"
41
42
43 struct user_event {
44         struct device_handler *handler;
45         int socket;
46 };
47
48 static const char *event_action_name(enum event_action action)
49 {
50         switch (action) {
51         case EVENT_ACTION_ADD:
52                 return "add";
53         case EVENT_ACTION_REMOVE:
54                 return "remove";
55         case EVENT_ACTION_CONF:
56                 return "conf";
57         default:
58                 break;
59         }
60
61         return "unknown";
62 }
63
64 static void user_event_print_event(struct event __attribute__((unused)) *event)
65 {
66         int i;
67
68         pb_debug("user_event %s event:\n", event_action_name(event->action));
69         pb_debug("\tdevice: %s\n", event->device);
70
71         for (i = 0; i < event->n_params; i++)
72                 pb_debug("\t%-12s => %s\n",
73                         event->params[i].name, event->params[i].value);
74 }
75
76 static enum conf_method parse_conf_method(const char *str)
77 {
78
79         if (!strcasecmp(str, "dhcp")) {
80                 return CONF_METHOD_DHCP;
81         }
82         return CONF_METHOD_UNKNOWN;
83 }
84
85 static struct resource *user_event_resource(struct discover_boot_option *opt,
86                 struct event *event, const char *param_name)
87 {
88         struct resource *res;
89         struct pb_url *url;
90         const char *val;
91
92         val = event_get_param(event, param_name);
93         if (!val)
94                 return NULL;
95
96         url = pb_url_parse(opt, val);
97         if (!url)
98                 return NULL;
99
100         res = create_url_resource(opt, url);
101         if (!res) {
102                 talloc_free(url);
103                 return NULL;
104         }
105
106         return res;
107 }
108
109 static int parse_user_event(struct discover_context *ctx, struct event *event)
110 {
111         struct discover_boot_option *d_opt;
112         struct boot_option *opt;
113         struct device *dev;
114         const char *p;
115
116         dev = ctx->device->device;
117
118         d_opt = discover_boot_option_create(ctx, ctx->device);
119         opt = d_opt->option;
120
121         if (!d_opt)
122                 goto fail;
123
124         p = event_get_param(event, "name");
125
126         if (!p) {
127                 pb_log("%s: no name found\n", __func__);
128                 goto fail;
129         }
130
131         opt->id = talloc_asprintf(opt, "%s#%s", dev->id, p);
132         opt->name = talloc_strdup(opt, p);
133
134         d_opt->boot_image = user_event_resource(d_opt, event, "image");
135         if (!d_opt->boot_image) {
136                 pb_log("%s: no boot image found for %s!\n", __func__,
137                                 opt->name);
138                 goto fail;
139         }
140
141         d_opt->initrd = user_event_resource(d_opt, event, "initrd");
142
143         p = event_get_param(event, "args");
144
145         if (p)
146                 opt->boot_args = talloc_strdup(opt, p);
147
148         opt->description = talloc_asprintf(opt, "%s %s", opt->boot_image_file,
149                 opt->boot_args ? : "");
150
151         if (event_get_param(event, "default"))
152                 opt->is_default = true;
153
154         discover_context_add_boot_option(ctx, d_opt);
155
156         return 0;
157
158 fail:
159         talloc_free(d_opt);
160         return -1;
161 }
162
163 static int user_event_conf(struct user_event *uev, struct event *event)
164 {
165         struct device_handler *handler = uev->handler;
166         struct discover_device *dev;
167         enum conf_method method;
168         struct pb_url *url;
169         const char *val;
170
171         val = event_get_param(event, "url");
172         if (!val)
173                 return 0;
174
175         url = pb_url_parse(event, val);
176         if (!url)
177                 return 0;
178
179         val = event_get_param(event, "method");
180         if (!val)
181                 return 0;
182
183         method = parse_conf_method(val);
184         if (method == CONF_METHOD_UNKNOWN)
185                 return 0;
186
187         dev = discover_device_create(handler, event->device);
188
189         device_handler_conf(handler, dev, url, method);
190
191         return 0;
192 }
193
194 static int user_event_add(struct user_event *uev, struct event *event)
195 {
196         struct device_handler *handler = uev->handler;
197         struct discover_context *ctx;
198         struct discover_device *dev;
199
200         dev = discover_device_create(handler, event->device);
201         ctx = device_handler_discover_context_create(handler, dev);
202
203         parse_user_event(ctx, event);
204
205         device_handler_discover_context_commit(handler, ctx);
206
207         talloc_free(ctx);
208
209         return 0;
210 }
211
212 static int user_event_remove(struct user_event *uev, struct event *event)
213 {
214         struct device_handler *handler = uev->handler;
215         struct discover_device *dev;
216
217         dev = device_lookup_by_id(handler, event->device);
218         if (!dev)
219                 return 0;
220
221         device_handler_remove(handler, dev);
222
223         return 0;
224 }
225
226 static void user_event_handle_message(struct user_event *uev, char *buf,
227         int len)
228 {
229         int result;
230         struct event *event;
231
232         event = talloc(uev, struct event);
233         event->type = EVENT_TYPE_USER;
234
235         result = event_parse_ad_message(event, buf, len);
236
237         if (result)
238                 return;
239
240         user_event_print_event(event);
241
242         switch (event->action) {
243         case EVENT_ACTION_ADD:
244                 result = user_event_add(uev, event);
245                 break;
246         case EVENT_ACTION_REMOVE:
247                 result = user_event_remove(uev, event);
248                 break;
249         case EVENT_ACTION_CONF:
250                 result = user_event_conf(uev, event);
251                 break;
252         default:
253                 break;
254         }
255
256         talloc_free(event);
257
258         return;
259 }
260
261 static int user_event_process(void *arg)
262 {
263         struct user_event *uev = arg;
264         char buf[PBOOT_USER_EVENT_SIZE];
265         int len;
266
267         len = recvfrom(uev->socket, buf, sizeof(buf), 0, NULL, NULL);
268
269         if (len < 0) {
270                 pb_log("%s: socket read failed: %s", __func__, strerror(errno));
271                 return 0;
272         }
273
274         if (len == 0) {
275                 pb_log("%s: empty", __func__);
276                 return 0;
277         }
278
279         pb_debug("%s: %u bytes\n", __func__, len);
280
281         user_event_handle_message(uev, buf, len);
282
283         return 0;
284 }
285
286 static int user_event_destructor(void *arg)
287 {
288         struct user_event *uev = arg;
289
290         pb_debug("%s\n", __func__);
291
292         if (uev->socket >= 0)
293                 close(uev->socket);
294
295         return 0;
296 }
297
298 struct user_event *user_event_init(struct waitset *waitset,
299                 struct device_handler *handler)
300 {
301         struct sockaddr_un addr;
302         struct user_event *uev;
303
304         unlink(PBOOT_USER_EVENT_SOCKET);
305
306         uev = talloc(NULL, struct user_event);
307
308         uev->handler = handler;
309
310         uev->socket = socket(AF_UNIX, SOCK_DGRAM, 0);
311         if (uev->socket < 0) {
312                 pb_log("%s: Error creating event handler socket: %s\n",
313                         __func__, strerror(errno));
314                 goto out_err;
315         }
316
317         talloc_set_destructor(uev, user_event_destructor);
318
319         memset(&addr, 0, sizeof(addr));
320         addr.sun_family = AF_UNIX;
321         strcpy(addr.sun_path, PBOOT_USER_EVENT_SOCKET);
322
323         if (bind(uev->socket, (struct sockaddr *)&addr, sizeof(addr))) {
324                 pb_log("Error binding event handler socket: %s\n",
325                         strerror(errno));
326         }
327
328         waiter_register_io(waitset, uev->socket, WAIT_IN,
329                         user_event_process, uev);
330
331         pb_debug("%s: waiting on %s\n", __func__, PBOOT_USER_EVENT_SOCKET);
332
333         return uev;
334
335 out_err:
336         talloc_free(uev);
337         return NULL;
338 }
339
340 void user_event_destroy(struct user_event *uev)
341 {
342         talloc_free(uev);
343 }