discover: Use pxeconffile for udhcpc option name
[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 #define MAC_ADDR_SIZE   6
44 #define IP_ADDR_SIZE    4
45
46 struct user_event {
47         struct device_handler *handler;
48         int socket;
49 };
50
51 static const char *event_action_name(enum event_action action)
52 {
53         switch (action) {
54         case EVENT_ACTION_ADD:
55                 return "add";
56         case EVENT_ACTION_REMOVE:
57                 return "remove";
58         case EVENT_ACTION_DHCP:
59                 return "dhcp";
60         default:
61                 break;
62         }
63
64         return "unknown";
65 }
66
67 static void user_event_print_event(struct event __attribute__((unused)) *event)
68 {
69         int i;
70
71         pb_debug("user_event %s event:\n", event_action_name(event->action));
72         pb_debug("\tdevice: %s\n", event->device);
73
74         for (i = 0; i < event->n_params; i++)
75                 pb_debug("\t%-12s => %s\n",
76                         event->params[i].name, event->params[i].value);
77 }
78
79 static struct resource *user_event_resource(struct discover_boot_option *opt,
80                 struct event *event)
81 {
82         const char *siaddr, *boot_file;
83         struct resource *res;
84         struct pb_url *url;
85         char *url_str;
86
87         siaddr = event_get_param(event, "siaddr");
88         if (!siaddr) {
89                 pb_log("%s: next server option not found\n", __func__);
90                 return NULL;
91         }
92
93         boot_file = event_get_param(event, "boot_file");
94         if (!boot_file) {
95                 pb_log("%s: boot_file not found\n", __func__);
96                 return NULL;
97         }
98
99         url_str = talloc_asprintf(opt, "%s%s/%s", "tftp://", siaddr, boot_file);
100         url = pb_url_parse(opt, url_str);
101         talloc_free(url_str);
102
103         if (!url)
104                 return NULL;
105
106         res = create_url_resource(opt, url);
107         if (!res) {
108                 talloc_free(url);
109                 return NULL;
110         }
111
112         return res;
113 }
114
115 static int parse_user_event(struct discover_context *ctx, struct event *event)
116 {
117         struct discover_boot_option *d_opt;
118         char *server_ip, *root_dir, *p;
119         struct boot_option *opt;
120         struct device *dev;
121         const char *val;
122
123         dev = ctx->device->device;
124
125         d_opt = discover_boot_option_create(ctx, ctx->device);
126         if (!d_opt)
127                 goto fail;
128
129         opt = d_opt->option;
130
131         val = event_get_param(event, "name");
132
133         if (!val) {
134                 pb_log("%s: no name found\n", __func__);
135                 goto fail_opt;
136         }
137
138         opt->id = talloc_asprintf(opt, "%s#%s", dev->id, val);
139         opt->name = talloc_strdup(opt, val);
140
141         d_opt->boot_image = user_event_resource(d_opt, event);
142         if (!d_opt->boot_image) {
143                 pb_log("%s: no boot image found for %s!\n", __func__,
144                                 opt->name);
145                 goto fail_opt;
146         }
147
148         val = event_get_param(event, "rootpath");
149         if (val) {
150                 server_ip = talloc_strdup(opt, val);
151                 p = strchr(server_ip, ':');
152                 if (p) {
153                         root_dir = talloc_strdup(opt, p + 1);
154                         *p = '\0';
155                         opt->boot_args = talloc_asprintf(opt, "%s%s:%s",
156                                         "root=/dev/nfs ip=any nfsroot=",
157                                         server_ip, root_dir);
158
159                         talloc_free(root_dir);
160                 } else {
161                         opt->boot_args = talloc_asprintf(opt, "%s",
162                                         "root=/dev/nfs ip=any nfsroot=");
163                 }
164
165                 talloc_free(server_ip);
166         }
167
168         opt->description = talloc_asprintf(opt, "%s %s", opt->boot_image_file,
169                 opt->boot_args ? : "");
170
171         if (event_get_param(event, "default"))
172                 opt->is_default = true;
173
174         discover_context_add_boot_option(ctx, d_opt);
175
176         return 0;
177
178 fail_opt:
179         talloc_free(d_opt);
180 fail:
181         return -1;
182 }
183
184 static const char *parse_host_addr(struct event *event)
185 {
186         const char *val;
187
188         val = event_get_param(event, "tftp");
189         if (val)
190                 return val;
191
192         val = event_get_param(event, "siaddr");
193         if (val)
194                 return val;
195
196         val = event_get_param(event, "serverid");
197         if (val)
198                 return val;
199
200         return NULL;
201 }
202
203 static char *parse_mac_addr(struct discover_context *ctx, const char *mac)
204 {
205         unsigned int mac_addr_arr[MAC_ADDR_SIZE];
206         char *mac_addr;
207
208         sscanf(mac, "%X:%X:%X:%X:%X:%X", mac_addr_arr, mac_addr_arr + 1,
209                         mac_addr_arr + 2, mac_addr_arr + 3, mac_addr_arr + 4,
210                         mac_addr_arr + 5);
211
212         mac_addr = talloc_asprintf(ctx, "01-%02X-%02X-%02X-%02X-%02X-%02X",
213                         mac_addr_arr[0], mac_addr_arr[1], mac_addr_arr[2],
214                         mac_addr_arr[3], mac_addr_arr[4], mac_addr_arr[5]);
215
216         return mac_addr;
217 }
218
219 static char *parse_ip_addr(struct discover_context *ctx, const char *ip)
220 {
221         unsigned int ip_addr_arr[IP_ADDR_SIZE];
222         char *ip_hex;
223
224         sscanf(ip, "%u.%u.%u.%u", ip_addr_arr, ip_addr_arr + 1,
225                         ip_addr_arr + 2, ip_addr_arr + 3);
226
227         ip_hex = talloc_asprintf(ctx, "%02X%02X%02X%02X", ip_addr_arr[0],
228                         ip_addr_arr[1], ip_addr_arr[2], ip_addr_arr[3]);
229
230         return ip_hex;
231 }
232
233 struct pb_url *user_event_parse_conf_url(struct discover_context *ctx,
234                 struct event *event)
235 {
236         const char *conffile, *host, *bootfile;
237         char *p, *basedir, *url_str;
238         struct pb_url *url;
239
240         conffile = event_get_param(event, "pxeconffile");
241         if (conffile) {
242                 if (is_url(conffile)) {
243                         url = pb_url_parse(ctx, conffile);
244                 } else {
245                         host = parse_host_addr(event);
246                         if (!host) {
247                                 pb_log("%s: host address not found\n",
248                                                 __func__);
249                                 return NULL;
250                         }
251
252                         url_str = talloc_asprintf(ctx, "%s%s/%s", "tftp://",
253                                         host, conffile);
254                         url = pb_url_parse(ctx, url_str);
255
256                         talloc_free(url_str);
257                 }
258
259                 ctx->conf_url = url;
260         } else {
261                 host = parse_host_addr(event);
262                 if (!host) {
263                         pb_log("%s: host address not found\n", __func__);
264                         return NULL;
265                 }
266
267                 bootfile = event_get_param(event, "bootfile");
268                 if (!bootfile) {
269                         pb_log("%s: bootfile param not found\n", __func__);
270                         return NULL;
271                 }
272
273                 basedir = talloc_strdup(ctx, bootfile);
274                 p = strchr(basedir, '/');
275                 if (p)
276                         *p = '\0';
277
278                 if (!strcmp(basedir,"") || !strcmp(basedir, "."))
279                         url_str = talloc_asprintf(ctx, "%s%s/", "tftp://",host);
280                 else
281                         url_str = talloc_asprintf(ctx, "%s%s/%s/", "tftp://",host,
282                                         basedir);
283
284                 url = pb_url_parse(ctx, url_str);
285
286                 talloc_free(url_str);
287                 talloc_free(basedir);
288         }
289
290         return url;
291 }
292
293 char **user_event_parse_conf_filenames(
294                 struct discover_context *ctx, struct event *event)
295 {
296         char *mac_addr, *ip_hex;
297         const char *mac, *ip;
298         char **filenames;
299         int index, len;
300
301         mac = event_get_param(event, "mac");
302         if (mac)
303                 mac_addr = parse_mac_addr(ctx, mac);
304         else
305                 mac_addr = NULL;
306
307         ip = event_get_param(event, "ip");
308         if (ip) {
309                 ip_hex = parse_ip_addr(ctx, ip);
310                 len = strlen(ip_hex);
311         } else {
312                 ip_hex = NULL;
313                 len = 0;
314         }
315
316         if (!mac_addr && !ip_hex) {
317                 pb_log("%s: neither mac nor ip parameter found\n", __func__);
318                 return NULL;
319         }
320
321         /* Filenames as fallback IP's + mac + default */
322         filenames = talloc_array(ctx, char *, len + 3);
323
324         index = 0;
325         if (mac_addr)
326                 filenames[index++] = talloc_strdup(filenames, mac_addr);
327
328         while (len) {
329                 filenames[index++] = talloc_strdup(filenames, ip_hex);
330                 ip_hex[--len] = '\0';
331         }
332
333         filenames[index++] = talloc_strdup(filenames, "default");
334         filenames[index++] = NULL;
335
336         if (mac_addr)
337                 talloc_free(mac_addr);
338
339         if (ip_hex)
340                 talloc_free(ip_hex);
341
342         return filenames;
343 }
344
345 static int user_event_dhcp(struct user_event *uev, struct event *event)
346 {
347         struct device_handler *handler = uev->handler;
348         struct discover_device *dev;
349
350         dev = discover_device_create(handler, event->device);
351
352         device_handler_dhcp(handler, dev, event);
353
354         return 0;
355 }
356
357 static int user_event_conf(struct user_event *uev, struct event *event)
358 {
359         struct device_handler *handler = uev->handler;
360         struct discover_device *dev;
361         struct pb_url *url;
362         const char *val;
363
364         val = event_get_param(event, "url");
365         if (!val)
366                 return 0;
367
368         url = pb_url_parse(event, val);
369         if (!url)
370                 return 0;
371
372         dev = discover_device_create(handler, event->device);
373
374         device_handler_conf(handler, dev, url);
375
376         return 0;
377 }
378
379 static int user_event_add(struct user_event *uev, struct event *event)
380 {
381         struct device_handler *handler = uev->handler;
382         struct discover_context *ctx;
383         struct discover_device *dev;
384
385         dev = discover_device_create(handler, event->device);
386         ctx = device_handler_discover_context_create(handler, dev);
387
388         parse_user_event(ctx, event);
389
390         device_handler_discover_context_commit(handler, ctx);
391
392         talloc_free(ctx);
393
394         return 0;
395 }
396
397 static int user_event_remove(struct user_event *uev, struct event *event)
398 {
399         struct device_handler *handler = uev->handler;
400         struct discover_device *dev;
401
402         dev = device_lookup_by_id(handler, event->device);
403         if (!dev)
404                 return 0;
405
406         device_handler_remove(handler, dev);
407
408         return 0;
409 }
410
411 static void user_event_handle_message(struct user_event *uev, char *buf,
412         int len)
413 {
414         int result;
415         struct event *event;
416
417         event = talloc(uev, struct event);
418         event->type = EVENT_TYPE_USER;
419
420         result = event_parse_ad_message(event, buf, len);
421
422         if (result)
423                 return;
424
425         user_event_print_event(event);
426
427         switch (event->action) {
428         case EVENT_ACTION_ADD:
429                 result = user_event_add(uev, event);
430                 break;
431         case EVENT_ACTION_REMOVE:
432                 result = user_event_remove(uev, event);
433                 break;
434         case EVENT_ACTION_CONF:
435                 result = user_event_conf(uev, event);
436                 break;
437         case EVENT_ACTION_DHCP:
438                 result = user_event_dhcp(uev, event);
439                 break;
440         default:
441                 break;
442         }
443
444         talloc_free(event);
445
446         return;
447 }
448
449 static int user_event_process(void *arg)
450 {
451         struct user_event *uev = arg;
452         char buf[PBOOT_USER_EVENT_SIZE];
453         int len;
454
455         len = recvfrom(uev->socket, buf, sizeof(buf), 0, NULL, NULL);
456
457         if (len < 0) {
458                 pb_log("%s: socket read failed: %s", __func__, strerror(errno));
459                 return 0;
460         }
461
462         if (len == 0) {
463                 pb_log("%s: empty", __func__);
464                 return 0;
465         }
466
467         pb_debug("%s: %u bytes\n", __func__, len);
468
469         user_event_handle_message(uev, buf, len);
470
471         return 0;
472 }
473
474 static int user_event_destructor(void *arg)
475 {
476         struct user_event *uev = arg;
477
478         pb_debug("%s\n", __func__);
479
480         if (uev->socket >= 0)
481                 close(uev->socket);
482
483         return 0;
484 }
485
486 struct user_event *user_event_init(struct waitset *waitset,
487                 struct device_handler *handler)
488 {
489         struct sockaddr_un addr;
490         struct user_event *uev;
491
492         unlink(PBOOT_USER_EVENT_SOCKET);
493
494         uev = talloc(NULL, struct user_event);
495
496         uev->handler = handler;
497
498         uev->socket = socket(AF_UNIX, SOCK_DGRAM, 0);
499         if (uev->socket < 0) {
500                 pb_log("%s: Error creating event handler socket: %s\n",
501                         __func__, strerror(errno));
502                 goto out_err;
503         }
504
505         talloc_set_destructor(uev, user_event_destructor);
506
507         memset(&addr, 0, sizeof(addr));
508         addr.sun_family = AF_UNIX;
509         strcpy(addr.sun_path, PBOOT_USER_EVENT_SOCKET);
510
511         if (bind(uev->socket, (struct sockaddr *)&addr, sizeof(addr))) {
512                 pb_log("Error binding event handler socket: %s\n",
513                         strerror(errno));
514         }
515
516         waiter_register_io(waitset, uev->socket, WAIT_IN,
517                         user_event_process, uev);
518
519         pb_debug("%s: waiting on %s\n", __func__, PBOOT_USER_EVENT_SOCKET);
520
521         return uev;
522
523 out_err:
524         talloc_free(uev);
525         return NULL;
526 }
527
528 void user_event_destroy(struct user_event *uev)
529 {
530         talloc_free(uev);
531 }