discover: Maintain a backlog of status updates
[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_device_remove_message(struct discover_server *server,
143                 struct client *client, char *dev_id)
144 {
145         struct pb_protocol_message *message;
146         int len;
147
148         len = strlen(dev_id) + sizeof(uint32_t);
149
150         message = pb_protocol_create_message(client,
151                         PB_PROTOCOL_ACTION_DEVICE_REMOVE, len);
152         if (!message)
153                 return -1;
154
155         pb_protocol_serialise_string(message->payload, dev_id);
156
157         return client_write_message(server, client, message);
158 }
159
160 static int write_boot_status_message(struct discover_server *server,
161                 struct client *client, const struct status *status)
162 {
163         struct pb_protocol_message *message;
164         int len;
165
166         len = pb_protocol_boot_status_len(status);
167
168         message = pb_protocol_create_message(client,
169                         PB_PROTOCOL_ACTION_STATUS, len);
170         if (!message)
171                 return -1;
172
173         pb_protocol_serialise_boot_status(status, message->payload, len);
174
175         return client_write_message(server, client, message);
176 }
177
178 static int write_system_info_message(struct discover_server *server,
179                 struct client *client, const struct system_info *sysinfo)
180 {
181         struct pb_protocol_message *message;
182         int len;
183
184         len = pb_protocol_system_info_len(sysinfo);
185
186         message = pb_protocol_create_message(client,
187                         PB_PROTOCOL_ACTION_SYSTEM_INFO, len);
188         if (!message)
189                 return -1;
190
191         pb_protocol_serialise_system_info(sysinfo, message->payload, len);
192
193         return client_write_message(server, client, message);
194 }
195
196 static int write_config_message(struct discover_server *server,
197                 struct client *client, const struct config *config)
198 {
199         struct pb_protocol_message *message;
200         int len;
201
202         len = pb_protocol_config_len(config);
203
204         message = pb_protocol_create_message(client,
205                         PB_PROTOCOL_ACTION_CONFIG, len);
206         if (!message)
207                 return -1;
208
209         pb_protocol_serialise_config(config, message->payload, len);
210
211         return client_write_message(server, client, message);
212 }
213
214 static int discover_server_process_message(void *arg)
215 {
216         struct pb_protocol_message *message;
217         struct boot_command *boot_command;
218         struct client *client = arg;
219         struct config *config;
220         char *url;
221         int rc;
222
223         message = pb_protocol_read_message(client, client->fd);
224
225         if (!message) {
226                 talloc_free(client);
227                 return 0;
228         }
229
230
231         switch (message->action) {
232         case PB_PROTOCOL_ACTION_BOOT:
233                 boot_command = talloc(client, struct boot_command);
234
235                 rc = pb_protocol_deserialise_boot_command(boot_command,
236                                 message);
237                 if (rc) {
238                         pb_log("%s: no boot command?", __func__);
239                         return 0;
240                 }
241
242                 device_handler_boot(client->server->device_handler,
243                                 boot_command);
244                 break;
245
246         case PB_PROTOCOL_ACTION_CANCEL_DEFAULT:
247                 device_handler_cancel_default(client->server->device_handler);
248                 break;
249
250         case PB_PROTOCOL_ACTION_REINIT:
251                 device_handler_reinit(client->server->device_handler);
252                 break;
253
254         case PB_PROTOCOL_ACTION_CONFIG:
255                 config = talloc_zero(client, struct config);
256
257                 rc = pb_protocol_deserialise_config(config, message);
258                 if (rc) {
259                         pb_log("%s: no config?", __func__);
260                         return 0;
261                 }
262
263                 device_handler_update_config(client->server->device_handler,
264                                 config);
265                 break;
266
267         case PB_PROTOCOL_ACTION_ADD_URL:
268                 url = pb_protocol_deserialise_string((void *) client, message);
269
270                 device_handler_process_url(client->server->device_handler,
271                                 url, NULL, NULL);
272                 break;
273
274         default:
275                 pb_log("%s: invalid action %d\n", __func__, message->action);
276                 return 0;
277         }
278
279
280         return 0;
281 }
282
283 static int discover_server_process_connection(void *arg)
284 {
285         struct discover_server *server = arg;
286         struct statuslog_entry *entry;
287         int fd, rc, i, n_devices;
288         struct client *client;
289
290         /* accept the incoming connection */
291         fd = accept(server->socket, NULL, NULL);
292         if (fd < 0) {
293                 pb_log("accept: %s\n", strerror(errno));
294                 return 0;
295         }
296
297         /* add to our list of clients */
298         client = talloc_zero(server, struct client);
299         list_add(&server->clients, &client->list);
300
301         talloc_set_destructor(client, client_destructor);
302
303         client->fd = fd;
304         client->server = server;
305         client->waiter = waiter_register_io(server->waitset, client->fd,
306                                 WAIT_IN, discover_server_process_message,
307                                 client);
308
309         /* send sysinfo to client */
310         rc = write_system_info_message(server, client, system_info_get());
311         if (rc)
312                 return 0;
313
314         /* send config to client */
315         rc = write_config_message(server, client, config_get());
316         if (rc)
317                 return 0;
318
319         /* send existing devices to client */
320         n_devices = device_handler_get_device_count(server->device_handler);
321         for (i = 0; i < n_devices; i++) {
322                 const struct discover_boot_option *opt;
323                 const struct discover_device *device;
324
325                 device = device_handler_get_device(server->device_handler, i);
326                 rc = write_device_add_message(server, client, device->device);
327                 if (rc)
328                         return 0;
329
330                 list_for_each_entry(&device->boot_options, opt, list) {
331                         rc = write_boot_option_add_message(server, client,
332                                         opt->option);
333                         if (rc)
334                                 return 0;
335                 }
336         }
337
338         /* send status backlog to client */
339         list_for_each_entry(&server->status, entry, list)
340                 write_boot_status_message(server, client, entry->status);
341
342         return 0;
343 }
344
345 void discover_server_notify_device_add(struct discover_server *server,
346                 struct device *device)
347 {
348         struct client *client;
349
350         list_for_each_entry(&server->clients, client, list)
351                 write_device_add_message(server, client, device);
352
353 }
354
355 void discover_server_notify_boot_option_add(struct discover_server *server,
356                 struct boot_option *boot_option)
357 {
358         struct client *client;
359
360         list_for_each_entry(&server->clients, client, list)
361                 write_boot_option_add_message(server, client, boot_option);
362 }
363
364 void discover_server_notify_device_remove(struct discover_server *server,
365                 struct device *device)
366 {
367         struct client *client;
368
369         list_for_each_entry(&server->clients, client, list)
370                 write_device_remove_message(server, client, device->id);
371
372 }
373
374 void discover_server_notify_boot_status(struct discover_server *server,
375                 struct status *status)
376 {
377         struct statuslog_entry *entry;
378         struct client *client;
379
380         /* Duplicate the status struct to add to the backlog */
381         entry = talloc(server, struct statuslog_entry);
382         if (!entry) {
383                 pb_log("Failed to allocated saved status!\n");
384         } else {
385                 entry->status = talloc(entry, struct status);
386                 if (entry->status) {
387                         entry->status->type = status->type;
388                         entry->status->message = talloc_strdup(entry->status,
389                                                                status->message);
390                         entry->status->backlog = true;
391                         list_add_tail(&server->status, &entry->list);
392                 } else {
393                         talloc_free(entry);
394                 }
395         }
396
397         list_for_each_entry(&server->clients, client, list)
398                 write_boot_status_message(server, client, status);
399 }
400
401 void discover_server_notify_system_info(struct discover_server *server,
402                 const struct system_info *sysinfo)
403 {
404         struct client *client;
405
406         list_for_each_entry(&server->clients, client, list)
407                 write_system_info_message(server, client, sysinfo);
408 }
409
410 void discover_server_notify_config(struct discover_server *server,
411                 const struct config *config)
412 {
413         struct client *client;
414
415         list_for_each_entry(&server->clients, client, list)
416                 write_config_message(server, client, config);
417 }
418
419 void discover_server_set_device_source(struct discover_server *server,
420                 struct device_handler *handler)
421 {
422         server->device_handler = handler;
423 }
424
425 struct discover_server *discover_server_init(struct waitset *waitset)
426 {
427         struct discover_server *server;
428         struct sockaddr_un addr;
429
430         server = talloc(NULL, struct discover_server);
431         if (!server)
432                 return NULL;
433
434         server->waiter = NULL;
435         server->waitset = waitset;
436         list_init(&server->clients);
437         list_init(&server->status);
438
439         unlink(PB_SOCKET_PATH);
440
441         server->socket = socket(AF_UNIX, SOCK_STREAM, 0);
442         if (server->socket < 0) {
443                 pb_log("error creating server socket: %s\n", strerror(errno));
444                 goto out_err;
445         }
446
447         talloc_set_destructor(server, server_destructor);
448
449         addr.sun_family = AF_UNIX;
450         strcpy(addr.sun_path, PB_SOCKET_PATH);
451
452         if (bind(server->socket, (struct sockaddr *)&addr, sizeof(addr))) {
453                 pb_log("error binding server socket: %s\n", strerror(errno));
454                 goto out_err;
455         }
456
457         if (listen(server->socket, 8)) {
458                 pb_log("server socket listen: %s\n", strerror(errno));
459                 goto out_err;
460         }
461
462         server->waiter = waiter_register_io(server->waitset, server->socket,
463                         WAIT_IN, discover_server_process_connection, server);
464
465         return server;
466
467 out_err:
468         talloc_free(server);
469         return NULL;
470 }
471
472 void discover_server_destroy(struct discover_server *server)
473 {
474         talloc_free(server);
475 }
476