discover: Don't depend on tftp failure for type detection
[petitboot] / discover / paths.c
1 #define _GNU_SOURCE
2
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include <talloc/talloc.h>
8 #include <system/system.h>
9 #include <process/process.h>
10 #include <url/url.h>
11 #include <log/log.h>
12
13 #include "paths.h"
14
15 #define DEVICE_MOUNT_BASE (LOCAL_STATE_DIR "/petitboot/mnt")
16
17 struct load_url_async_data {
18         load_url_callback url_cb;
19         void *ctx;
20 };
21
22 const char *mount_base(void)
23 {
24         return DEVICE_MOUNT_BASE;
25 }
26
27 char *join_paths(void *alloc_ctx, const char *a, const char *b)
28 {
29         char *full_path;
30
31         full_path = talloc_array(alloc_ctx, char, strlen(a) + strlen(b) + 2);
32
33         strcpy(full_path, a);
34         if (b[0] != '/' && a[strlen(a) - 1] != '/')
35                 strcat(full_path, "/");
36         strcat(full_path, b);
37
38         return full_path;
39 }
40
41
42 static char *local_name(void *ctx)
43 {
44         char *tmp, *ret;
45
46         tmp = tempnam(NULL, "pb-");
47
48         if (!tmp)
49                 return NULL;
50
51         ret = talloc_strdup(ctx, tmp);
52         free(tmp);
53
54         return ret;
55 }
56
57 static void load_url_exit_cb(struct process *process)
58 {
59         struct load_url_async_data *url_data = process->data;
60
61         pb_log("The download client '%s' [pid %d] exited, rc %d\n",
62                         process->path, process->pid, process->exit_status);
63
64         url_data->url_cb(url_data->ctx, process->exit_status);
65
66         process_release(process);
67 }
68
69 /**
70  * pb_load_nfs - Mounts the NFS export and returns the local file path.
71  *
72  * Returns the local file path in a talloc'ed character string on success,
73  * or NULL on error.
74  */
75 static char *load_nfs(void *ctx, struct pb_url *url,
76                 struct load_url_async_data *url_data)
77 {
78         char *local, *opts;
79         int result;
80         struct process *process;
81         const char *argv[] = {
82                         pb_system_apps.mount,
83                         "-t", "nfs",
84                         NULL,
85                         url->host,
86                         url->dir,
87                         NULL,
88                         NULL,
89         };
90
91         local = local_name(ctx);
92         if (!local)
93                 return NULL;
94         argv[6] = local;
95
96         result = pb_mkdir_recursive(local);
97         if (result)
98                 goto fail;
99
100         opts = talloc_strdup(NULL, "ro,nolock,nodiratime");
101         argv[3] = opts;
102
103         if (url->port)
104                 opts = talloc_asprintf_append(opts, ",port=%s", url->port);
105
106         if (url_data) {
107                 process = process_create(ctx);
108
109                 process->path = pb_system_apps.mount;
110                 process->argv = argv;
111                 process->exit_cb = load_url_exit_cb;
112                 process->data = url_data;
113
114                 result = process_run_async(process);
115                 if (result)
116                         process_release(process);
117         } else {
118                 result = process_run_simple_argv(ctx, argv);
119         }
120
121         talloc_free(opts);
122
123         if (result)
124                 goto fail;
125
126         local = talloc_asprintf_append(local,  "/%s", url->path);
127         pb_log("%s: local '%s'\n", __func__, local);
128
129         return local;
130
131 fail:
132         pb_rmdir_recursive("/", local);
133         talloc_free(local);
134         return NULL;
135 }
136
137 /**
138  * pb_load_sftp - Loads a remote file via sftp and returns the local file path.
139  *
140  * Returns the local file path in a talloc'ed character string on success,
141  * or NULL on error.
142  */
143 static char *load_sftp(void *ctx, struct pb_url *url,
144                 struct load_url_async_data *url_data)
145 {
146         char *host_path, *local;
147         int result;
148         struct process *process;
149         const char *argv[] = {
150                         pb_system_apps.sftp,
151                         NULL,
152                         NULL,
153                         NULL,
154         };
155
156         local = local_name(ctx);
157         if (!local)
158                 return NULL;
159         argv[2] = local;
160
161         host_path = talloc_asprintf(local, "%s:%s", url->host, url->path);
162         argv[1] = host_path;
163
164         if (url_data) {
165                 process = process_create(ctx);
166
167                 process->path = pb_system_apps.sftp;
168                 process->argv = argv;
169                 process->exit_cb = load_url_exit_cb;
170                 process->data = url_data;
171
172                 result = process_run_async(process);
173                 if (result)
174                         process_release(process);
175         } else {
176                 result = process_run_simple_argv(ctx, argv);
177         }
178
179         if (result)
180                 goto fail;
181
182         return local;
183
184 fail:
185         talloc_free(local);
186         return NULL;
187 }
188
189 static enum tftp_type check_tftp_type(void *ctx)
190 {
191         const char *argv[] = { pb_system_apps.tftp, "-V", NULL };
192         struct process *process;
193         enum tftp_type type;
194
195         process = process_create(ctx);
196         process->path = pb_system_apps.tftp;
197         process->argv = argv;
198         process->keep_stdout = true;
199         process_run_sync(process);
200
201         if (!process->stdout_buf || process->stdout_len == 0) {
202                 pb_log("Can't check TFTP client type!\n");
203                 type = TFTP_TYPE_BROKEN;
204
205         } else if (memmem(process->stdout_buf, process->stdout_len,
206                                 "tftp-hpa", strlen("tftp-hpa"))) {
207                 pb_debug("Found TFTP client type: tftp-hpa\n");
208                 type = TFTP_TYPE_HPA;
209
210         } else if (memmem(process->stdout_buf, process->stdout_len,
211                                 "BusyBox", strlen("BusyBox"))) {
212                 pb_debug("Found TFTP client type: BusyBox tftp\n");
213                 type = TFTP_TYPE_BUSYBOX;
214
215         } else {
216                 pb_log("Unknown TFTP client type!\n");
217                 type = TFTP_TYPE_BROKEN;
218         }
219
220         process_release(process);
221         return type;
222 }
223
224 /**
225  * pb_load_tftp - Loads a remote file via tftp and returns the local file path.
226  *
227  * Returns the local file path in a talloc'ed character string on success,
228  * or NULL on error.
229  */
230
231 static char *load_tftp(void *ctx, struct pb_url *url,
232                 struct load_url_async_data *url_data)
233 {
234         int result;
235         const char *argv[10];
236         const char **p;
237         char *local;
238         struct process *process;
239
240         if (tftp_type == TFTP_TYPE_UNKNOWN)
241                 tftp_type = check_tftp_type(ctx);
242
243         if (tftp_type == TFTP_TYPE_BROKEN)
244                 return NULL;
245
246         local = local_name(ctx);
247         if (!local)
248                 return NULL;
249
250         if (tftp_type == TFTP_TYPE_BUSYBOX) {
251                 /* first try busybox tftp args */
252
253                 p = argv;
254                 *p++ = pb_system_apps.tftp;     /* 1 */
255                 *p++ = "-g";                    /* 2 */
256                 *p++ = "-l";                    /* 3 */
257                 *p++ = local;                   /* 4 */
258                 *p++ = "-r";                    /* 5 */
259                 *p++ = url->path;               /* 6 */
260                 *p++ = url->host;               /* 7 */
261                 if (url->port)
262                         *p++ = url->port;       /* 8 */
263                 *p++ = NULL;                    /* 9 */
264         } else {
265                 p = argv;
266                 *p++ = pb_system_apps.tftp;     /* 1 */
267                 *p++ = "-m";                    /* 2 */
268                 *p++ = "binary";                /* 3 */
269                 *p++ = url->host;               /* 4 */
270                 if (url->port)
271                         *p++ = url->port;       /* 5 */
272                 *p++ = "-c";                    /* 6 */
273                 *p++ = "get";                   /* 7 */
274                 *p++ = url->path;               /* 8 */
275                 *p++ = local;                   /* 9 */
276                 *p++ = NULL;                    /* 10 */
277         }
278
279         if (url_data) {
280                 process = process_create(ctx);
281
282                 process->path = pb_system_apps.tftp;
283                 process->argv = argv;
284                 process->exit_cb = load_url_exit_cb;
285                 process->data = url_data;
286
287                 result = process_run_async(process);
288         } else {
289                 result = process_run_simple_argv(ctx, argv);
290         }
291
292         if (!result)
293                 return local;
294
295         talloc_free(local);
296         return NULL;
297 }
298
299 enum wget_flags {
300         wget_empty = 0,
301         wget_no_check_certificate = 1,
302 };
303
304 /**
305  * pb_load_wget - Loads a remote file via wget and returns the local file path.
306  *
307  * Returns the local file path in a talloc'ed character string on success,
308  * or NULL on error.
309  */
310
311 static char *load_wget(void *ctx, struct pb_url *url, enum wget_flags flags,
312                 struct load_url_async_data *url_data)
313 {
314         int result;
315         const char *argv[7];
316         const char **p;
317         char *local;
318         struct process *process;
319
320         local = local_name(ctx);
321
322         if (!local)
323                 return NULL;
324
325         p = argv;
326         *p++ = pb_system_apps.wget;                     /* 1 */
327 #if !defined(DEBUG)
328         *p++ = "--quiet";                               /* 2 */
329 #endif
330         *p++ = "-O";                                    /* 3 */
331         *p++ = local;                                   /* 4 */
332         *p++ = url->full;                               /* 5 */
333         if (flags & wget_no_check_certificate)
334                 *p++ = "--no-check-certificate";        /* 6 */
335         *p++ = NULL;                                    /* 7 */
336
337         if (url_data) {
338                 process = process_create(ctx);
339
340                 process->path = pb_system_apps.wget;
341                 process->argv = argv;
342                 process->exit_cb = load_url_exit_cb;
343                 process->data = url_data;
344
345                 result = process_run_async(process);
346                 if (result)
347                         process_release(process);
348         } else {
349                 result = process_run_simple_argv(ctx, argv);
350         }
351
352         if (result)
353                 goto fail;
354
355         return local;
356
357 fail:
358         talloc_free(local);
359         return NULL;
360 }
361
362 /**
363  * load_url - Loads a (possibly) remote URL and returns the local file
364  * path.
365  * @ctx: The talloc context to associate with the returned string.
366  * @url: The remote file URL.
367  * @tempfile: An optional variable pointer to be set when a temporary local
368  *  file is created.
369  * @url_cb: An optional callback pointer if the caller wants to load url
370  *  asynchronously.
371  *
372  * Returns the local file path in a talloc'ed character string on success,
373  * or NULL on error.
374  */
375
376 char *load_url_async(void *ctx, struct pb_url *url, unsigned int *tempfile,
377                 load_url_callback url_cb)
378 {
379         char *local;
380         int tmp = 0;
381         struct load_url_async_data *url_data;
382
383         if (!url)
384                 return NULL;
385
386         url_data = NULL;
387
388         if (url_cb) {
389                 url_data = talloc_zero(ctx, struct load_url_async_data);
390                 url_data->url_cb = url_cb;
391                 url_data->ctx = ctx;
392         }
393
394         switch (url->scheme) {
395         case pb_url_ftp:
396         case pb_url_http:
397                 local = load_wget(ctx, url, 0, url_data);
398                 tmp = !!local;
399                 break;
400         case pb_url_https:
401                 local = load_wget(ctx, url, wget_no_check_certificate,
402                                 url_data);
403                 tmp = !!local;
404                 break;
405         case pb_url_nfs:
406                 local = load_nfs(ctx, url, url_data);
407                 tmp = !!local;
408                 break;
409         case pb_url_sftp:
410                 local = load_sftp(ctx, url, url_data);
411                 tmp = !!local;
412                 break;
413         case pb_url_tftp:
414                 local = load_tftp(ctx, url, url_data);
415                 tmp = !!local;
416                 break;
417         default:
418                 local = talloc_strdup(ctx, url->full);
419                 tmp = 0;
420                 break;
421         }
422
423         if (tempfile)
424                 *tempfile = tmp;
425
426         return local;
427 }
428
429 char *load_url(void *ctx, struct pb_url *url, unsigned int *tempfile)
430 {
431         return load_url_async(ctx, url, tempfile, NULL);
432 }