struct list_item list;
};
+struct crypt_info {
+ struct discover_device *source_device;
+ char *dm_name;
+
+ struct list_item list;
+};
+
struct device_handler {
struct discover_server *server;
int dry_run;
struct autoboot_option *temp_autoboot;
struct discover_boot_option *default_boot_option;
+ struct discover_boot_option *last_boot_option;
int default_boot_option_priority;
struct list unresolved_boot_options;
struct plugin_option **plugins;
unsigned int n_plugins;
bool plugin_installing;
+
+ struct list crypt_devices;
};
static int mount_device(struct discover_device *dev);
static int destroy_device(void *arg)
{
struct discover_device *dev = arg;
+ struct process *p;
umount_device(dev);
+ devmapper_destroy_snapshot(dev);
+
+ if (dev->crypt_device) {
+ const char *argv[] = {
+ pb_system_apps.cryptsetup,
+ "luksClose",
+ dev->device->id,
+ NULL
+ };
+
+ p = process_create(dev);
+ p->path = pb_system_apps.cryptsetup;
+ p->argv = argv;
+
+ if (process_run_async(p)) {
+ pb_log("Failed to run cryptsetup\n");
+ return -1;
+ }
+ }
+
return 0;
}
list_init(&handler->unresolved_boot_options);
list_init(&handler->progress);
+ list_init(&handler->crypt_devices);
/* set up our mount point base */
pb_mkdir_recursive(mount_base());
void device_handler_reinit(struct device_handler *handler)
{
struct discover_boot_option *opt, *tmp;
+ struct crypt_info *crypt, *c;
struct ramdisk_device *ramdisk;
struct config *config;
unsigned int i;
discover_server_notify_plugins_remove(handler->server);
+ /* forget encrypted devices */
+ list_for_each_entry_safe(&handler->crypt_devices, crypt, c, list)
+ talloc_free(crypt);
+ list_init(&handler->crypt_devices);
+
set_env_variables(config_get());
/* If the safe mode warning was active disable it now */
void device_handler_status(struct device_handler *handler,
struct status *status)
{
+ pb_debug("%s: %s\n", __func__, status->message);
discover_server_notify_boot_status(handler->server, status);
}
status.type = type;
status.message = talloc_vasprintf(handler, fmt, ap);
status.backlog = false;
+ status.boot_active = false;
device_handler_status(handler, &status);
}
if (!update) {
- pb_log("%s: failed to allocate new status\n", __func__);
+ pb_log_fn("failed to allocate new status\n");
} else {
device_handler_status_info(handler, "%s\n", update);
talloc_free(update);
opt = handler->default_boot_option;
+ handler->last_boot_option = opt;
+
if (handler->sec_to_boot) {
countdown_status(handler, opt, handler->sec_to_boot);
handler->sec_to_boot--;
return;
}
+ if (handler->default_boot_option)
+ handler->default_boot_option->option->is_autoboot_default = false;
+ opt->option->is_autoboot_default = true;
+
handler->sec_to_boot = config_get()->autoboot_timeout_sec;
handler->default_boot_option = opt;
handler->default_boot_option_priority = new_prio;
}
handler->ramdisks[i] = dev;
- i = handler->n_ramdisks++;
+ handler->n_ramdisks++;
}
struct ramdisk_device *device_handler_get_ramdisk(
device->ramdisk = NULL;
}
+/*
+ * Check if a device name matches the name of an encrypted device that has been
+ * opened. If it matches remove it from the list and remove the original crypt
+ * discover device.
+ */
+bool device_handler_found_crypt_device(struct device_handler *handler,
+ const char *name)
+{
+ struct crypt_info *crypt, *c;
+
+ list_for_each_entry_safe(&handler->crypt_devices, crypt, c, list) {
+ if (!strncmp(crypt->dm_name, name, strlen(crypt->dm_name))) {
+ device_handler_remove(handler, crypt->source_device);
+ list_remove(&crypt->list);
+ talloc_free(crypt);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void cryptsetup_cb(struct process *process)
+{
+ struct device_handler *handler = process->data;
+ struct crypt_info *crypt, *c;
+
+ if (process->exit_status == 0)
+ return;
+ device_handler_status_err(handler,
+ _("Failed to open encrypted device %s"),
+ process->argv[2]);
+
+ /*
+ * Failed to open the device; stop tracking it, but don't remove
+ * the source device.
+ */
+ list_for_each_entry_safe(&handler->crypt_devices, crypt, c, list) {
+ if (!strncmp(crypt->dm_name, process->argv[3],
+ strlen(crypt->dm_name))) {
+ list_remove(&crypt->list);
+ talloc_free(crypt);
+ break;
+ }
+ }
+}
+
+void device_handler_open_encrypted_dev(struct device_handler *handler,
+ char *password, char *device_id)
+{
+ struct discover_device *dev;
+ struct crypt_info *crypt;
+ const char *device_path, **argv;
+ struct process *p;
+ char *name;
+ int result;
+
+ dev = device_lookup_by_id(handler, device_id);
+ if (!dev) {
+ pb_log_fn("Can't find device %s\n", device_id);
+ device_handler_status_err(handler,
+ _("Encrypted device %s does not exist"),
+ device_id);
+ return;
+ }
+
+ device_path = dev->device_path;
+ name = talloc_asprintf(handler, "luks_%s", device_id);
+
+ p = process_create(handler);
+ /* talloc argv under the process so we can access it in cryptsetup_cb */
+ argv = talloc_zero_array(p, const char *, 6);
+ argv[0] = talloc_strdup(argv, pb_system_apps.cryptsetup);
+ argv[1] = talloc_asprintf(argv, "luksOpen");
+ argv[2] = talloc_strdup(argv, device_path);
+ argv[3] = talloc_strdup(argv, name);
+ argv[4] = talloc_asprintf(argv, "-");
+ argv[5] = NULL;
+
+ p->path = pb_system_apps.cryptsetup;
+ p->argv = (const char **)argv;
+ p->exit_cb = cryptsetup_cb;
+ p->data = handler;
+ p->keep_stdout = true;
+ p->pipe_stdin = talloc_asprintf(p, "%s\n", password);
+
+ result = process_run_async(p);
+ if (result) {
+ pb_log("Failed to run cryptsetup\n");
+ return;
+ }
+
+ crypt = talloc(handler, struct crypt_info);
+ crypt->source_device = dev;
+ crypt->dm_name = name;
+ talloc_steal(crypt, name);
+ list_add(&handler->crypt_devices, &crypt->list);
+}
+
+void device_handler_add_encrypted_dev(struct device_handler *handler,
+ struct discover_device *dev)
+{
+ system_info_register_blockdev(dev->device->id, dev->uuid, "");
+ discover_server_notify_device_add(handler->server,
+ dev->device);
+ dev->notified = true;
+ if (!device_lookup_by_uuid(handler, dev->uuid))
+ device_handler_add_device(handler, dev);
+}
+
/* Start discovery on a hotplugged device. The device will be in our devices
* array, but has only just been initialised by the hotplug source.
*/
struct requery_data *rqd = data;
struct device_handler *handler;
struct discover_device *device;
+ bool autoboot;
handler = rqd->handler;
device = rqd->device;
talloc_free(opt);
}
+ /* Track whether autoboot was enabled, if we cancel a default option
+ * it will be switched off.
+ */
+ autoboot = handler->autoboot_enabled;
+
list_for_each_entry_safe(&device->boot_options, opt, tmp, list) {
if (opt == handler->default_boot_option) {
- pb_log("Default option %s cancelled since device is being requeried",
+ pb_log("Default option %s cancelled since device is being requeried\n",
opt->option->name);
device_handler_cancel_default(handler);
}
talloc_free(opt);
}
+ handler->autoboot_enabled = autoboot;
+
discover_server_notify_device_remove(handler->server, device->device);
device->notified = false;
struct discover_device *dev, struct event *event)
{
struct discover_context *ctx;
+ const char *ip;
+
+ if (event_get_param(event, "ipv6"))
+ ip = event_get_param(event, "ipv6");
+ else
+ ip = event_get_param(event, "ip");
device_handler_status_dev_info(handler, dev,
- _("Processing DHCP lease response (ip: %s)"),
- event_get_param(event, "ip"));
+ _("Processing DHCP lease response (ip: %s)"), ip);
pending_network_jobs_start();
return 0;
}
+struct discover_boot_option *device_handler_find_option_by_name(
+ struct device_handler *handler, const char *device,
+ const char *name)
+{
+ size_t len = strlen(name);
+ unsigned int i;
+
+ for (i = 0; i < handler->n_devices; i++) {
+ struct discover_device *dev = handler->devices[i];
+ struct discover_boot_option *opt;
+
+ list_for_each_entry(&dev->boot_options, opt, list)
+ /* Match exactly, partial matches can be quite common */
+ if (strlen(opt->option->name) == len &&
+ !strcmp(opt->option->name, name))
+ if (!dev || !strcmp(opt->option->device_id, device))
+ return opt;
+ }
+
+ return NULL;
+}
+
static struct discover_boot_option *find_boot_option_by_id(
struct device_handler *handler, const char *id)
{
}
void device_handler_boot(struct device_handler *handler,
- struct boot_command *cmd)
+ bool change_default, struct boot_command *cmd)
{
struct discover_boot_option *opt = NULL;
if (cmd->option_id && strlen(cmd->option_id))
opt = find_boot_option_by_id(handler, cmd->option_id);
+ /* Don't allow a normal client to change the default */
+ if (!change_default && handler->last_boot_option &&
+ opt != handler->last_boot_option) {
+ pb_log("Non-root user tried to change boot option\n");
+ device_handler_status_err(handler,
+ "Must be root to change default boot option\n");
+ return;
+ }
+
if (handler->pending_boot)
boot_cancel(handler->pending_boot);
int rc;
rc = config_set(config);
- if (rc)
+ if (rc) {
+ device_handler_status_err(handler,
+ "Failed to update configuration!");
return;
+ }
discover_server_notify_config(handler->server, config);
device_handler_update_lang(config->lang);
static char *device_from_addr(void *ctx, struct pb_url *url)
{
char *ipaddr, *buf, *tok, *dev = NULL;
+ bool ipv6_route;
const char *delim = " ";
- struct sockaddr_in *ip;
- struct sockaddr_in si;
+ struct sockaddr_in *ipv4;
+ struct sockaddr_in6 *ipv6;
struct addrinfo *res;
struct process *p;
int rc;
- /* Note: IPv4 only */
- rc = inet_pton(AF_INET, url->host, &(si.sin_addr));
- if (rc > 0) {
- ipaddr = url->host;
- } else {
- /* need to turn hostname into a valid IP */
- rc = getaddrinfo(url->host, NULL, NULL, &res);
- if (rc) {
- pb_debug("%s: Invalid URL\n",__func__);
- return NULL;
- }
+ /* Confirm url->host is either a valid hostname, or a
+ * valid IPv4 or IPv6 address */
+ rc = getaddrinfo(url->host, NULL, NULL, &res);
+ if (rc) {
+ pb_debug("%s: Invalid URL\n",__func__);
+ return NULL;
+ }
+
+ switch (res->ai_family) {
+ case AF_INET: /* ipv4 */
ipaddr = talloc_array(ctx,char,INET_ADDRSTRLEN);
- ip = (struct sockaddr_in *) res->ai_addr;
- inet_ntop(AF_INET, &(ip->sin_addr), ipaddr, INET_ADDRSTRLEN);
+ ipv4 = (struct sockaddr_in *) res->ai_addr;
+ inet_ntop(AF_INET, &(ipv4->sin_addr), ipaddr, INET_ADDRSTRLEN);
+ ipv6_route = false;
+ break;
+ case AF_INET6: /* ipv6 */
+ ipaddr = talloc_array(ctx,char,INET6_ADDRSTRLEN);
+ ipv6 = (struct sockaddr_in6 *) res->ai_addr;
+ inet_ntop(AF_INET6, &(ipv6->sin6_addr), ipaddr, INET6_ADDRSTRLEN);
+ ipv6_route = true;
+ break;
+ default: /* error */
freeaddrinfo(res);
+ return NULL;
}
+ freeaddrinfo(res);
const char *argv[] = {
pb_system_apps.ip,
+ ipv6_route ? "-6" : "-4",
"route", "show", "to", "match",
ipaddr,
NULL
const char *mac;
if (result->status != LOAD_OK) {
- pb_log("%s: Load failed for %s\n", __func__, result->url->full);
+ pb_log_fn("Load failed for %s\n", result->url->full);
return;
}
struct device_handler *handler = process->data;
if (!handler) {
- pb_log("%s: Missing data!\n", __func__);
+ pb_log_fn("Missing data!\n");
return;
}
return -1;
dev->mounted = false;
- devmapper_destroy_snapshot(dev);
pb_rmdir_recursive(mount_base(), dev->mount_path);
struct device_handler *handler __attribute__((unused)),
struct discover_context *ctx __attribute__((unused)))
{
- pb_log("%s stubbed out for test cases\n", __func__);
+ pb_log_fn("stubbed out for test cases\n");
}
static void device_handler_update_lang(const char *lang __attribute__((unused)))