]> git.ozlabs.org Git - petitboot/blob - discover/discover-server.c
bd631f61bf86eac57c76dbfb8dfcb43476ad741a
[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 "sysinfo.h"
24
25 struct discover_server {
26         int socket;
27         struct waitset *waitset;
28         struct waiter *waiter;
29         struct list clients;
30         struct device_handler *device_handler;
31 };
32
33 struct client {
34         struct discover_server *server;
35         struct list_item list;
36         struct waiter *waiter;
37         int fd;
38         bool remote_closed;
39 };
40
41
42 static int server_destructor(void *arg)
43 {
44         struct discover_server *server = arg;
45
46         if (server->waiter)
47                 waiter_remove(server->waiter);
48
49         if (server->socket >= 0)
50                 close(server->socket);
51
52         return 0;
53 }
54
55 static int client_destructor(void *arg)
56 {
57         struct client *client = arg;
58
59         if (client->fd >= 0)
60                 close(client->fd);
61
62         if (client->waiter)
63                 waiter_remove(client->waiter);
64
65         list_remove(&client->list);
66
67         return 0;
68
69 }
70
71 static void print_clients(struct discover_server *server)
72         __attribute__((unused));
73
74 static void print_clients(struct discover_server *server)
75 {
76         struct client *client;
77
78         pb_debug("current clients [%p,%p,%p]:\n",
79                         &server->clients.head,
80                         server->clients.head.prev,
81                         server->clients.head.next);
82         list_for_each_entry(&server->clients, client, list)
83                 pb_debug("\t[%p,%p,%p] client: %d\n", &client->list,
84                                 client->list.prev, client->list.next,
85                                 client->fd);
86 }
87
88 static int client_write_message(
89                 struct discover_server *server __attribute__((unused)),
90                 struct client *client, struct pb_protocol_message *message)
91 {
92         int rc;
93
94         if (client->remote_closed)
95                 return -1;
96
97         rc = pb_protocol_write_message(client->fd, message);
98         if (rc)
99                 client->remote_closed = true;
100
101         return rc;
102 }
103
104 static int write_device_add_message(struct discover_server *server,
105                 struct client *client, const struct device *dev)
106 {
107         struct pb_protocol_message *message;
108         int len;
109
110         len = pb_protocol_device_len(dev);
111
112         message = pb_protocol_create_message(client,
113                         PB_PROTOCOL_ACTION_DEVICE_ADD, len);
114         if (!message)
115                 return -1;
116
117         pb_protocol_serialise_device(dev, message->payload, len);
118
119         return client_write_message(server, client, message);
120 }
121
122 static int write_boot_option_add_message(struct discover_server *server,
123                 struct client *client, const struct boot_option *opt)
124 {
125         struct pb_protocol_message *message;
126         int len;
127
128         len = pb_protocol_boot_option_len(opt);
129
130         message = pb_protocol_create_message(client,
131                         PB_PROTOCOL_ACTION_BOOT_OPTION_ADD, len);
132         if (!message)
133                 return -1;
134
135         pb_protocol_serialise_boot_option(opt, message->payload, len);
136
137         return client_write_message(server, client, message);
138 }
139
140 static int write_device_remove_message(struct discover_server *server,
141                 struct client *client, char *dev_id)
142 {
143         struct pb_protocol_message *message;
144         int len;
145
146         len = strlen(dev_id) + sizeof(uint32_t);
147
148         message = pb_protocol_create_message(client,
149                         PB_PROTOCOL_ACTION_DEVICE_REMOVE, len);
150         if (!message)
151                 return -1;
152
153         pb_protocol_serialise_string(message->payload, dev_id);
154
155         return client_write_message(server, client, message);
156 }
157
158 static int write_boot_status_message(struct discover_server *server,
159                 struct client *client, const struct boot_status *status)
160 {
161         struct pb_protocol_message *message;
162         int len;
163
164         len = pb_protocol_boot_status_len(status);
165
166         message = pb_protocol_create_message(client,
167                         PB_PROTOCOL_ACTION_STATUS, len);
168         if (!message)
169                 return -1;
170
171         pb_protocol_serialise_boot_status(status, message->payload, len);
172
173         return client_write_message(server, client, message);
174 }
175
176 static int write_system_info_message(struct discover_server *server,
177                 struct client *client, const struct system_info *sysinfo)
178 {
179         struct pb_protocol_message *message;
180         int len;
181
182         len = pb_protocol_system_info_len(sysinfo);
183
184         message = pb_protocol_create_message(client,
185                         PB_PROTOCOL_ACTION_SYSTEM_INFO, len);
186         if (!message)
187                 return -1;
188
189         pb_protocol_serialise_system_info(sysinfo, message->payload, len);
190
191         return client_write_message(server, client, message);
192 }
193
194 static int write_config_message(struct discover_server *server,
195                 struct client *client, const struct config *config)
196 {
197         struct pb_protocol_message *message;
198         int len;
199
200         len = pb_protocol_config_len(config);
201
202         message = pb_protocol_create_message(client,
203                         PB_PROTOCOL_ACTION_CONFIG, len);
204         if (!message)
205                 return -1;
206
207         pb_protocol_serialise_config(config, message->payload, len);
208
209         return client_write_message(server, client, message);
210 }
211
212 static int discover_server_process_message(void *arg)
213 {
214         struct pb_protocol_message *message;
215         struct boot_command *boot_command;
216         struct client *client = arg;
217         int rc;
218
219         message = pb_protocol_read_message(client, client->fd);
220
221         if (!message) {
222                 talloc_free(client);
223                 return 0;
224         }
225
226
227         switch (message->action) {
228         case PB_PROTOCOL_ACTION_BOOT:
229                 boot_command = talloc(client, struct boot_command);
230
231                 rc = pb_protocol_deserialise_boot_command(boot_command,
232                                 message);
233                 if (rc) {
234                         pb_log("%s: no boot command?", __func__);
235                         return 0;
236                 }
237
238                 device_handler_boot(client->server->device_handler,
239                                 boot_command);
240                 break;
241
242         case PB_PROTOCOL_ACTION_CANCEL_DEFAULT:
243                 device_handler_cancel_default(client->server->device_handler);
244                 break;
245
246         default:
247                 pb_log("%s: invalid action %d\n", __func__, message->action);
248                 return 0;
249         }
250
251
252         return 0;
253 }
254
255 static int discover_server_process_connection(void *arg)
256 {
257         struct discover_server *server = arg;
258         int fd, rc, i, n_devices;
259         struct client *client;
260
261         /* accept the incoming connection */
262         fd = accept(server->socket, NULL, 0);
263         if (fd < 0) {
264                 pb_log("accept: %s\n", strerror(errno));
265                 return 0;
266         }
267
268         /* add to our list of clients */
269         client = talloc_zero(server, struct client);
270         list_add(&server->clients, &client->list);
271
272         talloc_set_destructor(client, client_destructor);
273
274         client->fd = fd;
275         client->server = server;
276         client->waiter = waiter_register_io(server->waitset, client->fd,
277                                 WAIT_IN, discover_server_process_message,
278                                 client);
279
280         /* send sysinfo to client */
281         rc = write_system_info_message(server, client, system_info_get());
282         if (rc)
283                 return 0;
284
285         /* send config to client */
286         rc = write_config_message(server, client, config_get());
287         if (rc)
288                 return 0;
289
290         /* send existing devices to client */
291         n_devices = device_handler_get_device_count(server->device_handler);
292         for (i = 0; i < n_devices; i++) {
293                 const struct discover_boot_option *opt;
294                 const struct discover_device *device;
295
296                 device = device_handler_get_device(server->device_handler, i);
297                 rc = write_device_add_message(server, client, device->device);
298                 if (rc)
299                         return 0;
300
301                 list_for_each_entry(&device->boot_options, opt, list) {
302                         rc = write_boot_option_add_message(server, client,
303                                         opt->option);
304                         if (rc)
305                                 return 0;
306                 }
307         }
308
309         return 0;
310 }
311
312 void discover_server_notify_device_add(struct discover_server *server,
313                 struct device *device)
314 {
315         struct client *client;
316
317         list_for_each_entry(&server->clients, client, list)
318                 write_device_add_message(server, client, device);
319
320 }
321
322 void discover_server_notify_boot_option_add(struct discover_server *server,
323                 struct boot_option *boot_option)
324 {
325         struct client *client;
326
327         list_for_each_entry(&server->clients, client, list)
328                 write_boot_option_add_message(server, client, boot_option);
329 }
330
331 void discover_server_notify_device_remove(struct discover_server *server,
332                 struct device *device)
333 {
334         struct client *client;
335
336         list_for_each_entry(&server->clients, client, list)
337                 write_device_remove_message(server, client, device->id);
338
339 }
340
341 void discover_server_notify_boot_status(struct discover_server *server,
342                 struct boot_status *status)
343 {
344         struct client *client;
345
346         list_for_each_entry(&server->clients, client, list)
347                 write_boot_status_message(server, client, status);
348 }
349
350 void discover_server_notify_system_info(struct discover_server *server,
351                 const struct system_info *sysinfo)
352 {
353         struct client *client;
354
355         list_for_each_entry(&server->clients, client, list)
356                 write_system_info_message(server, client, sysinfo);
357 }
358
359 void discover_server_notify_config(struct discover_server *server,
360                 const struct config *config)
361 {
362         struct client *client;
363
364         list_for_each_entry(&server->clients, client, list)
365                 write_config_message(server, client, config);
366 }
367
368 void discover_server_set_device_source(struct discover_server *server,
369                 struct device_handler *handler)
370 {
371         server->device_handler = handler;
372 }
373
374 struct discover_server *discover_server_init(struct waitset *waitset)
375 {
376         struct discover_server *server;
377         struct sockaddr_un addr;
378
379         server = talloc(NULL, struct discover_server);
380         if (!server)
381                 return NULL;
382
383         server->waiter = NULL;
384         server->waitset = waitset;
385         list_init(&server->clients);
386
387         unlink(PB_SOCKET_PATH);
388
389         server->socket = socket(AF_UNIX, SOCK_STREAM, 0);
390         if (server->socket < 0) {
391                 pb_log("error creating server socket: %s\n", strerror(errno));
392                 goto out_err;
393         }
394
395         talloc_set_destructor(server, server_destructor);
396
397         addr.sun_family = AF_UNIX;
398         strcpy(addr.sun_path, PB_SOCKET_PATH);
399
400         if (bind(server->socket, (struct sockaddr *)&addr, sizeof(addr))) {
401                 pb_log("error binding server socket: %s\n", strerror(errno));
402                 goto out_err;
403         }
404
405         if (listen(server->socket, 8)) {
406                 pb_log("server socket listen: %s\n", strerror(errno));
407                 goto out_err;
408         }
409
410         server->waiter = waiter_register_io(server->waitset, server->socket,
411                         WAIT_IN, discover_server_process_connection, server);
412
413         return server;
414
415 out_err:
416         talloc_free(server);
417         return NULL;
418 }
419
420 void discover_server_destroy(struct discover_server *server)
421 {
422         talloc_free(server);
423 }
424