+void discover_client_destroy(struct discover_client *client)
+{
+ talloc_free(client);
+}
+
+static struct device *find_device(struct discover_client *client,
+ const char *id)
+{
+ int i;
+
+ for (i = 0; i < client->n_devices; i++) {
+ struct device *dev = client->devices[i];
+ if (!strcmp(dev->id, id))
+ return dev;
+ }
+
+ return NULL;
+}
+
+static void device_add(struct discover_client *client, struct device *device)
+{
+ client->n_devices++;
+ client->devices = talloc_realloc(client, client->devices,
+ struct device *, client->n_devices);
+
+ client->devices[client->n_devices - 1] = device;
+ talloc_steal(client, device);
+
+ if (client->ops.device_add)
+ client->ops.device_add(device, client->ops.cb_arg);
+}
+
+static void boot_option_add(struct discover_client *client,
+ struct boot_option *opt)
+{
+ struct device *dev;
+
+ dev = find_device(client, opt->device_id);
+
+ /* we require that devices are already present before any boot options
+ * are added */
+ assert(dev);
+
+ talloc_steal(dev, opt);
+
+ if (client->ops.boot_option_add)
+ client->ops.boot_option_add(dev, opt, client->ops.cb_arg);
+}
+
+static void device_remove(struct discover_client *client, const char *id)
+{
+ struct device *device = NULL;
+ int i;
+
+ for (i = 0; i < client->n_devices; i++) {
+ if (!strcmp(client->devices[i]->id, id)) {
+ device = client->devices[i];
+ break;
+ }
+ }
+
+ if (!device)
+ return;
+
+ /* remove the device from the client's device array */
+ client->n_devices--;
+ memmove(&client->devices[i], &client->devices[i+1],
+ (client->n_devices - i) * sizeof(client->devices[0]));
+ client->devices = talloc_realloc(client, client->devices,
+ struct device *, client->n_devices);
+
+ /* notify the UI */
+ client->ops.device_remove(device, client->ops.cb_arg);
+
+ talloc_free(device);
+}
+
+static void update_status(struct discover_client *client,
+ struct boot_status *status)
+{
+ if (client->ops.update_status)
+ client->ops.update_status(status, client->ops.cb_arg);
+ talloc_free(status);
+}
+
+static int discover_client_process(void *arg)
+{
+ struct discover_client *client = arg;
+ struct pb_protocol_message *message;
+ struct boot_status *status;
+ struct boot_option *opt;
+ struct device *dev;
+ char *dev_id;
+ int rc;
+
+ message = pb_protocol_read_message(client, client->fd);
+
+ if (!message)
+ return -1;
+
+ switch (message->action) {
+ case PB_PROTOCOL_ACTION_DEVICE_ADD:
+ dev = talloc_zero(client, struct device);
+ list_init(&dev->boot_options);
+
+ rc = pb_protocol_deserialise_device(dev, message);
+ if (rc) {
+ pb_log("%s: no device?\n", __func__);
+ return 0;
+ }
+
+ device_add(client, dev);
+ break;
+ case PB_PROTOCOL_ACTION_BOOT_OPTION_ADD:
+ opt = talloc_zero(client, struct boot_option);
+
+ rc = pb_protocol_deserialise_boot_option(opt, message);
+ if (rc) {
+ pb_log("%s: no boot_option?\n", __func__);
+ return 0;
+ }
+
+ boot_option_add(client, opt);
+ break;
+ case PB_PROTOCOL_ACTION_DEVICE_REMOVE:
+ dev_id = pb_protocol_deserialise_string(client, message);
+ if (!dev_id) {
+ pb_log("%s: no device id?\n", __func__);
+ return 0;
+ }
+ device_remove(client, dev_id);
+ break;
+ case PB_PROTOCOL_ACTION_STATUS:
+ status = talloc_zero(client, struct boot_status);
+
+ rc = pb_protocol_deserialise_boot_status(status, message);
+ if (rc) {
+ pb_log("%s: invalid status message?\n", __func__);
+ return 0;
+ }
+ update_status(client, status);
+ break;
+ default:
+ pb_log("%s: unknown action %d\n", __func__, message->action);
+ }
+
+
+ return 0;
+}
+
+struct discover_client* discover_client_init(struct waitset *waitset,
+ const struct discover_client_ops *ops, void *cb_arg)