]> git.ozlabs.org Git - petitboot/blob - discover/discover-server.c
lib/efi: Cleanup read/write routines
[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 autoboot_option *autoboot_opt;
251         struct pb_protocol_message *message;
252         struct boot_command *boot_command;
253         struct client *client = arg;
254         struct config *config;
255         char *url;
256         int rc;
257
258         message = pb_protocol_read_message(client, client->fd);
259
260         if (!message) {
261                 talloc_free(client);
262                 return 0;
263         }
264
265
266         switch (message->action) {
267         case PB_PROTOCOL_ACTION_BOOT:
268                 boot_command = talloc(client, struct boot_command);
269
270                 rc = pb_protocol_deserialise_boot_command(boot_command,
271                                 message);
272                 if (rc) {
273                         pb_log_fn("no boot command?\n");
274                         return 0;
275                 }
276
277                 device_handler_boot(client->server->device_handler,
278                                 boot_command);
279                 break;
280
281         case PB_PROTOCOL_ACTION_CANCEL_DEFAULT:
282                 device_handler_cancel_default(client->server->device_handler);
283                 break;
284
285         case PB_PROTOCOL_ACTION_REINIT:
286                 device_handler_reinit(client->server->device_handler);
287                 break;
288
289         case PB_PROTOCOL_ACTION_CONFIG:
290                 config = talloc_zero(client, struct config);
291
292                 rc = pb_protocol_deserialise_config(config, message);
293                 if (rc) {
294                         pb_log_fn("no config?\n");
295                         return 0;
296                 }
297
298                 device_handler_update_config(client->server->device_handler,
299                                 config);
300                 break;
301
302         case PB_PROTOCOL_ACTION_ADD_URL:
303                 url = pb_protocol_deserialise_string((void *) client, message);
304
305                 device_handler_process_url(client->server->device_handler,
306                                 url, NULL, NULL);
307                 break;
308
309         case PB_PROTOCOL_ACTION_PLUGIN_INSTALL:
310                 url = pb_protocol_deserialise_string((void *) client, message);
311
312                 device_handler_install_plugin(client->server->device_handler,
313                                 url);
314                 break;
315
316         case PB_PROTOCOL_ACTION_TEMP_AUTOBOOT:
317                 autoboot_opt = talloc_zero(client, struct autoboot_option);
318                 rc = pb_protocol_deserialise_temp_autoboot(autoboot_opt,
319                                 message);
320                 if (rc) {
321                         pb_log("can't parse temporary autoboot message\n");
322                         return 0;
323                 }
324
325                 device_handler_apply_temp_autoboot(
326                                 client->server->device_handler,
327                                 autoboot_opt);
328                 break;
329
330         default:
331                 pb_log_fn("invalid action %d\n", message->action);
332                 return 0;
333         }
334
335
336         return 0;
337 }
338
339 static int discover_server_process_connection(void *arg)
340 {
341         struct discover_server *server = arg;
342         struct statuslog_entry *entry;
343         int fd, rc, i, n_devices, n_plugins;
344         struct client *client;
345
346         /* accept the incoming connection */
347         fd = accept(server->socket, NULL, NULL);
348         if (fd < 0) {
349                 pb_log("accept: %s\n", strerror(errno));
350                 return 0;
351         }
352
353         /* add to our list of clients */
354         client = talloc_zero(server, struct client);
355         list_add(&server->clients, &client->list);
356
357         talloc_set_destructor(client, client_destructor);
358
359         client->fd = fd;
360         client->server = server;
361         client->waiter = waiter_register_io(server->waitset, client->fd,
362                                 WAIT_IN, discover_server_process_message,
363                                 client);
364
365         /* send sysinfo to client */
366         rc = write_system_info_message(server, client, system_info_get());
367         if (rc)
368                 return 0;
369
370         /* send config to client */
371         rc = write_config_message(server, client, config_get());
372         if (rc)
373                 return 0;
374
375         /* send existing devices to client */
376         n_devices = device_handler_get_device_count(server->device_handler);
377         for (i = 0; i < n_devices; i++) {
378                 const struct discover_boot_option *opt;
379                 const struct discover_device *device;
380
381                 device = device_handler_get_device(server->device_handler, i);
382                 rc = write_device_add_message(server, client, device->device);
383                 if (rc)
384                         return 0;
385
386                 list_for_each_entry(&device->boot_options, opt, list) {
387                         rc = write_boot_option_add_message(server, client,
388                                         opt->option);
389                         if (rc)
390                                 return 0;
391                 }
392         }
393
394         /* send status backlog to client */
395         list_for_each_entry(&server->status, entry, list)
396                 write_boot_status_message(server, client, entry->status);
397
398         /* send installed plugins to client */
399         n_plugins = device_handler_get_plugin_count(server->device_handler);
400         for (i = 0; i < n_plugins; i++) {
401                 const struct plugin_option *plugin;
402
403                 plugin = device_handler_get_plugin(server->device_handler, i);
404                 write_plugin_option_add_message(server, client, plugin);
405         }
406
407         return 0;
408 }
409
410 void discover_server_notify_device_add(struct discover_server *server,
411                 struct device *device)
412 {
413         struct client *client;
414
415         list_for_each_entry(&server->clients, client, list)
416                 write_device_add_message(server, client, device);
417
418 }
419
420 void discover_server_notify_boot_option_add(struct discover_server *server,
421                 struct boot_option *boot_option)
422 {
423         struct client *client;
424
425         list_for_each_entry(&server->clients, client, list)
426                 write_boot_option_add_message(server, client, boot_option);
427 }
428
429 void discover_server_notify_device_remove(struct discover_server *server,
430                 struct device *device)
431 {
432         struct client *client;
433
434         list_for_each_entry(&server->clients, client, list)
435                 write_device_remove_message(server, client, device->id);
436
437 }
438
439 void discover_server_notify_boot_status(struct discover_server *server,
440                 struct status *status)
441 {
442         struct statuslog_entry *entry;
443         struct client *client;
444
445         /* Duplicate the status struct to add to the backlog */
446         entry = talloc(server, struct statuslog_entry);
447         if (!entry) {
448                 pb_log("Failed to allocated saved status!\n");
449         } else {
450                 entry->status = talloc(entry, struct status);
451                 if (entry->status) {
452                         entry->status->type = status->type;
453                         entry->status->message = talloc_strdup(entry->status,
454                                                                status->message);
455                         entry->status->backlog = true;
456                         list_add_tail(&server->status, &entry->list);
457                 } else {
458                         talloc_free(entry);
459                 }
460         }
461
462         list_for_each_entry(&server->clients, client, list)
463                 write_boot_status_message(server, client, status);
464 }
465
466 void discover_server_notify_system_info(struct discover_server *server,
467                 const struct system_info *sysinfo)
468 {
469         struct client *client;
470
471         list_for_each_entry(&server->clients, client, list)
472                 write_system_info_message(server, client, sysinfo);
473 }
474
475 void discover_server_notify_config(struct discover_server *server,
476                 const struct config *config)
477 {
478         struct client *client;
479
480         list_for_each_entry(&server->clients, client, list)
481                 write_config_message(server, client, config);
482 }
483
484 void discover_server_notify_plugin_option_add(struct discover_server *server,
485                 struct plugin_option *opt)
486 {
487         struct client *client;
488
489         list_for_each_entry(&server->clients, client, list)
490                 write_plugin_option_add_message(server, client, opt);
491 }
492
493 void discover_server_notify_plugins_remove(struct discover_server *server)
494 {
495         struct client *client;
496
497         list_for_each_entry(&server->clients, client, list)
498                 write_plugins_remove_message(server, client);
499 }
500
501 void discover_server_set_device_source(struct discover_server *server,
502                 struct device_handler *handler)
503 {
504         server->device_handler = handler;
505 }
506
507 struct discover_server *discover_server_init(struct waitset *waitset)
508 {
509         struct discover_server *server;
510         struct sockaddr_un addr;
511
512         server = talloc(NULL, struct discover_server);
513         if (!server)
514                 return NULL;
515
516         server->waiter = NULL;
517         server->waitset = waitset;
518         list_init(&server->clients);
519         list_init(&server->status);
520
521         unlink(PB_SOCKET_PATH);
522
523         server->socket = socket(AF_UNIX, SOCK_STREAM, 0);
524         if (server->socket < 0) {
525                 pb_log("error creating server socket: %s\n", strerror(errno));
526                 goto out_err;
527         }
528
529         talloc_set_destructor(server, server_destructor);
530
531         addr.sun_family = AF_UNIX;
532         strcpy(addr.sun_path, PB_SOCKET_PATH);
533
534         if (bind(server->socket, (struct sockaddr *)&addr, sizeof(addr))) {
535                 pb_log("error binding server socket: %s\n", strerror(errno));
536                 goto out_err;
537         }
538
539         if (listen(server->socket, 8)) {
540                 pb_log("server socket listen: %s\n", strerror(errno));
541                 goto out_err;
542         }
543
544         server->waiter = waiter_register_io(server->waitset, server->socket,
545                         WAIT_IN, discover_server_process_connection, server);
546
547         return server;
548
549 out_err:
550         talloc_free(server);
551         return NULL;
552 }
553
554 void discover_server_destroy(struct discover_server *server)
555 {
556         talloc_free(server);
557 }
558