]> git.ozlabs.org Git - petitboot/blob - discover/discover-server.c
ui/ncurses: Fixup some missing gettext calls
[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         case PB_PROTOCOL_ACTION_PLUGIN_INSTALL:
308                 url = pb_protocol_deserialise_string((void *) client, message);
309
310                 device_handler_install_plugin(client->server->device_handler,
311                                 url);
312                 break;
313         default:
314                 pb_log("%s: invalid action %d\n", __func__, message->action);
315                 return 0;
316         }
317
318
319         return 0;
320 }
321
322 static int discover_server_process_connection(void *arg)
323 {
324         struct discover_server *server = arg;
325         struct statuslog_entry *entry;
326         int fd, rc, i, n_devices, n_plugins;
327         struct client *client;
328
329         /* accept the incoming connection */
330         fd = accept(server->socket, NULL, NULL);
331         if (fd < 0) {
332                 pb_log("accept: %s\n", strerror(errno));
333                 return 0;
334         }
335
336         /* add to our list of clients */
337         client = talloc_zero(server, struct client);
338         list_add(&server->clients, &client->list);
339
340         talloc_set_destructor(client, client_destructor);
341
342         client->fd = fd;
343         client->server = server;
344         client->waiter = waiter_register_io(server->waitset, client->fd,
345                                 WAIT_IN, discover_server_process_message,
346                                 client);
347
348         /* send sysinfo to client */
349         rc = write_system_info_message(server, client, system_info_get());
350         if (rc)
351                 return 0;
352
353         /* send config to client */
354         rc = write_config_message(server, client, config_get());
355         if (rc)
356                 return 0;
357
358         /* send existing devices to client */
359         n_devices = device_handler_get_device_count(server->device_handler);
360         for (i = 0; i < n_devices; i++) {
361                 const struct discover_boot_option *opt;
362                 const struct discover_device *device;
363
364                 device = device_handler_get_device(server->device_handler, i);
365                 rc = write_device_add_message(server, client, device->device);
366                 if (rc)
367                         return 0;
368
369                 list_for_each_entry(&device->boot_options, opt, list) {
370                         rc = write_boot_option_add_message(server, client,
371                                         opt->option);
372                         if (rc)
373                                 return 0;
374                 }
375         }
376
377         /* send status backlog to client */
378         list_for_each_entry(&server->status, entry, list)
379                 write_boot_status_message(server, client, entry->status);
380
381         /* send installed plugins to client */
382         n_plugins = device_handler_get_plugin_count(server->device_handler);
383         for (i = 0; i < n_plugins; i++) {
384                 const struct plugin_option *plugin;
385
386                 plugin = device_handler_get_plugin(server->device_handler, i);
387                 write_plugin_option_add_message(server, client, plugin);
388         }
389
390         return 0;
391 }
392
393 void discover_server_notify_device_add(struct discover_server *server,
394                 struct device *device)
395 {
396         struct client *client;
397
398         list_for_each_entry(&server->clients, client, list)
399                 write_device_add_message(server, client, device);
400
401 }
402
403 void discover_server_notify_boot_option_add(struct discover_server *server,
404                 struct boot_option *boot_option)
405 {
406         struct client *client;
407
408         list_for_each_entry(&server->clients, client, list)
409                 write_boot_option_add_message(server, client, boot_option);
410 }
411
412 void discover_server_notify_device_remove(struct discover_server *server,
413                 struct device *device)
414 {
415         struct client *client;
416
417         list_for_each_entry(&server->clients, client, list)
418                 write_device_remove_message(server, client, device->id);
419
420 }
421
422 void discover_server_notify_boot_status(struct discover_server *server,
423                 struct status *status)
424 {
425         struct statuslog_entry *entry;
426         struct client *client;
427
428         /* Duplicate the status struct to add to the backlog */
429         entry = talloc(server, struct statuslog_entry);
430         if (!entry) {
431                 pb_log("Failed to allocated saved status!\n");
432         } else {
433                 entry->status = talloc(entry, struct status);
434                 if (entry->status) {
435                         entry->status->type = status->type;
436                         entry->status->message = talloc_strdup(entry->status,
437                                                                status->message);
438                         entry->status->backlog = true;
439                         list_add_tail(&server->status, &entry->list);
440                 } else {
441                         talloc_free(entry);
442                 }
443         }
444
445         list_for_each_entry(&server->clients, client, list)
446                 write_boot_status_message(server, client, status);
447 }
448
449 void discover_server_notify_system_info(struct discover_server *server,
450                 const struct system_info *sysinfo)
451 {
452         struct client *client;
453
454         list_for_each_entry(&server->clients, client, list)
455                 write_system_info_message(server, client, sysinfo);
456 }
457
458 void discover_server_notify_config(struct discover_server *server,
459                 const struct config *config)
460 {
461         struct client *client;
462
463         list_for_each_entry(&server->clients, client, list)
464                 write_config_message(server, client, config);
465 }
466
467 void discover_server_notify_plugin_option_add(struct discover_server *server,
468                 struct plugin_option *opt)
469 {
470         struct client *client;
471
472         list_for_each_entry(&server->clients, client, list)
473                 write_plugin_option_add_message(server, client, opt);
474 }
475
476 void discover_server_notify_plugins_remove(struct discover_server *server)
477 {
478         struct client *client;
479
480         list_for_each_entry(&server->clients, client, list)
481                 write_plugins_remove_message(server, client);
482 }
483
484 void discover_server_set_device_source(struct discover_server *server,
485                 struct device_handler *handler)
486 {
487         server->device_handler = handler;
488 }
489
490 struct discover_server *discover_server_init(struct waitset *waitset)
491 {
492         struct discover_server *server;
493         struct sockaddr_un addr;
494
495         server = talloc(NULL, struct discover_server);
496         if (!server)
497                 return NULL;
498
499         server->waiter = NULL;
500         server->waitset = waitset;
501         list_init(&server->clients);
502         list_init(&server->status);
503
504         unlink(PB_SOCKET_PATH);
505
506         server->socket = socket(AF_UNIX, SOCK_STREAM, 0);
507         if (server->socket < 0) {
508                 pb_log("error creating server socket: %s\n", strerror(errno));
509                 goto out_err;
510         }
511
512         talloc_set_destructor(server, server_destructor);
513
514         addr.sun_family = AF_UNIX;
515         strcpy(addr.sun_path, PB_SOCKET_PATH);
516
517         if (bind(server->socket, (struct sockaddr *)&addr, sizeof(addr))) {
518                 pb_log("error binding server socket: %s\n", strerror(errno));
519                 goto out_err;
520         }
521
522         if (listen(server->socket, 8)) {
523                 pb_log("server socket listen: %s\n", strerror(errno));
524                 goto out_err;
525         }
526
527         server->waiter = waiter_register_io(server->waitset, server->socket,
528                         WAIT_IN, discover_server_process_connection, server);
529
530         return server;
531
532 out_err:
533         talloc_free(server);
534         return NULL;
535 }
536
537 void discover_server_destroy(struct discover_server *server)
538 {
539         talloc_free(server);
540 }
541