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