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>
28 #include <sys/types.h>
33 #include <types/types.h>
34 #include <talloc/talloc.h>
35 #include <waiter/waiter.h>
37 #include "device-handler.h"
40 #include "user-event.h"
44 #define MAC_ADDR_SIZE 6
45 #define IP_ADDR_SIZE 4
48 struct device_handler *handler;
52 static const char *event_action_name(enum event_action action)
55 case EVENT_ACTION_ADD:
57 case EVENT_ACTION_REMOVE:
59 case EVENT_ACTION_URL:
61 case EVENT_ACTION_DHCP:
63 case EVENT_ACTION_BOOT:
65 case EVENT_ACTION_SYNC:
67 case EVENT_ACTION_PLUGIN:
76 static void user_event_print_event(struct event __attribute__((unused)) *event)
80 pb_debug("user_event %s event:\n", event_action_name(event->action));
81 pb_debug("\tdevice: %s\n", event->device);
83 for (i = 0; i < event->n_params; i++)
84 pb_debug("\t%-12s => %s\n",
85 event->params[i].name, event->params[i].value);
88 static struct resource *user_event_resource(struct discover_boot_option *opt,
89 struct event *event, bool gen_boot_args_sigfile)
91 const char *siaddr, *boot_file;
96 siaddr = event_get_param(event, "siaddr");
98 pb_log_fn("next server option not found\n");
102 boot_file = event_get_param(event, "bootfile");
104 pb_log_fn("bootfile not found\n");
108 if (gen_boot_args_sigfile) {
109 char* args_sigfile_default = talloc_asprintf(opt,
110 "%s.cmdline.sig", boot_file);
111 url_str = talloc_asprintf(opt, "%s%s/%s", "tftp://", siaddr,
112 args_sigfile_default);
113 talloc_free(args_sigfile_default);
116 url_str = talloc_asprintf(opt, "%s%s/%s", "tftp://", siaddr,
118 url = pb_url_parse(opt, url_str);
119 talloc_free(url_str);
124 res = create_url_resource(opt, url);
133 static int parse_user_event(struct discover_context *ctx, struct event *event)
135 struct discover_boot_option *d_opt;
136 char *server_ip, *root_dir, *p;
137 struct boot_option *opt;
141 dev = ctx->device->device;
143 d_opt = discover_boot_option_create(ctx, ctx->device);
149 val = event_get_param(event, "name");
152 pb_log_fn("no name found\n");
156 opt->id = talloc_asprintf(opt, "%s#%s", dev->id, val);
157 opt->name = talloc_strdup(opt, val);
159 d_opt->boot_image = user_event_resource(d_opt, event, false);
160 if (!d_opt->boot_image) {
161 pb_log_fn("no boot image found for %s!\n",
165 d_opt->args_sig_file = user_event_resource(d_opt, event, true);
167 val = event_get_param(event, "rootpath");
169 server_ip = talloc_strdup(opt, val);
170 p = strchr(server_ip, ':');
172 root_dir = talloc_strdup(opt, p + 1);
174 opt->boot_args = talloc_asprintf(opt, "%s%s:%s",
175 "root=/dev/nfs ip=any nfsroot=",
176 server_ip, root_dir);
178 talloc_free(root_dir);
180 opt->boot_args = talloc_asprintf(opt, "%s",
181 "root=/dev/nfs ip=any nfsroot=");
184 talloc_free(server_ip);
187 opt->description = talloc_asprintf(opt, "%s %s", opt->boot_image_file,
188 opt->boot_args ? : "");
190 if (event_get_param(event, "default"))
191 opt->is_default = true;
193 discover_context_add_boot_option(ctx, d_opt);
203 static const char *parse_host_addr(struct event *event)
207 val = event_get_param(event, "tftp");
211 val = event_get_param(event, "siaddr");
215 val = event_get_param(event, "serverid");
222 static char *parse_mac_addr(struct discover_context *ctx, const char *mac)
224 unsigned int mac_addr_arr[MAC_ADDR_SIZE];
227 sscanf(mac, "%X:%X:%X:%X:%X:%X", mac_addr_arr, mac_addr_arr + 1,
228 mac_addr_arr + 2, mac_addr_arr + 3, mac_addr_arr + 4,
231 mac_addr = talloc_asprintf(ctx, "01-%02x-%02x-%02x-%02x-%02x-%02x",
232 mac_addr_arr[0], mac_addr_arr[1], mac_addr_arr[2],
233 mac_addr_arr[3], mac_addr_arr[4], mac_addr_arr[5]);
238 static char *parse_ip_addr(struct discover_context *ctx, const char *ip)
240 unsigned int ip_addr_arr[IP_ADDR_SIZE];
243 sscanf(ip, "%u.%u.%u.%u", ip_addr_arr, ip_addr_arr + 1,
244 ip_addr_arr + 2, ip_addr_arr + 3);
246 ip_hex = talloc_asprintf(ctx, "%02X%02X%02X%02X", ip_addr_arr[0],
247 ip_addr_arr[1], ip_addr_arr[2], ip_addr_arr[3]);
252 struct pb_url *user_event_parse_conf_url(struct discover_context *ctx,
253 struct event *event, bool *is_complete)
255 const char *conffile, *pathprefix, *host, *bootfile, *bootfile_url;
256 char *p, *basedir, *url_str;
259 conffile = event_get_param(event, "pxeconffile");
260 pathprefix = event_get_param(event, "pxepathprefix");
261 bootfile = event_get_param(event, "bootfile");
262 bootfile_url = event_get_param(event, "bootfile_url");
264 /* If we're given a conf file, we're able to generate a complete URL to
265 * the configuration file, and the parser doesn't need to do any
266 * further autodiscovery */
267 *is_complete = !!conffile;
269 /* if conffile is a URL, that's all we need */
270 if (conffile && is_url(conffile)) {
271 url = pb_url_parse(ctx, conffile);
275 /* If we can create a URL from pathprefix (optionally with
276 * conffile appended to create a complete URL), use that */
277 if (pathprefix && is_url(pathprefix)) {
279 url_str = talloc_asprintf(ctx, "%s%s",
280 pathprefix, conffile);
281 url = pb_url_parse(ctx, url_str);
282 talloc_free(url_str);
284 url = pb_url_parse(ctx, pathprefix);
290 host = parse_host_addr(event);
292 pb_log_fn("host address not found\n");
294 /* No full URLs and no host address? Check for DHCPv6 options */
295 if (bootfile_url && is_url(bootfile_url)) {
297 return pb_url_parse(ctx, bootfile_url);
302 url_str = talloc_asprintf(ctx, "tftp://%s/", host);
304 /* if we have a pathprefix, use that directly.. */
306 /* strip leading slashes */
307 while (pathprefix[0] == '/')
309 url_str = talloc_asprintf_append(url_str, "%s", pathprefix);
311 /* ... otherwise, add a path based on the bootfile name, but only
312 * if conffile isn't an absolute path itself */
313 } else if (bootfile && !(conffile && conffile[0] == '/')) {
315 basedir = talloc_strdup(ctx, bootfile);
317 /* strip filename from the bootfile path, leaving only a
319 p = strrchr(basedir, '/');
325 url_str = talloc_asprintf_append(url_str, "%s/",
328 talloc_free(basedir);
331 /* finally, append conffile */
333 url_str = talloc_asprintf_append(url_str, "%s", conffile);
335 url = pb_url_parse(ctx, url_str);
337 talloc_free(url_str);
342 char **user_event_parse_conf_filenames(
343 struct discover_context *ctx, struct event *event)
345 char *mac_addr, *ip_hex;
346 const char *mac, *ip;
350 mac = event_get_param(event, "mac");
352 mac_addr = parse_mac_addr(ctx, mac);
356 ip = event_get_param(event, "ip");
358 ip_hex = parse_ip_addr(ctx, ip);
359 len = strlen(ip_hex);
365 if (!mac_addr && !ip_hex) {
366 pb_log_fn("neither mac nor ip parameter found\n");
370 /* Filenames as fallback IP's + mac + default */
371 filenames = talloc_array(ctx, char *, len + 3);
375 filenames[index++] = talloc_strdup(filenames, mac_addr);
378 filenames[index++] = talloc_strdup(filenames, ip_hex);
379 ip_hex[--len] = '\0';
382 filenames[index++] = talloc_strdup(filenames, "default");
383 filenames[index++] = NULL;
386 talloc_free(mac_addr);
394 static int user_event_dhcp(struct user_event *uev, struct event *event)
396 struct device_handler *handler = uev->handler;
397 struct discover_device *dev;
399 uint8_t hwaddr[MAC_ADDR_SIZE];
401 if (!event_get_param(event, "mac"))
403 if (!event_get_param(event, "ip") && !event_get_param(event, "ipv6"))
406 sscanf(event_get_param(event, "mac"),
407 "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX",
408 hwaddr, hwaddr + 1, hwaddr + 2,
409 hwaddr + 3, hwaddr + 4, hwaddr + 5);
411 if (event_get_param(event, "ipv6"))
412 system_info_set_interface_address(sizeof(hwaddr), hwaddr,
413 event_get_param(event, "ipv6"));
415 system_info_set_interface_address(sizeof(hwaddr), hwaddr,
416 event_get_param(event, "ip"));
418 dev = discover_device_create(handler, event_get_param(event, "mac"),
421 device_handler_dhcp(handler, dev, event);
426 static int user_event_add(struct user_event *uev, struct event *event)
428 struct device_handler *handler = uev->handler;
429 struct discover_context *ctx;
430 struct discover_device *dev;
433 /* In case this is a network interface, try to refer to it by UUID */
434 dev = discover_device_create(handler, event_get_param(event, "mac"),
436 dev->device->id = talloc_strdup(dev, event->device);
437 ctx = device_handler_discover_context_create(handler, dev);
439 rc = parse_user_event(ctx, event);
441 pb_log("parse_user_event returned %d\n", rc);
445 device_handler_discover_context_commit(handler, ctx);
452 static int user_event_remove(struct user_event *uev, struct event *event)
454 struct device_handler *handler = uev->handler;
455 struct discover_device *dev;
456 const char *mac = event_get_param(event, "mac");
459 dev = device_lookup_by_uuid(handler, event_get_param(event, "mac"));
461 dev = device_lookup_by_id(handler, event->device);
466 device_handler_remove(handler, dev);
471 static int user_event_url(struct user_event *uev, struct event *event)
473 struct device_handler *handler = uev->handler;
476 url = event_get_param(event, "url");
478 device_handler_process_url(handler, url, NULL, NULL);
483 static int user_event_boot(struct user_event *uev, struct event *event)
485 struct device_handler *handler = uev->handler;
486 struct boot_command *cmd = talloc_zero(handler, struct boot_command);
487 struct discover_boot_option *opt;
490 name = event_get_param(event, "name");
492 pb_log("Finding boot option %s @ %s\n", name, event->device);
493 opt = device_handler_find_option_by_name(handler,
494 event->device, name);
496 pb_log("No option with name %s\n", name);
500 pb_log("Found option with id %s!\n", opt->option->id);
501 cmd->option_id = talloc_strdup(cmd, opt->option->id);
503 pb_log("Booting based on full boot command\n");
504 cmd->option_id = talloc_strdup(cmd, event_get_param(event, "id"));
505 cmd->boot_image_file = talloc_strdup(cmd, event_get_param(event, "image"));
506 cmd->initrd_file = talloc_strdup(cmd, event_get_param(event, "initrd"));
507 cmd->dtb_file = talloc_strdup(cmd, event_get_param(event, "dtb"));
508 cmd->boot_args = talloc_strdup(cmd, event_get_param(event, "args"));
511 device_handler_boot(handler, false, cmd);
518 static int user_event_sync(struct user_event *uev, struct event *event)
520 struct device_handler *handler = uev->handler;
522 if (strncasecmp(event->device, "all", strlen("all")) != 0)
523 device_sync_snapshots(handler, event->device);
525 device_sync_snapshots(handler, NULL);
530 static int process_uninstalled_plugin(struct user_event *uev,
533 struct device_handler *handler = uev->handler;
534 struct discover_boot_option *file_opt;
535 struct discover_device *device;
536 struct discover_context *ctx;
538 struct resource *res;
540 if (!event_get_param(event, "path")) {
541 pb_log("Uninstalled pb-plugin event missing path param\n");
545 device = device_lookup_by_name(handler, event->device);
547 pb_log("Couldn't find device matching %s for plugin\n",
552 ctx = device_handler_discover_context_create(handler, device);
553 file_opt = discover_boot_option_create(ctx, device);
554 file_opt->option->name = talloc_strdup(file_opt,
555 event_get_param(event, "name"));
556 file_opt->option->id = talloc_asprintf(file_opt, "%s@%p",
557 device->device->id, file_opt);
558 file_opt->option->type = DISCOVER_PLUGIN_OPTION;
561 path = event_get_param(event, "path");
562 /* path may be relative to root */
563 if (strncmp(device->mount_path, path, strlen(device->mount_path)) == 0) {
564 path += strlen(device->mount_path) + 1;
567 res = talloc(file_opt, struct resource);
568 resolve_resource_against_device(res, device, path);
569 file_opt->boot_image = res;
571 discover_context_add_boot_option(ctx, file_opt);
572 device_handler_discover_context_commit(handler, ctx);
578 * Notification of a plugin event. This can either be for an uninstalled plugin
579 * that pb-plugin has scanned, or the result of a plugin that pb-plugin has
582 static int user_event_plugin(struct user_event *uev, struct event *event)
584 struct device_handler *handler = uev->handler;
585 char *executable, *executables, *saveptr;
586 struct plugin_option *opt;
587 const char *installed;
589 installed = event_get_param(event, "installed");
590 if (!installed || strncmp(installed, "no", strlen("no")) == 0)
591 return process_uninstalled_plugin(uev, event);
593 opt = talloc_zero(handler, struct plugin_option);
596 opt->name = talloc_strdup(opt, event_get_param(event, "name"));
597 opt->id = talloc_strdup(opt, event_get_param(event, "id"));
598 opt->version = talloc_strdup(opt, event_get_param(event, "version"));
599 opt->vendor = talloc_strdup(opt, event_get_param(event, "vendor"));
600 opt->vendor_id = talloc_strdup(opt, event_get_param(event, "vendor_id"));
601 opt->date = talloc_strdup(opt, event_get_param(event, "date"));
602 opt->plugin_file = talloc_strdup(opt,
603 event_get_param(event, "source_file"));
605 executables = talloc_strdup(opt, event_get_param(event, "executables"));
612 * The 'executables' parameter is a space-delimited list of installed
615 executable = strtok_r(executables, " ", &saveptr);
617 opt->executables = talloc_realloc(opt, opt->executables,
618 char *, opt->n_executables + 1);
619 if (!opt->executables) {
623 opt->executables[opt->n_executables++] = talloc_strdup(opt,
625 executable = strtok_r(NULL, " ", &saveptr);
628 device_handler_add_plugin_option(handler, opt);
630 talloc_free(executables);
635 static void user_event_handle_message(struct user_event *uev, char *buf,
641 event = talloc(uev, struct event);
642 event->type = EVENT_TYPE_USER;
644 result = event_parse_ad_message(event, buf, len);
649 user_event_print_event(event);
651 switch (event->action) {
652 case EVENT_ACTION_ADD:
653 result = user_event_add(uev, event);
655 case EVENT_ACTION_REMOVE:
656 result = user_event_remove(uev, event);
658 case EVENT_ACTION_URL:
659 result = user_event_url(uev, event);
661 case EVENT_ACTION_DHCP:
662 result = user_event_dhcp(uev, event);
664 case EVENT_ACTION_BOOT:
665 result = user_event_boot(uev, event);
667 case EVENT_ACTION_SYNC:
668 result = user_event_sync(uev, event);
670 case EVENT_ACTION_PLUGIN:
671 result = user_event_plugin(uev, event);
677 /* user_event_url() and user_event_dhcp() will steal the event context,
678 * but all others still need to free */
684 static int user_event_process(void *arg)
686 struct user_event *uev = arg;
687 char buf[PBOOT_USER_EVENT_SIZE + 1];
690 len = recvfrom(uev->socket, buf, PBOOT_USER_EVENT_SIZE, 0, NULL, NULL);
693 pb_log_fn("socket read failed: %s\n", strerror(errno));
698 pb_log_fn("empty\n");
704 pb_debug("%s: %u bytes\n", __func__, len);
706 user_event_handle_message(uev, buf, len);
711 static int user_event_destructor(void *arg)
713 struct user_event *uev = arg;
715 pb_debug("%s\n", __func__);
717 if (uev->socket >= 0)
723 struct user_event *user_event_init(struct device_handler *handler,
724 struct waitset *waitset)
726 struct sockaddr_un addr;
727 struct user_event *uev;
729 unlink(PBOOT_USER_EVENT_SOCKET);
731 uev = talloc(handler, struct user_event);
733 uev->handler = handler;
735 uev->socket = socket(AF_UNIX, SOCK_DGRAM, 0);
736 if (uev->socket < 0) {
737 pb_log("%s: Error creating event handler socket: %s\n",
738 __func__, strerror(errno));
742 talloc_set_destructor(uev, user_event_destructor);
744 memset(&addr, 0, sizeof(addr));
745 addr.sun_family = AF_UNIX;
746 strcpy(addr.sun_path, PBOOT_USER_EVENT_SOCKET);
748 if (bind(uev->socket, (struct sockaddr *)&addr, sizeof(addr))) {
749 pb_log("Error binding event handler socket: %s\n",
753 /* Don't allow events from non-priviledged users */
754 chown(PBOOT_USER_EVENT_SOCKET, 0, 0);
755 chmod(PBOOT_USER_EVENT_SOCKET, 0660);
757 waiter_register_io(waitset, uev->socket, WAIT_IN,
758 user_event_process, uev);
760 pb_debug("%s: waiting on %s\n", __func__, PBOOT_USER_EVENT_SOCKET);