]> git.ozlabs.org Git - petitboot/blob - discover/discover-server.c
utils/pb-udhcpc: remove bashism when reading mac address
[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         struct config *config;
218         int rc;
219
220         message = pb_protocol_read_message(client, client->fd);
221
222         if (!message) {
223                 talloc_free(client);
224                 return 0;
225         }
226
227
228         switch (message->action) {
229         case PB_PROTOCOL_ACTION_BOOT:
230                 boot_command = talloc(client, struct boot_command);
231
232                 rc = pb_protocol_deserialise_boot_command(boot_command,
233                                 message);
234                 if (rc) {
235                         pb_log("%s: no boot command?", __func__);
236                         return 0;
237                 }
238
239                 device_handler_boot(client->server->device_handler,
240                                 boot_command);
241                 break;
242
243         case PB_PROTOCOL_ACTION_CANCEL_DEFAULT:
244                 device_handler_cancel_default(client->server->device_handler);
245                 break;
246
247         case PB_PROTOCOL_ACTION_CONFIG:
248                 config = talloc_zero(client, struct config);
249
250                 rc = pb_protocol_deserialise_config(config, message);
251                 if (rc) {
252                         pb_log("%s: no config?", __func__);
253                         return 0;
254                 }
255
256                 device_handler_update_config(client->server->device_handler,
257                                 config);
258                 break;
259
260         default:
261                 pb_log("%s: invalid action %d\n", __func__, message->action);
262                 return 0;
263         }
264
265
266         return 0;
267 }
268
269 static int discover_server_process_connection(void *arg)
270 {
271         struct discover_server *server = arg;
272         int fd, rc, i, n_devices;
273         struct client *client;
274
275         /* accept the incoming connection */
276         fd = accept(server->socket, NULL, 0);
277         if (fd < 0) {
278                 pb_log("accept: %s\n", strerror(errno));
279                 return 0;
280         }
281
282         /* add to our list of clients */
283         client = talloc_zero(server, struct client);
284         list_add(&server->clients, &client->list);
285
286         talloc_set_destructor(client, client_destructor);
287
288         client->fd = fd;
289         client->server = server;
290         client->waiter = waiter_register_io(server->waitset, client->fd,
291                                 WAIT_IN, discover_server_process_message,
292                                 client);
293
294         /* send sysinfo to client */
295         rc = write_system_info_message(server, client, system_info_get());
296         if (rc)
297                 return 0;
298
299         /* send config to client */
300         rc = write_config_message(server, client, config_get());
301         if (rc)
302                 return 0;
303
304         /* send existing devices to client */
305         n_devices = device_handler_get_device_count(server->device_handler);
306         for (i = 0; i < n_devices; i++) {
307                 const struct discover_boot_option *opt;
308                 const struct discover_device *device;
309
310                 device = device_handler_get_device(server->device_handler, i);
311                 rc = write_device_add_message(server, client, device->device);
312                 if (rc)
313                         return 0;
314
315                 list_for_each_entry(&device->boot_options, opt, list) {
316                         rc = write_boot_option_add_message(server, client,
317                                         opt->option);
318                         if (rc)
319                                 return 0;
320                 }
321         }
322
323         return 0;
324 }
325
326 void discover_server_notify_device_add(struct discover_server *server,
327                 struct device *device)
328 {
329         struct client *client;
330
331         list_for_each_entry(&server->clients, client, list)
332                 write_device_add_message(server, client, device);
333
334 }
335
336 void discover_server_notify_boot_option_add(struct discover_server *server,
337                 struct boot_option *boot_option)
338 {
339         struct client *client;
340
341         list_for_each_entry(&server->clients, client, list)
342                 write_boot_option_add_message(server, client, boot_option);
343 }
344
345 void discover_server_notify_device_remove(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_remove_message(server, client, device->id);
352
353 }
354
355 void discover_server_notify_boot_status(struct discover_server *server,
356                 struct boot_status *status)
357 {
358         struct client *client;
359
360         list_for_each_entry(&server->clients, client, list)
361                 write_boot_status_message(server, client, status);
362 }
363
364 void discover_server_notify_system_info(struct discover_server *server,
365                 const struct system_info *sysinfo)
366 {
367         struct client *client;
368
369         list_for_each_entry(&server->clients, client, list)
370                 write_system_info_message(server, client, sysinfo);
371 }
372
373 void discover_server_notify_config(struct discover_server *server,
374                 const struct config *config)
375 {
376         struct client *client;
377
378         list_for_each_entry(&server->clients, client, list)
379                 write_config_message(server, client, config);
380 }
381
382 void discover_server_set_device_source(struct discover_server *server,
383                 struct device_handler *handler)
384 {
385         server->device_handler = handler;
386 }
387
388 struct discover_server *discover_server_init(struct waitset *waitset)
389 {
390         struct discover_server *server;
391         struct sockaddr_un addr;
392
393         server = talloc(NULL, struct discover_server);
394         if (!server)
395                 return NULL;
396
397         server->waiter = NULL;
398         server->waitset = waitset;
399         list_init(&server->clients);
400
401         unlink(PB_SOCKET_PATH);
402
403         server->socket = socket(AF_UNIX, SOCK_STREAM, 0);
404         if (server->socket < 0) {
405                 pb_log("error creating server socket: %s\n", strerror(errno));
406                 goto out_err;
407         }
408
409         talloc_set_destructor(server, server_destructor);
410
411         addr.sun_family = AF_UNIX;
412         strcpy(addr.sun_path, PB_SOCKET_PATH);
413
414         if (bind(server->socket, (struct sockaddr *)&addr, sizeof(addr))) {
415                 pb_log("error binding server socket: %s\n", strerror(errno));
416                 goto out_err;
417         }
418
419         if (listen(server->socket, 8)) {
420                 pb_log("server socket listen: %s\n", strerror(errno));
421                 goto out_err;
422         }
423
424         server->waiter = waiter_register_io(server->waitset, server->socket,
425                         WAIT_IN, discover_server_process_connection, server);
426
427         return server;
428
429 out_err:
430         talloc_free(server);
431         return NULL;
432 }
433
434 void discover_server_destroy(struct discover_server *server)
435 {
436         talloc_free(server);
437 }
438