2 * Copyright (C) 2009 Sony Computer Entertainment Inc.
3 * Copyright 2009 Sony Corp.
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.
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.
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
19 #if defined(HAVE_CONFIG_H)
26 #include <sys/socket.h>
27 #include <sys/types.h>
32 #include <types/types.h>
33 #include <talloc/talloc.h>
34 #include <waiter/waiter.h>
36 #include "device-handler.h"
39 #include "user-event.h"
42 #define MAC_ADDR_SIZE 6
43 #define IP_ADDR_SIZE 4
46 struct device_handler *handler;
50 static const char *event_action_name(enum event_action action)
53 case EVENT_ACTION_ADD:
55 case EVENT_ACTION_REMOVE:
57 case EVENT_ACTION_URL:
59 case EVENT_ACTION_DHCP:
61 case EVENT_ACTION_BOOT:
63 case EVENT_ACTION_SYNC:
72 static void user_event_print_event(struct event __attribute__((unused)) *event)
76 pb_debug("user_event %s event:\n", event_action_name(event->action));
77 pb_debug("\tdevice: %s\n", event->device);
79 for (i = 0; i < event->n_params; i++)
80 pb_debug("\t%-12s => %s\n",
81 event->params[i].name, event->params[i].value);
84 static struct resource *user_event_resource(struct discover_boot_option *opt,
87 const char *siaddr, *boot_file;
92 siaddr = event_get_param(event, "siaddr");
94 pb_log("%s: next server option not found\n", __func__);
98 boot_file = event_get_param(event, "bootfile");
100 pb_log("%s: bootfile not found\n", __func__);
104 url_str = talloc_asprintf(opt, "%s%s/%s", "tftp://", siaddr, boot_file);
105 url = pb_url_parse(opt, url_str);
106 talloc_free(url_str);
111 res = create_url_resource(opt, url);
120 static int parse_user_event(struct discover_context *ctx, struct event *event)
122 struct discover_boot_option *d_opt;
123 char *server_ip, *root_dir, *p;
124 struct boot_option *opt;
128 dev = ctx->device->device;
130 d_opt = discover_boot_option_create(ctx, ctx->device);
136 val = event_get_param(event, "name");
139 pb_log("%s: no name found\n", __func__);
143 opt->id = talloc_asprintf(opt, "%s#%s", dev->id, val);
144 opt->name = talloc_strdup(opt, val);
146 d_opt->boot_image = user_event_resource(d_opt, event);
147 if (!d_opt->boot_image) {
148 pb_log("%s: no boot image found for %s!\n", __func__,
153 val = event_get_param(event, "rootpath");
155 server_ip = talloc_strdup(opt, val);
156 p = strchr(server_ip, ':');
158 root_dir = talloc_strdup(opt, p + 1);
160 opt->boot_args = talloc_asprintf(opt, "%s%s:%s",
161 "root=/dev/nfs ip=any nfsroot=",
162 server_ip, root_dir);
164 talloc_free(root_dir);
166 opt->boot_args = talloc_asprintf(opt, "%s",
167 "root=/dev/nfs ip=any nfsroot=");
170 talloc_free(server_ip);
173 opt->description = talloc_asprintf(opt, "%s %s", opt->boot_image_file,
174 opt->boot_args ? : "");
176 if (event_get_param(event, "default"))
177 opt->is_default = true;
179 discover_context_add_boot_option(ctx, d_opt);
189 static const char *parse_host_addr(struct event *event)
193 val = event_get_param(event, "tftp");
197 val = event_get_param(event, "siaddr");
201 val = event_get_param(event, "serverid");
208 static char *parse_mac_addr(struct discover_context *ctx, const char *mac)
210 unsigned int mac_addr_arr[MAC_ADDR_SIZE];
213 sscanf(mac, "%X:%X:%X:%X:%X:%X", mac_addr_arr, mac_addr_arr + 1,
214 mac_addr_arr + 2, mac_addr_arr + 3, mac_addr_arr + 4,
217 mac_addr = talloc_asprintf(ctx, "01-%02x-%02x-%02x-%02x-%02x-%02x",
218 mac_addr_arr[0], mac_addr_arr[1], mac_addr_arr[2],
219 mac_addr_arr[3], mac_addr_arr[4], mac_addr_arr[5]);
224 static char *parse_ip_addr(struct discover_context *ctx, const char *ip)
226 unsigned int ip_addr_arr[IP_ADDR_SIZE];
229 sscanf(ip, "%u.%u.%u.%u", ip_addr_arr, ip_addr_arr + 1,
230 ip_addr_arr + 2, ip_addr_arr + 3);
232 ip_hex = talloc_asprintf(ctx, "%02X%02X%02X%02X", ip_addr_arr[0],
233 ip_addr_arr[1], ip_addr_arr[2], ip_addr_arr[3]);
238 struct pb_url *user_event_parse_conf_url(struct discover_context *ctx,
239 struct event *event, bool *is_complete)
241 const char *conffile, *pathprefix, *host, *bootfile;
242 char *p, *basedir, *url_str;
245 conffile = event_get_param(event, "pxeconffile");
246 pathprefix = event_get_param(event, "pxepathprefix");
247 bootfile = event_get_param(event, "bootfile");
249 /* If we're given a conf file, we're able to generate a complete URL to
250 * the configuration file, and the parser doesn't need to do any
251 * further autodiscovery */
252 *is_complete = !!conffile;
254 /* if conffile is a URL, that's all we need */
255 if (conffile && is_url(conffile)) {
256 url = pb_url_parse(ctx, conffile);
260 /* If we can create a URL from pathprefix (optionally with
261 * conffile appended to create a complete URL), use that */
262 if (pathprefix && is_url(pathprefix)) {
264 url_str = talloc_asprintf(ctx, "%s%s",
265 pathprefix, conffile);
266 url = pb_url_parse(ctx, url_str);
267 talloc_free(url_str);
269 url = pb_url_parse(ctx, pathprefix);
275 host = parse_host_addr(event);
277 pb_log("%s: host address not found\n", __func__);
281 url_str = talloc_asprintf(ctx, "tftp://%s/", host);
283 /* if we have a pathprefix, use that directly.. */
285 /* strip leading slashes */
286 while (pathprefix[0] == '/')
288 url_str = talloc_asprintf_append(url_str, "%s", pathprefix);
290 /* ... otherwise, add a path based on the bootfile name, but only
291 * if conffile isn't an absolute path itself */
292 } else if (bootfile && !(conffile && conffile[0] == '/')) {
294 basedir = talloc_strdup(ctx, bootfile);
296 /* strip filename from the bootfile path, leaving only a
298 p = strrchr(basedir, '/');
304 url_str = talloc_asprintf_append(url_str, "%s/",
307 talloc_free(basedir);
310 /* finally, append conffile */
312 url_str = talloc_asprintf_append(url_str, "%s", conffile);
314 url = pb_url_parse(ctx, url_str);
316 talloc_free(url_str);
321 char **user_event_parse_conf_filenames(
322 struct discover_context *ctx, struct event *event)
324 char *mac_addr, *ip_hex;
325 const char *mac, *ip;
329 mac = event_get_param(event, "mac");
331 mac_addr = parse_mac_addr(ctx, mac);
335 ip = event_get_param(event, "ip");
337 ip_hex = parse_ip_addr(ctx, ip);
338 len = strlen(ip_hex);
344 if (!mac_addr && !ip_hex) {
345 pb_log("%s: neither mac nor ip parameter found\n", __func__);
349 /* Filenames as fallback IP's + mac + default */
350 filenames = talloc_array(ctx, char *, len + 3);
354 filenames[index++] = talloc_strdup(filenames, mac_addr);
357 filenames[index++] = talloc_strdup(filenames, ip_hex);
358 ip_hex[--len] = '\0';
361 filenames[index++] = talloc_strdup(filenames, "default");
362 filenames[index++] = NULL;
365 talloc_free(mac_addr);
373 static int user_event_dhcp(struct user_event *uev, struct event *event)
375 struct device_handler *handler = uev->handler;
376 struct discover_device *dev;
378 dev = discover_device_create(handler, event->device);
380 device_handler_dhcp(handler, dev, event);
385 static int user_event_conf(struct user_event *uev, struct event *event)
387 struct device_handler *handler = uev->handler;
388 struct discover_device *dev;
392 val = event_get_param(event, "url");
396 url = pb_url_parse(event, val);
400 dev = discover_device_create(handler, event->device);
402 device_handler_conf(handler, dev, url);
407 static int user_event_add(struct user_event *uev, struct event *event)
409 struct device_handler *handler = uev->handler;
410 struct discover_context *ctx;
411 struct discover_device *dev;
413 dev = discover_device_create(handler, event->device);
414 ctx = device_handler_discover_context_create(handler, dev);
416 parse_user_event(ctx, event);
418 device_handler_discover_context_commit(handler, ctx);
425 static int user_event_remove(struct user_event *uev, struct event *event)
427 struct device_handler *handler = uev->handler;
428 struct discover_device *dev;
430 dev = device_lookup_by_id(handler, event->device);
434 device_handler_remove(handler, dev);
439 static int user_event_url(struct user_event *uev, struct event *event)
441 struct device_handler *handler = uev->handler;
444 url = event_get_param(event, "url");
446 device_handler_process_url(handler, url, NULL, NULL);
451 static int user_event_boot(struct user_event *uev, struct event *event)
453 struct device_handler *handler = uev->handler;
454 struct boot_command *cmd = talloc(handler, struct boot_command);
456 cmd->option_id = talloc_strdup(cmd, event_get_param(event, "id"));
457 cmd->boot_image_file = talloc_strdup(cmd, event_get_param(event, "image"));
458 cmd->initrd_file = talloc_strdup(cmd, event_get_param(event, "initrd"));
459 cmd->dtb_file = talloc_strdup(cmd, event_get_param(event, "dtb"));
460 cmd->boot_args = talloc_strdup(cmd, event_get_param(event, "args"));
462 device_handler_boot(handler, cmd);
469 static int user_event_sync(struct user_event *uev, struct event *event)
471 struct device_handler *handler = uev->handler;
473 if (strncasecmp(event->device, "all", strlen("all")) != 0)
474 device_sync_snapshots(handler, event->device);
476 device_sync_snapshots(handler, NULL);
481 static void user_event_handle_message(struct user_event *uev, char *buf,
487 event = talloc(uev, struct event);
488 event->type = EVENT_TYPE_USER;
490 result = event_parse_ad_message(event, buf, len);
495 user_event_print_event(event);
497 switch (event->action) {
498 case EVENT_ACTION_ADD:
499 result = user_event_add(uev, event);
501 case EVENT_ACTION_REMOVE:
502 result = user_event_remove(uev, event);
504 case EVENT_ACTION_URL:
505 result = user_event_url(uev, event);
507 case EVENT_ACTION_CONF:
508 result = user_event_conf(uev, event);
510 case EVENT_ACTION_DHCP:
511 result = user_event_dhcp(uev, event);
513 case EVENT_ACTION_BOOT:
514 result = user_event_boot(uev, event);
516 case EVENT_ACTION_SYNC:
517 result = user_event_sync(uev, event);
528 static int user_event_process(void *arg)
530 struct user_event *uev = arg;
531 char buf[PBOOT_USER_EVENT_SIZE + 1];
534 len = recvfrom(uev->socket, buf, PBOOT_USER_EVENT_SIZE, 0, NULL, NULL);
537 pb_log("%s: socket read failed: %s", __func__, strerror(errno));
542 pb_log("%s: empty", __func__);
548 pb_debug("%s: %u bytes\n", __func__, len);
550 user_event_handle_message(uev, buf, len);
555 static int user_event_destructor(void *arg)
557 struct user_event *uev = arg;
559 pb_debug("%s\n", __func__);
561 if (uev->socket >= 0)
567 struct user_event *user_event_init(struct device_handler *handler,
568 struct waitset *waitset)
570 struct sockaddr_un addr;
571 struct user_event *uev;
573 unlink(PBOOT_USER_EVENT_SOCKET);
575 uev = talloc(handler, struct user_event);
577 uev->handler = handler;
579 uev->socket = socket(AF_UNIX, SOCK_DGRAM, 0);
580 if (uev->socket < 0) {
581 pb_log("%s: Error creating event handler socket: %s\n",
582 __func__, strerror(errno));
586 talloc_set_destructor(uev, user_event_destructor);
588 memset(&addr, 0, sizeof(addr));
589 addr.sun_family = AF_UNIX;
590 strcpy(addr.sun_path, PBOOT_USER_EVENT_SOCKET);
592 if (bind(uev->socket, (struct sockaddr *)&addr, sizeof(addr))) {
593 pb_log("Error binding event handler socket: %s\n",
597 waiter_register_io(waitset, uev->socket, WAIT_IN,
598 user_event_process, uev);
600 pb_debug("%s: waiting on %s\n", __func__, PBOOT_USER_EVENT_SOCKET);