discover: load_url_async callback should take an int status
[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 /**
190  * pb_load_tftp - Loads a remote file via tftp and returns the local file path.
191  *
192  * Returns the local file path in a talloc'ed character string on success,
193  * or NULL on error.
194  */
195
196 static char *load_tftp(void *ctx, struct pb_url *url,
197                 struct load_url_async_data *url_data)
198 {
199         int result;
200         const char *argv[10];
201         const char **p;
202         char *local;
203         struct process *process;
204
205         local = local_name(ctx);
206
207         if (!local)
208                 return NULL;
209
210         /* first try busybox tftp args */
211
212         p = argv;
213         *p++ = pb_system_apps.tftp;     /* 1 */
214         *p++ = "-g";                    /* 2 */
215         *p++ = "-l";                    /* 3 */
216         *p++ = local;                   /* 4 */
217         *p++ = "-r";                    /* 5 */
218         *p++ = url->path;               /* 6 */
219         *p++ = url->host;               /* 7 */
220         if (url->port)
221                 *p++ = url->port;       /* 8 */
222         *p++ = NULL;                    /* 9 */
223
224         if (url_data) {
225                 process = process_create(ctx);
226
227                 process->path = pb_system_apps.tftp;
228                 process->argv = argv;
229                 process->exit_cb = load_url_exit_cb;
230                 process->data = url_data;
231
232                 result = process_run_async(process);
233         } else {
234                 result = process_run_simple_argv(ctx, argv);
235         }
236
237         if (!result)
238                 return local;
239
240         /* next try tftp-hpa args */
241         p = argv;
242         *p++ = pb_system_apps.tftp;     /* 1 */
243         *p++ = "-m";                    /* 2 */
244         *p++ = "binary";                /* 3 */
245         *p++ = url->host;               /* 4 */
246         if (url->port)
247                 *p++ = url->port;       /* 5 */
248         *p++ = "-c";                    /* 6 */
249         *p++ = "get";                   /* 7 */
250         *p++ = url->path;               /* 8 */
251         *p++ = local;                   /* 9 */
252         *p++ = NULL;                    /* 10 */
253
254         if (url_data) {
255                 process->argv = argv;
256                 result = process_run_async(process);
257                 if (result)
258                         process_release(process);
259         } else {
260                 result = process_run_simple_argv(ctx, argv);
261         }
262
263         if (!result)
264                 return local;
265
266         talloc_free(local);
267         return NULL;
268 }
269
270 enum wget_flags {
271         wget_empty = 0,
272         wget_no_check_certificate = 1,
273 };
274
275 /**
276  * pb_load_wget - Loads a remote file via wget and returns the local file path.
277  *
278  * Returns the local file path in a talloc'ed character string on success,
279  * or NULL on error.
280  */
281
282 static char *load_wget(void *ctx, struct pb_url *url, enum wget_flags flags,
283                 struct load_url_async_data *url_data)
284 {
285         int result;
286         const char *argv[7];
287         const char **p;
288         char *local;
289         struct process *process;
290
291         local = local_name(ctx);
292
293         if (!local)
294                 return NULL;
295
296         p = argv;
297         *p++ = pb_system_apps.wget;                     /* 1 */
298 #if !defined(DEBUG)
299         *p++ = "--quiet";                               /* 2 */
300 #endif
301         *p++ = "-O";                                    /* 3 */
302         *p++ = local;                                   /* 4 */
303         *p++ = url->full;                               /* 5 */
304         if (flags & wget_no_check_certificate)
305                 *p++ = "--no-check-certificate";        /* 6 */
306         *p++ = NULL;                                    /* 7 */
307
308         if (url_data) {
309                 process = process_create(ctx);
310
311                 process->path = pb_system_apps.wget;
312                 process->argv = argv;
313                 process->exit_cb = load_url_exit_cb;
314                 process->data = url_data;
315
316                 result = process_run_async(process);
317                 if (result)
318                         process_release(process);
319         } else {
320                 result = process_run_simple_argv(ctx, argv);
321         }
322
323         if (result)
324                 goto fail;
325
326         return local;
327
328 fail:
329         talloc_free(local);
330         return NULL;
331 }
332
333 /**
334  * load_url - Loads a (possibly) remote URL and returns the local file
335  * path.
336  * @ctx: The talloc context to associate with the returned string.
337  * @url: The remote file URL.
338  * @tempfile: An optional variable pointer to be set when a temporary local
339  *  file is created.
340  * @url_cb: An optional callback pointer if the caller wants to load url
341  *  asynchronously.
342  *
343  * Returns the local file path in a talloc'ed character string on success,
344  * or NULL on error.
345  */
346
347 char *load_url_async(void *ctx, struct pb_url *url, unsigned int *tempfile,
348                 load_url_callback url_cb)
349 {
350         char *local;
351         int tmp = 0;
352         struct load_url_async_data *url_data;
353
354         if (!url)
355                 return NULL;
356
357         url_data = NULL;
358
359         if (url_cb) {
360                 url_data = talloc_zero(ctx, struct load_url_async_data);
361                 url_data->url_cb = url_cb;
362                 url_data->ctx = ctx;
363         }
364
365         switch (url->scheme) {
366         case pb_url_ftp:
367         case pb_url_http:
368                 local = load_wget(ctx, url, 0, url_data);
369                 tmp = !!local;
370                 break;
371         case pb_url_https:
372                 local = load_wget(ctx, url, wget_no_check_certificate,
373                                 url_data);
374                 tmp = !!local;
375                 break;
376         case pb_url_nfs:
377                 local = load_nfs(ctx, url, url_data);
378                 tmp = !!local;
379                 break;
380         case pb_url_sftp:
381                 local = load_sftp(ctx, url, url_data);
382                 tmp = !!local;
383                 break;
384         case pb_url_tftp:
385                 local = load_tftp(ctx, url, url_data);
386                 tmp = !!local;
387                 break;
388         default:
389                 local = talloc_strdup(ctx, url->full);
390                 tmp = 0;
391                 break;
392         }
393
394         if (tempfile)
395                 *tempfile = tmp;
396
397         return local;
398 }
399
400 char *load_url(void *ctx, struct pb_url *url, unsigned int *tempfile)
401 {
402         return load_url_async(ctx, url, tempfile, NULL);
403 }