Use compressed cursor
[petitboot] / devices.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <sys/un.h>
9 #include <asm/byteorder.h>
10
11 #include <libtwin/twin_png.h>
12 #include "petitboot.h"
13 #include "petitboot-paths.h"
14 #include "devices/message.h"
15
16 #define PBOOT_DEFAULT_ICON      "usbpen.png"
17
18 static const char *default_icon = artwork_pathname(PBOOT_DEFAULT_ICON);
19
20 struct discovery_context {
21         /* nothing at present */
22         int pad;
23 } _ctx;
24
25 struct device_context {
26         struct discovery_context *discovery_ctx;
27         uint8_t action;
28         int device_idx;
29 };
30
31 static twin_pixmap_t *get_icon(const char *filename)
32 {
33         /* todo: cache */
34         twin_pixmap_t *icon;
35
36         if (!filename)
37                 filename = default_icon;
38
39 retry:
40         LOG("loading icon %s ... ", filename);
41         icon = twin_png_to_pixmap(filename, TWIN_ARGB32);
42         LOG("%s\n", icon ? "ok" : "failed");
43
44         if (!icon && filename != default_icon) {
45                 filename = default_icon;
46                 LOG("reverting to default icon %s\n", filename);
47                 goto retry;
48         }
49
50         return icon;
51 }
52
53 #define MAX_LEN 4096
54 static char *read_string(int fd)
55 {
56         int len;
57         uint32_t len_buf;
58         char *str, *pos;
59
60         if (read(fd, &len_buf, sizeof(len_buf)) != sizeof(len_buf)) {
61                 perror("read");
62                 return NULL;
63         }
64
65         len = __be32_to_cpu(len_buf);
66         if (len + 1 > MAX_LEN) {
67                 /* todo: truncate instead */
68                 return NULL;
69         }
70
71         pos = str = malloc(len + 1);
72
73         while (len) {
74                 int rc = read(fd, pos, len);
75                 if (rc <= 0) {
76                         free(str);
77                         LOG("read failed: %s\n", strerror(errno));
78                         return NULL;
79                 }
80                 pos += rc;
81                 len -= rc;
82         }
83         *pos = '\0';
84
85         return str;
86 }
87
88 static int _read_strings(int fd, void *ptr, int n_strings)
89 {
90         char **strings = ptr;
91         int i, ret = TWIN_TRUE;
92
93         for (i = 0; i < n_strings; i++) {
94                 strings[i] = read_string(fd);
95                 if (!strings[i]) {
96                         ret = TWIN_FALSE;
97                         break;
98                 }
99         }
100
101         if (!ret)
102                 while (i-- > 0) {
103                         free(strings[i]);
104                         strings[i] = NULL;
105                 }
106
107         return ret;
108 }
109
110 static void _free_strings(void *ptr, int n_strings)
111 {
112         char **strings = ptr;
113         int i;
114
115         for (i = 0; i < n_strings; i++)
116                 free(strings[i]);
117
118 }
119
120 #define n_strings(x) (sizeof((x)) / sizeof(char *))
121 #define read_strings(f,x) _read_strings((f), &(x), n_strings(x))
122 #define free_strings(x) _free_strings(&(x), n_strings(x))
123
124 static int read_action(int fd, uint8_t *action)
125 {
126         return read(fd, action, sizeof(*action)) != sizeof(*action);
127 }
128
129 static int read_device(int fd, struct device_context *dev_ctx)
130 {
131         /* name, description, icon_file */
132         struct device dev;
133         twin_pixmap_t *icon;
134         int index = -1;
135
136         if (!read_strings(fd, dev))
137                 return TWIN_FALSE;
138
139         LOG("got device: '%s'\n", dev.name);
140
141         icon = get_icon(dev.icon_file);
142
143         if (!icon)
144                 goto out;
145
146         index = dev_ctx->device_idx = pboot_add_device(dev.id, dev.name, icon);
147
148 out:
149         free_strings(dev);
150
151         return index != -1;
152 }
153
154 static int read_option(int fd, struct device_context *dev_ctx)
155 {
156         struct boot_option *opt = malloc(sizeof(*opt));
157         twin_pixmap_t *icon;
158         int index = -1;
159
160         if (!opt)
161                 return TWIN_FALSE;
162
163         if (!read_strings(fd, (*opt)))
164                 return TWIN_FALSE;
165
166         LOG("got option: '%s'\n", opt->name);
167         icon = get_icon(opt->icon_file);
168
169         if (icon)
170                 index = pboot_add_option(dev_ctx->device_idx, opt->name,
171                                          opt->description, icon, opt);
172
173         return index != -1;
174 }
175
176 static twin_bool_t pboot_proc_client_sock(int sock, twin_file_op_t ops,
177                 void *closure)
178 {
179         struct device_context *dev_ctx = closure;
180         uint8_t action;
181
182         if (read_action(sock, &action))
183                 goto out_err;
184
185         if (action == DEV_ACTION_ADD_DEVICE) {
186                 if (!read_device(sock, dev_ctx))
187                         goto out_err;
188
189         } else if (action == DEV_ACTION_ADD_OPTION) {
190                 if (dev_ctx->device_idx == -1) {
191                         LOG("option, but no device has been sent?\n");
192                         goto out_err;
193                 }
194
195                 if (!read_option(sock, dev_ctx))
196                         goto out_err;
197
198         } else if (action == DEV_ACTION_REMOVE_DEVICE) {
199                 char *dev_id = read_string(sock);
200                 if (!dev_id)
201                         goto out_err;
202                 
203                 LOG("remove device %s\n", dev_id);
204                 pboot_remove_device(dev_id);
205
206         } else {
207                 LOG("unsupported action %d\n", action);
208                 goto out_err;
209         }
210
211         return TWIN_TRUE;
212
213 out_err:
214         close(sock);
215         return TWIN_FALSE;
216 }
217
218 static twin_bool_t pboot_proc_server_sock(int sock, twin_file_op_t ops,
219                 void *closure)
220 {
221         int fd;
222         struct discovery_context *disc_ctx = closure;
223         struct device_context *dev_ctx;
224
225         fd = accept(sock, NULL, 0);
226         if (fd < 0) {
227                 LOG("accept failed: %s", strerror(errno));
228                 return TWIN_FALSE;
229         }
230
231         dev_ctx = malloc(sizeof(*dev_ctx));
232         dev_ctx->discovery_ctx = disc_ctx;
233         dev_ctx->device_idx = -1;
234         dev_ctx->action = 0xff;
235
236         twin_set_file(pboot_proc_client_sock, fd, TWIN_READ, dev_ctx);
237
238         return TWIN_TRUE;
239 }
240
241 int pboot_start_device_discovery(int udev_trigger)
242 {
243         int sock;
244         struct sockaddr_un addr;
245
246         unlink(PBOOT_DEVICE_SOCKET);
247
248         sock = socket(PF_UNIX, SOCK_STREAM, 0);
249         if (sock < 0) {
250                 perror("socket");
251                 return TWIN_FALSE;
252         }
253
254         addr.sun_family = AF_UNIX;
255         strcpy(addr.sun_path, PBOOT_DEVICE_SOCKET);
256
257         if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) {
258                 LOG("can't bind to %s: %s", addr.sun_path, strerror(errno));
259                 return TWIN_FALSE;
260         }
261
262         if (listen(sock, 1)) {
263                 LOG("can't listen on socket %s: %s",
264                                 addr.sun_path, strerror(errno));
265                 return TWIN_FALSE;
266         }
267
268         LOG("listening on %s\n", addr.sun_path);
269
270         twin_set_file(pboot_proc_server_sock, sock, TWIN_READ, &_ctx);
271
272         if (udev_trigger) {
273                 int rc = system("udevtrigger");
274                 if (rc)
275                         LOG("udevtrigger failed, rc %d\n", rc);
276         }
277
278         return TWIN_TRUE;
279 }
280
281 void pboot_exec_option(void *data)
282 {
283         struct boot_option *opt = data;
284         char *kexec_opts[10];
285         int nr_opts = 2;
286
287         kexec_opts[0] = "/sbin/kexec";
288         kexec_opts[1] = "-f";
289         if (opt->initrd_file) {
290                 kexec_opts[nr_opts] = malloc(10 + strlen(opt->initrd_file));
291                 sprintf(kexec_opts[nr_opts], "--initrd=%s", opt->initrd_file);
292                 nr_opts++;
293         }
294         if (opt->boot_args)  {
295                 kexec_opts[nr_opts] = malloc(10 + strlen(opt->boot_args));
296                 sprintf(kexec_opts[nr_opts], "--command-line=%s",
297                         opt->boot_args);
298                 nr_opts++;
299         }
300
301         kexec_opts[nr_opts++] = opt->boot_image_file;
302         kexec_opts[nr_opts] = NULL;
303         execv(kexec_opts[0], kexec_opts);
304 }