]> git.ozlabs.org Git - petitboot/blob - discover/discover-server.c
discover: Handle and track plugin_options
[petitboot] / discover / discover-server.c
1
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <stdint.h>
6 #include <errno.h>
7 #include <assert.h>
8
9 #include <sys/socket.h>
10 #include <sys/un.h>
11 #include <asm/byteorder.h>
12
13 #include <pb-config/pb-config.h>
14 #include <talloc/talloc.h>
15 #include <waiter/waiter.h>
16 #include <log/log.h>
17
18 #include "pb-protocol/pb-protocol.h"
19 #include "list/list.h"
20
21 #include "device-handler.h"
22 #include "discover-server.h"
23 #include "platform.h"
24 #include "sysinfo.h"
25
26 struct discover_server {
27         int socket;
28         struct waitset *waitset;
29         struct waiter *waiter;
30         struct list clients;
31         struct list status;
32         struct device_handler *device_handler;
33 };
34
35 struct client {
36         struct discover_server *server;
37         struct list_item list;
38         struct waiter *waiter;
39         int fd;
40         bool remote_closed;
41 };
42
43
44 static int server_destructor(void *arg)
45 {
46         struct discover_server *server = arg;
47
48         if (server->waiter)
49                 waiter_remove(server->waiter);
50
51         if (server->socket >= 0)
52                 close(server->socket);
53
54         return 0;
55 }
56
57 static int client_destructor(void *arg)
58 {
59         struct client *client = arg;
60
61         if (client->fd >= 0)
62                 close(client->fd);
63
64         if (client->waiter)
65                 waiter_remove(client->waiter);
66
67         list_remove(&client->list);
68
69         return 0;
70
71 }
72
73 static void print_clients(struct discover_server *server)
74         __attribute__((unused));
75
76 static void print_clients(struct discover_server *server)
77 {
78         struct client *client;
79
80         pb_debug("current clients [%p,%p,%p]:\n",
81                         &server->clients.head,
82                         server->clients.head.prev,
83                         server->clients.head.next);
84         list_for_each_entry(&server->clients, client, list)
85                 pb_debug("\t[%p,%p,%p] client: %d\n", &client->list,
86                                 client->list.prev, client->list.next,
87                                 client->fd);
88 }
89
90 static int client_write_message(
91                 struct discover_server *server __attribute__((unused)),
92                 struct client *client, struct pb_protocol_message *message)
93 {
94         int rc;
95
96         if (client->remote_closed)
97                 return -1;
98
99         rc = pb_protocol_write_message(client->fd, message);
100         if (rc)
101                 client->remote_closed = true;
102
103         return rc;
104 }
105
106 static int write_device_add_message(struct discover_server *server,
107                 struct client *client, const struct device *dev)
108 {
109         struct pb_protocol_message *message;
110         int len;
111
112         len = pb_protocol_device_len(dev);
113
114         message = pb_protocol_create_message(client,
115                         PB_PROTOCOL_ACTION_DEVICE_ADD, len);
116         if (!message)
117                 return -1;
118
119         pb_protocol_serialise_device(dev, message->payload, len);
120
121         return client_write_message(server, client, message);
122 }
123
124 static int write_boot_option_add_message(struct discover_server *server,
125                 struct client *client, const struct boot_option *opt)
126 {
127         struct pb_protocol_message *message;
128         int len;
129
130         len = pb_protocol_boot_option_len(opt);
131
132         message = pb_protocol_create_message(client,
133                         PB_PROTOCOL_ACTION_BOOT_OPTION_ADD, len);
134         if (!message)
135                 return -1;
136
137         pb_protocol_serialise_boot_option(opt, message->payload, len);
138
139         return client_write_message(server, client, message);
140 }
141
142 static int write_plugin_option_add_message(struct discover_server *server,
143                 struct client *client, const struct plugin_option *opt)
144 {
145         struct pb_protocol_message *message;
146         int len;
147
148         len = pb_protocol_plugin_option_len(opt);
149
150         message = pb_protocol_create_message(client,
151                         PB_PROTOCOL_ACTION_PLUGIN_OPTION_ADD, len);
152         if (!message)
153                 return -1;
154
155         pb_protocol_serialise_plugin_option(opt, message->payload, len);
156
157         return client_write_message(server, client, message);
158 }
159
160 static int write_plugins_remove_message(struct discover_server *server,
161                 struct client *client)
162 {
163         struct pb_protocol_message *message;
164
165         message = pb_protocol_create_message(client,
166                         PB_PROTOCOL_ACTION_PLUGINS_REMOVE, 0);
167         if (!message)
168                 return -1;
169
170         /* No payload so nothing to serialise */
171
172         return client_write_message(server, client, message);
173 }
174
175 static int write_device_remove_message(struct discover_server *server,
176                 struct client *client, char *dev_id)
177 {
178         struct pb_protocol_message *message;
179         int len;
180
181         len = strlen(dev_id) + sizeof(uint32_t);
182
183         message = pb_protocol_create_message(client,
184                         PB_PROTOCOL_ACTION_DEVICE_REMOVE, len);
185         if (!message)
186                 return -1;
187
188         pb_protocol_serialise_string(message->payload, dev_id);
189
190         return client_write_message(server, client, message);
191 }
192
193 static int write_boot_status_message(struct discover_server *server,
194                 struct client *client, const struct status *status)
195 {
196         struct pb_protocol_message *message;
197         int len;
198
199         len = pb_protocol_boot_status_len(status);
200
201         message = pb_protocol_create_message(client,
202                         PB_PROTOCOL_ACTION_STATUS, len);
203         if (!message)
204                 return -1;
205
206         pb_protocol_serialise_boot_status(status, message->payload, len);
207
208         return client_write_message(server, client, message);
209 }
210
211 static int write_system_info_message(struct discover_server *server,
212                 struct client *client, const struct system_info *sysinfo)
213 {
214         struct pb_protocol_message *message;
215         int len;
216
217         len = pb_protocol_system_info_len(sysinfo);
218
219         message = pb_protocol_create_message(client,
220                         PB_PROTOCOL_ACTION_SYSTEM_INFO, len);
221         if (!message)
222                 return -1;
223
224         pb_protocol_serialise_system_info(sysinfo, message->payload, len);
225
226         return client_write_message(server, client, message);
227 }
228
229 static int write_config_message(struct discover_server *server,
230                 struct client *client, const struct config *config)
231 {
232         struct pb_protocol_message *message;
233         int len;
234
235         len = pb_protocol_config_len(config);
236
237         message = pb_protocol_create_message(client,
238                         PB_PROTOCOL_ACTION_CONFIG, len);
239         if (!message)
240                 return -1;
241
242         pb_protocol_serialise_config(config, message->payload, len);
243
244         return client_write_message(server, client, message);
245 }
246
247 static int discover_server_process_message(void *arg)
248 {
249         struct pb_protocol_message *message;
250         struct boot_command *boot_command;
251         struct client *client = arg;
252         struct config *config;
253         char *url;
254         int rc;
255
256         message = pb_protocol_read_message(client, client->fd);
257
258         if (!message) {
259                 talloc_free(client);
260                 return 0;
261         }
262
263
264         switch (message->action) {
265         case PB_PROTOCOL_ACTION_BOOT:
266                 boot_command = talloc(client, struct boot_command);
267
268                 rc = pb_protocol_deserialise_boot_command(boot_command,
269                                 message);
270                 if (rc) {
271                         pb_log("%s: no boot command?", __func__);
272                         return 0;
273                 }
274
275                 device_handler_boot(client->server->device_handler,
276                                 boot_command);
277                 break;
278
279         case PB_PROTOCOL_ACTION_CANCEL_DEFAULT:
280                 device_handler_cancel_default(client->server->device_handler);
281                 break;
282
283         case PB_PROTOCOL_ACTION_REINIT:
284                 device_handler_reinit(client->server->device_handler);
285                 break;
286
287         case PB_PROTOCOL_ACTION_CONFIG:
288                 config = talloc_zero(client, struct config);
289
290                 rc = pb_protocol_deserialise_config(config, message);
291                 if (rc) {
292                         pb_log("%s: no config?", __func__);
293                         return 0;
294                 }
295
296                 device_handler_update_config(client->server->device_handler,
297                                 config);
298                 break;
299
300         case PB_PROTOCOL_ACTION_ADD_URL:
301                 url = pb_protocol_deserialise_string((void *) client, message);
302
303                 device_handler_process_url(client->server->device_handler,
304                                 url, NULL, NULL);
305                 break;
306
307         default:
308                 pb_log("%s: invalid action %d\n", __func__, message->action);
309                 return 0;
310         }
311
312
313         return 0;
314 }
315
316 static int discover_server_process_connection(void *arg)
317 {
318         struct discover_server *server = arg;
319         struct statuslog_entry *entry;
320         int fd, rc, i, n_devices, n_plugins;
321         struct client *client;
322
323         /* accept the incoming connection */
324         fd = accept(server->socket, NULL, NULL);
325         if (fd < 0) {
326                 pb_log("accept: %s\n", strerror(errno));
327                 return 0;
328         }
329
330         /* add to our list of clients */
331         client = talloc_zero(server, struct client);
332         list_add(&server->clients, &client->list);
333
334         talloc_set_destructor(client, client_destructor);
335
336         client->fd = fd;
337         client->server = server;
338         client->waiter = waiter_register_io(server->waitset, client->fd,
339                                 WAIT_IN, discover_server_process_message,
340                                 client);
341
342         /* send sysinfo to client */
343         rc = write_system_info_message(server, client, system_info_get());
344         if (rc)
345                 return 0;
346
347         /* send config to client */
348         rc = write_config_message(server, client, config_get());
349         if (rc)
350                 return 0;
351
352         /* send existing devices to client */
353         n_devices = device_handler_get_device_count(server->device_handler);
354         for (i = 0; i < n_devices; i++) {
355                 const struct discover_boot_option *opt;
356                 const struct discover_device *device;
357
358                 device = device_handler_get_device(server->device_handler, i);
359                 rc = write_device_add_message(server, client, device->device);
360                 if (rc)
361                         return 0;
362
363                 list_for_each_entry(&device->boot_options, opt, list) {
364                         rc = write_boot_option_add_message(server, client,
365                                         opt->option);
366                         if (rc)
367                                 return 0;
368                 }
369         }
370
371         /* send status backlog to client */
372         list_for_each_entry(&server->status, entry, list)
373                 write_boot_status_message(server, client, entry->status);
374
375         /* send installed plugins to client */
376         n_plugins = device_handler_get_plugin_count(server->device_handler);
377         for (i = 0; i < n_plugins; i++) {
378                 const struct plugin_option *plugin;
379
380                 plugin = device_handler_get_plugin(server->device_handler, i);
381                 write_plugin_option_add_message(server, client, plugin);
382         }
383
384         return 0;
385 }
386
387 void discover_server_notify_device_add(struct discover_server *server,
388                 struct device *device)
389 {
390         struct client *client;
391
392         list_for_each_entry(&server->clients, client, list)
393                 write_device_add_message(server, client, device);
394
395 }
396
397 void discover_server_notify_boot_option_add(struct discover_server *server,
398                 struct boot_option *boot_option)
399 {
400         struct client *client;
401
402         list_for_each_entry(&server->clients, client, list)
403                 write_boot_option_add_message(server, client, boot_option);
404 }
405
406 void discover_server_notify_device_remove(struct discover_server *server,
407                 struct device *device)
408 {
409         struct client *client;
410
411         list_for_each_entry(&server->clients, client, list)
412                 write_device_remove_message(server, client, device->id);
413
414 }
415
416 void discover_server_notify_boot_status(struct discover_server *server,
417                 struct status *status)
418 {
419         struct statuslog_entry *entry;
420         struct client *client;
421
422         /* Duplicate the status struct to add to the backlog */
423         entry = talloc(server, struct statuslog_entry);
424         if (!entry) {
425                 pb_log("Failed to allocated saved status!\n");
426         } else {
427                 entry->status = talloc(entry, struct status);
428                 if (entry->status) {
429                         entry->status->type = status->type;
430                         entry->status->message = talloc_strdup(entry->status,
431                                                                status->message);
432                         entry->status->backlog = true;
433                         list_add_tail(&server->status, &entry->list);
434                 } else {
435                         talloc_free(entry);
436                 }
437         }
438
439         list_for_each_entry(&server->clients, client, list)
440                 write_boot_status_message(server, client, status);
441 }
442
443 void discover_server_notify_system_info(struct discover_server *server,
444                 const struct system_info *sysinfo)
445 {
446         struct client *client;
447
448         list_for_each_entry(&server->clients, client, list)
449                 write_system_info_message(server, client, sysinfo);
450 }
451
452 void discover_server_notify_config(struct discover_server *server,
453                 const struct config *config)
454 {
455         struct client *client;
456
457         list_for_each_entry(&server->clients, client, list)
458                 write_config_message(server, client, config);
459 }
460
461 void discover_server_notify_plugin_option_add(struct discover_server *server,
462                 struct plugin_option *opt)
463 {
464         struct client *client;
465
466         list_for_each_entry(&server->clients, client, list)
467                 write_plugin_option_add_message(server, client, opt);
468 }
469
470 void discover_server_notify_plugins_remove(struct discover_server *server)
471 {
472         struct client *client;
473
474         list_for_each_entry(&server->clients, client, list)
475                 write_plugins_remove_message(server, client);
476 }
477
478 void discover_server_set_device_source(struct discover_server *server,
479                 struct device_handler *handler)
480 {
481         server->device_handler = handler;
482 }
483
484 struct discover_server *discover_server_init(struct waitset *waitset)
485 {
486         struct discover_server *server;
487         struct sockaddr_un addr;
488
489         server = talloc(NULL, struct discover_server);
490         if (!server)
491                 return NULL;
492
493         server->waiter = NULL;
494         server->waitset = waitset;
495         list_init(&server->clients);
496         list_init(&server->status);
497
498         unlink(PB_SOCKET_PATH);
499
500         server->socket = socket(AF_UNIX, SOCK_STREAM, 0);
501         if (server->socket < 0) {
502                 pb_log("error creating server socket: %s\n", strerror(errno));
503                 goto out_err;
504         }
505
506         talloc_set_destructor(server, server_destructor);
507
508         addr.sun_family = AF_UNIX;
509         strcpy(addr.sun_path, PB_SOCKET_PATH);
510
511         if (bind(server->socket, (struct sockaddr *)&addr, sizeof(addr))) {
512                 pb_log("error binding server socket: %s\n", strerror(errno));
513                 goto out_err;
514         }
515
516         if (listen(server->socket, 8)) {
517                 pb_log("server socket listen: %s\n", strerror(errno));
518                 goto out_err;
519         }
520
521         server->waiter = waiter_register_io(server->waitset, server->socket,
522                         WAIT_IN, discover_server_process_connection, server);
523
524         return server;
525
526 out_err:
527         talloc_free(server);
528         return NULL;
529 }
530
531 void discover_server_destroy(struct discover_server *server)
532 {
533         talloc_free(server);
534 }
535