ui/common/discover-client: explicit #include <string.h>
[petitboot] / ui / common / discover-client.c
1
2 #include <assert.h>
3 #include <errno.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <string.h>
9
10 #include <sys/socket.h>
11 #include <sys/un.h>
12 #include <asm/byteorder.h>
13
14 #include <talloc/talloc.h>
15 #include <log/log.h>
16
17 #include "discover-client.h"
18 #include "pb-protocol/pb-protocol.h"
19
20 struct discover_client {
21         int fd;
22         struct discover_client_ops ops;
23         int n_devices;
24         struct device **devices;
25 };
26
27 static int discover_client_destructor(void *arg)
28 {
29         struct discover_client *client = arg;
30
31         if (client->fd >= 0)
32                 close(client->fd);
33
34         return 0;
35 }
36
37 void discover_client_destroy(struct discover_client *client)
38 {
39         talloc_free(client);
40 }
41
42 static struct device *find_device(struct discover_client *client,
43                 const char *id)
44 {
45         int i;
46
47         for (i = 0; i < client->n_devices; i++) {
48                 struct device *dev = client->devices[i];
49                 if (!strcmp(dev->id, id))
50                         return dev;
51         }
52
53         return NULL;
54 }
55
56 static void device_add(struct discover_client *client, struct device *device)
57 {
58         client->n_devices++;
59         client->devices = talloc_realloc(client, client->devices,
60                         struct device *, client->n_devices);
61
62         client->devices[client->n_devices - 1] = device;
63         talloc_steal(client, device);
64         list_init(&device->boot_options);
65
66         if (client->ops.device_add)
67                 client->ops.device_add(device, client->ops.cb_arg);
68 }
69
70 static void boot_option_add(struct discover_client *client,
71                 struct boot_option *opt)
72 {
73         struct device *dev;
74
75         dev = find_device(client, opt->device_id);
76
77         /* we require that devices are already present before any boot options
78          * are added */
79         assert(dev);
80
81         talloc_steal(dev, opt);
82         list_add(&dev->boot_options, &opt->list);
83
84         if (client->ops.boot_option_add)
85                 client->ops.boot_option_add(dev, opt, client->ops.cb_arg);
86 }
87
88 static void device_remove(struct discover_client *client, const char *id)
89 {
90         struct device *device = NULL;
91         int i;
92
93         for (i = 0; i < client->n_devices; i++) {
94                 if (!strcmp(client->devices[i]->id, id)) {
95                         device = client->devices[i];
96                         break;
97                 }
98         }
99
100         if (!device)
101                 return;
102
103         /* remove the device from the client's device array */
104         client->n_devices--;
105         memmove(&client->devices[i], &client->devices[i+1],
106                         (client->n_devices - i) * sizeof(client->devices[0]));
107         client->devices = talloc_realloc(client, client->devices,
108                         struct device *, client->n_devices);
109
110         /* notify the UI */
111         client->ops.device_remove(device, client->ops.cb_arg);
112
113         talloc_free(device);
114 }
115
116 static void plugin_option_add(struct discover_client *client,
117                 struct plugin_option *opt)
118 {
119         talloc_steal(client, opt);
120
121         if (client->ops.plugin_option_add)
122                 client->ops.plugin_option_add(opt, client->ops.cb_arg);
123 }
124
125 static void plugins_remove(struct discover_client *client)
126 {
127         if (client->ops.plugins_remove)
128                 client->ops.plugins_remove(client->ops.cb_arg);
129 }
130
131 void discover_client_enumerate(struct discover_client *client)
132 {
133         struct boot_option *opt;
134         struct device *device;
135         int i;
136
137         for (i = 0; i < client->n_devices; i++) {
138                 device = client->devices[i];
139                 if (client->ops.device_add)
140                         client->ops.device_add(device, client->ops.cb_arg);
141
142                 list_for_each_entry(&device->boot_options, opt, list)
143                         if (client->ops.boot_option_add)
144                                 client->ops.boot_option_add(device, opt,
145                                                 client->ops.cb_arg);
146         }
147 }
148
149 static void update_status(struct discover_client *client,
150                 struct status *status)
151 {
152         if (client->ops.update_status)
153                 client->ops.update_status(status, client->ops.cb_arg);
154 }
155
156 static void update_sysinfo(struct discover_client *client,
157                 struct system_info *sysinfo)
158 {
159         if (client->ops.update_sysinfo)
160                 client->ops.update_sysinfo(sysinfo, client->ops.cb_arg);
161 }
162
163 static void update_config(struct discover_client *client,
164                 struct config *config)
165 {
166         if (client->ops.update_config)
167                 client->ops.update_config(config, client->ops.cb_arg);
168 }
169
170 static int discover_client_process(void *arg)
171 {
172         struct discover_client *client = arg;
173         struct pb_protocol_message *message;
174         struct plugin_option *p_opt;
175         struct system_info *sysinfo;
176         struct boot_option *opt;
177         struct status *status;
178         struct config *config;
179         struct device *dev;
180         char *dev_id;
181         void *ctx;
182         int rc;
183
184         /* We use a temporary context for processing one message; persistent
185          * data is re-parented to the client in the callbacks. */
186         ctx = talloc_new(client);
187
188         message = pb_protocol_read_message(ctx, client->fd);
189
190         if (!message)
191                 return -1;
192
193         switch (message->action) {
194         case PB_PROTOCOL_ACTION_DEVICE_ADD:
195                 dev = talloc_zero(ctx, struct device);
196                 list_init(&dev->boot_options);
197
198                 rc = pb_protocol_deserialise_device(dev, message);
199                 if (rc) {
200                         pb_log("%s: no device?\n", __func__);
201                         goto out;
202                 }
203
204                 device_add(client, dev);
205                 break;
206         case PB_PROTOCOL_ACTION_BOOT_OPTION_ADD:
207                 opt = talloc_zero(ctx, struct boot_option);
208
209                 rc = pb_protocol_deserialise_boot_option(opt, message);
210                 if (rc) {
211                         pb_log("%s: no boot_option?\n", __func__);
212                         goto out;
213                 }
214
215                 boot_option_add(client, opt);
216                 break;
217         case PB_PROTOCOL_ACTION_DEVICE_REMOVE:
218                 dev_id = pb_protocol_deserialise_string(ctx, message);
219                 if (!dev_id) {
220                         pb_log("%s: no device id?\n", __func__);
221                         goto out;
222                 }
223                 device_remove(client, dev_id);
224                 break;
225         case PB_PROTOCOL_ACTION_STATUS:
226                 status = talloc_zero(ctx, struct status);
227
228                 rc = pb_protocol_deserialise_boot_status(status, message);
229                 if (rc) {
230                         pb_log("%s: invalid status message?\n", __func__);
231                         goto out;
232                 }
233                 update_status(client, status);
234                 break;
235         case PB_PROTOCOL_ACTION_SYSTEM_INFO:
236                 sysinfo = talloc_zero(ctx, struct system_info);
237
238                 rc = pb_protocol_deserialise_system_info(sysinfo, message);
239                 if (rc) {
240                         pb_log("%s: invalid sysinfo message?\n", __func__);
241                         goto out;
242                 }
243                 update_sysinfo(client, sysinfo);
244                 break;
245         case PB_PROTOCOL_ACTION_CONFIG:
246                 config = talloc_zero(ctx, struct config);
247
248                 rc = pb_protocol_deserialise_config(config, message);
249                 if (rc) {
250                         pb_log("%s: invalid config message?\n", __func__);
251                         goto out;
252                 }
253                 update_config(client, config);
254                 break;
255         case PB_PROTOCOL_ACTION_PLUGIN_OPTION_ADD:
256                 p_opt = talloc_zero(ctx, struct plugin_option);
257
258                 rc = pb_protocol_deserialise_plugin_option(p_opt, message);
259                 if (rc) {
260                         pb_log("%s: no plugin_option?\n", __func__);
261                         goto out;
262                 }
263
264                 plugin_option_add(client, p_opt);
265                 break;
266         case PB_PROTOCOL_ACTION_PLUGINS_REMOVE:
267                 plugins_remove(client);
268                 break;
269         default:
270                 pb_log("%s: unknown action %d\n", __func__, message->action);
271         }
272
273 out:
274         talloc_free(ctx);
275
276         return 0;
277 }
278
279 struct discover_client* discover_client_init(struct waitset *waitset,
280         const struct discover_client_ops *ops, void *cb_arg)
281 {
282         struct discover_client *client;
283         struct sockaddr_un addr;
284
285         client = talloc(NULL, struct discover_client);
286         if (!client)
287                 return NULL;
288
289         memcpy(&client->ops, ops, sizeof(client->ops));
290         client->ops.cb_arg = cb_arg;
291
292         client->fd = socket(AF_UNIX, SOCK_STREAM, 0);
293         if (client->fd < 0) {
294                 pb_log("%s: socket: %s\n", __func__, strerror(errno));
295                 goto out_err;
296         }
297
298         talloc_set_destructor(client, discover_client_destructor);
299
300         client->n_devices = 0;
301         client->devices = NULL;
302
303         addr.sun_family = AF_UNIX;
304         strcpy(addr.sun_path, PB_SOCKET_PATH);
305
306         if (connect(client->fd, (struct sockaddr *)&addr, sizeof(addr))) {
307                 pb_log("%s: connect: %s\n", __func__, strerror(errno));
308                 goto out_err;
309         }
310
311         waiter_register_io(waitset, client->fd, WAIT_IN,
312                         discover_client_process, client);
313
314         return client;
315
316 out_err:
317         talloc_free(client);
318         return NULL;
319 }
320
321 /* accessors for discovered devices */
322 int discover_client_device_count(struct discover_client *client)
323 {
324         return client->n_devices;
325 }
326
327 struct device *discover_client_get_device(struct discover_client *client,
328                 int index)
329 {
330         if (index < 0 || index >= client->n_devices)
331                 return NULL;
332
333         return client->devices[index];
334 }
335
336 static void create_boot_command(struct boot_command *command,
337                 const struct device *device __attribute__((unused)),
338                 const struct boot_option *boot_option,
339                 const struct pb_boot_data *data)
340 {
341         command->option_id = boot_option ? boot_option->id : NULL;
342         command->boot_image_file = data->image;
343         command->initrd_file = data->initrd;
344         command->dtb_file = data->dtb;
345         command->boot_args = data->args;
346         command->args_sig_file = data->args_sig_file;
347         command->console = ttyname(STDIN_FILENO);
348 }
349
350 int discover_client_boot(struct discover_client *client,
351                 const struct device *device,
352                 const struct boot_option *boot_option,
353                 const struct pb_boot_data *data)
354 {
355         struct pb_protocol_message *message;
356         struct boot_command boot_command;
357         int len, rc;
358
359         create_boot_command(&boot_command, device, boot_option, data);
360
361         len = pb_protocol_boot_len(&boot_command);
362
363         message = pb_protocol_create_message(client,
364                         PB_PROTOCOL_ACTION_BOOT, len);
365
366         if (!message)
367                 return -1;
368
369         pb_protocol_serialise_boot_command(&boot_command,
370                         message->payload, len);
371
372         rc = pb_protocol_write_message(client->fd, message);
373
374         return rc;
375 }
376
377 int discover_client_cancel_default(struct discover_client *client)
378 {
379         struct pb_protocol_message *message;
380
381         message = pb_protocol_create_message(client,
382                         PB_PROTOCOL_ACTION_CANCEL_DEFAULT, 0);
383
384         if (!message)
385                 return -1;
386
387         return pb_protocol_write_message(client->fd, message);
388 }
389
390 int discover_client_send_reinit(struct discover_client *client)
391 {
392         struct pb_protocol_message *message;
393
394         message = pb_protocol_create_message(client,
395                         PB_PROTOCOL_ACTION_REINIT, 0);
396
397         if (!message)
398                 return -1;
399
400         return pb_protocol_write_message(client->fd, message);
401 }
402
403 int discover_client_send_config(struct discover_client *client,
404                 struct config *config)
405 {
406         struct pb_protocol_message *message;
407         int len;
408
409         len = pb_protocol_config_len(config);
410
411         message = pb_protocol_create_message(client,
412                                 PB_PROTOCOL_ACTION_CONFIG, len);
413         if (!message)
414                 return -1;
415
416         pb_protocol_serialise_config(config, message->payload, len);
417
418         return pb_protocol_write_message(client->fd, message);
419 }
420
421 int discover_client_send_url(struct discover_client *client,
422                 char *url)
423 {
424         struct pb_protocol_message *message;
425         int len;
426
427         len = pb_protocol_url_len(url);
428
429         message = pb_protocol_create_message(client,
430                                 PB_PROTOCOL_ACTION_ADD_URL, len);
431         if (!message)
432                 return -1;
433
434         pb_protocol_serialise_url(url, message->payload, len);
435
436         return pb_protocol_write_message(client->fd, message);
437 }
438
439 int discover_client_send_plugin_install(struct discover_client *client,
440                 char *file)
441 {
442         struct pb_protocol_message *message;
443         int len;
444
445         len = pb_protocol_url_len(file);
446
447         message = pb_protocol_create_message(client,
448                                 PB_PROTOCOL_ACTION_PLUGIN_INSTALL, len);
449         if (!message)
450                 return -1;
451
452         pb_protocol_serialise_url(file, message->payload, len);
453
454         return pb_protocol_write_message(client->fd, message);
455 }