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