]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/common/install.c
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / stand / common / install.c
1 /*-
2  * Copyright (c) 2008-2014, Juniper Networks, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <net/if.h>
33 #include <netinet/in.h>
34 #include <netinet/in_systm.h>
35
36 #include <stand.h>
37 #include <net.h>
38 #include <string.h>
39
40 #include "bootstrap.h"
41
42 extern struct in_addr servip;
43
44 extern int pkgfs_init(const char *, struct fs_ops *);
45 extern void pkgfs_cleanup(void);
46
47 COMMAND_SET(install, "install", "install software package", command_install);
48
49 static char *inst_kernel;
50 static char **inst_modules;
51 static char *inst_rootfs;
52 static char *inst_loader_rc;
53
54 static int
55 setpath(char **what, char *val)
56 {
57         char *path;
58         size_t len;
59         int rel;
60
61         len = strlen(val) + 1;
62         rel = (val[0] != '/') ? 1 : 0;
63         path = malloc(len + rel);
64         if (path == NULL)
65                 return (ENOMEM);
66         path[0] = '/';
67         strcpy(path + rel, val);
68
69         *what = path;
70         return (0);
71 }
72
73 static int
74 setmultipath(char ***what, char *val)
75 {
76         char *s, *v;
77         int count, error, idx;
78
79         count = 0;
80         v = val;
81         do {
82                 count++;
83                 s = strchr(v, ',');
84                 v = (s == NULL) ? NULL : s + 1;
85         } while (v != NULL);
86
87         *what = calloc(count + 1, sizeof(char *));
88         if (*what == NULL)
89                 return (ENOMEM);
90
91         for (idx = 0; idx < count; idx++) {
92                 s = strchr(val, ',');
93                 if (s != NULL)
94                         *s++ = '\0';
95                 error = setpath(*what + idx, val);
96                 if (error)
97                         return (error);
98                 val = s;
99         }
100
101         return (0);
102 }
103
104 static int
105 read_metatags(int fd)
106 {
107         char buf[1024];
108         char *p, *tag, *val;
109         ssize_t fsize;
110         int error;
111
112         fsize = read(fd, buf, sizeof(buf));
113         if (fsize == -1)
114                 return (errno);
115
116         /*
117          * Assume that if we read a whole buffer worth of data, we
118          * haven't read the entire file. In other words, the buffer
119          * size must always be larger than the file size. That way
120          * we can append a '\0' and use standard string operations.
121          * Return an error if this is not possible.
122          */
123         if (fsize == sizeof(buf))
124                 return (ENOMEM);
125
126         buf[fsize] = '\0';
127         error = 0;
128         tag = buf;
129         while (!error && *tag != '\0') {
130                 val = strchr(tag, '=');
131                 if (val == NULL) {
132                         error = EINVAL;
133                         break;
134                 }
135                 *val++ = '\0';
136                 p = strchr(val, '\n');
137                 if (p == NULL) {
138                         error = EINVAL;
139                         break;
140                 }
141                 *p++ = '\0';
142
143                 if (strcmp(tag, "KERNEL") == 0)
144                         error = setpath(&inst_kernel, val);
145                 else if (strcmp(tag, "MODULES") == 0)
146                         error = setmultipath(&inst_modules, val);
147                 else if (strcmp(tag, "ROOTFS") == 0)
148                         error = setpath(&inst_rootfs, val);
149                 else if (strcmp(tag, "LOADER_RC") == 0)
150                         error = setpath(&inst_loader_rc, val);
151
152                 tag = p;
153         }
154
155         return (error);
156 }
157
158 static void
159 cleanup(void)
160 {
161         u_int i;
162
163         if (inst_kernel != NULL) {
164                 free(inst_kernel);
165                 inst_kernel = NULL;
166         }
167         if (inst_modules != NULL) {
168                 i = 0;
169                 while (inst_modules[i] != NULL)
170                         free(inst_modules[i++]);
171                 free(inst_modules);
172                 inst_modules = NULL;
173         }
174         if (inst_rootfs != NULL) {
175                 free(inst_rootfs);
176                 inst_rootfs = NULL;
177         }
178         if (inst_loader_rc != NULL) {
179                 free(inst_loader_rc);
180                 inst_loader_rc = NULL;
181         }
182         pkgfs_cleanup();
183 }
184
185 /*
186  * usage: install URL
187  * where: URL = tftp://[host]/<package>
188  *      or      file://[devname[:fstype]]/<package>
189  */
190 static int
191 install(char *pkgname)
192 {
193         static char buf[256];
194         struct fs_ops *proto;
195         struct preloaded_file *fp;
196         char *e, *s, *currdev;
197         char *devname;
198         size_t devnamelen;
199         int error, fd, i, local;
200
201         s = strstr(pkgname, "://");
202         if (s == NULL)
203                 goto invalid_url;
204
205         i = s - pkgname;
206         s += 3;
207         if (*s == '\0')
208                 goto invalid_url;
209
210         devname = NULL;
211         devnamelen = 0;
212         proto = NULL;
213         local = 0;
214
215         if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
216                 devname = "net0";
217                 devnamelen = 4;
218                 proto = &tftp_fsops;
219         } else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
220                 currdev = getenv("currdev");
221                 local = 1;
222
223                 if (*s == '/') {        /* file:/// */
224                         if (devname == NULL)
225                                 devname = currdev;
226                         if (devname == NULL)
227                                 devname = "disk1";
228                 } else {                /* file://devname[:fstype]/ */
229                         devname = s;
230                         e = strchr(devname, '/');
231                         if (!e)
232                                 goto invalid_url;
233                         devnamelen = e - devname;
234                         s = e;          /* consume devname */
235                 }
236                 if ((e = strchr(devname, ':')) != NULL) {
237                         /* could be :fstype */
238                         devnamelen = e - devname;
239                         switch (e[1]) {
240                         case '\0':      /* just currdev */
241                                 break;
242                         case 'd':
243                                 proto = &dosfs_fsops;
244                                 break;
245 #ifdef HOSTPROG
246                         case 'h':
247                                 {
248                                         extern struct fs_ops host_fsops;
249
250                                         proto = &host_fsops;
251                                 }
252                                 break;
253 #endif
254                         case 'u':
255                                 proto = &ufs_fsops;
256                                 break;
257                         }
258                 }
259                 if (proto == NULL && strncmp(devname, "disk", 4) == 0) {
260                         proto = &dosfs_fsops;
261                 }
262         }
263
264         if (devname == NULL)
265                 goto invalid_url;
266
267         if (devnamelen == 0) {
268                 /* default is currdev which ends with ':' */
269                 devnamelen = strlen(devname);
270                 if (devname[devnamelen - 1] == ':')
271                         devnamelen--;
272         }
273
274         if (*s != '/' ) {
275                 if (local)
276                         goto invalid_url;
277
278                 pkgname = strchr(s, '/');
279                 if (pkgname == NULL)
280                         goto invalid_url;
281
282                 *pkgname = '\0';
283                 servip.s_addr = inet_addr(s);
284                 if (servip.s_addr == htonl(INADDR_NONE))
285                         goto invalid_url;
286
287                 setenv("serverip", inet_ntoa(servip), 1);
288
289                 if (proto == &tftp_fsops) {
290                         tftpip.s_addr = servip.s_addr;
291                 }
292
293                 *pkgname = '/';
294         } else
295                 pkgname = s;
296
297         i = snprintf(buf, sizeof(buf), "%.*s:%s",
298             (int) devnamelen, devname, pkgname);
299         if (i >= (int) sizeof(buf)) {
300                 command_errmsg = "package name too long";
301                 return (CMD_ERROR);
302         }
303         setenv("install_package", buf, 1);
304
305         error = pkgfs_init(buf, proto);
306         if (error) {
307                 command_errmsg = "cannot open package";
308                 goto fail;
309         }
310
311         /*
312          * Point of no return: unload anything that may have been
313          * loaded and prune the environment from harmful variables.
314          */
315         unload();
316         unsetenv("vfs.root.mountfrom");
317
318         /*
319          * read the metatags file.
320          */
321         fd = open("/metatags", O_RDONLY);
322         if (fd != -1) {
323                 error = read_metatags(fd);
324                 close(fd);
325                 if (error) {
326                         command_errmsg = "cannot load metatags";
327                         goto fail;
328                 }
329         }
330
331         s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
332         error = mod_loadkld(s, 0, NULL);
333         if (error) {
334                 command_errmsg = "cannot load kernel from package";
335                 goto fail;
336         }
337
338         /* If there is a loader.rc in the package, execute it */
339         s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc;
340         fd = open(s, O_RDONLY);
341         if (fd != -1) {
342                 close(fd);
343                 error = inter_include(s);
344                 if (error == CMD_ERROR)
345                         goto fail;
346         }
347
348         i = 0;
349         while (inst_modules != NULL && inst_modules[i] != NULL) {
350                 error = mod_loadkld(inst_modules[i], 0, NULL);
351                 if (error) {
352                         command_errmsg = "cannot load module(s) from package";
353                         goto fail;
354                 }
355                 i++;
356         }
357
358         s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
359         if (file_loadraw(s, "mfs_root", 1) == NULL) {
360                 error = errno;
361                 command_errmsg = "cannot load root file system";
362                 goto fail;
363         }
364
365         cleanup();
366
367         fp = file_findfile(NULL, NULL);
368         if (fp != NULL)
369                 file_formats[fp->f_loader]->l_exec(fp);
370         error = CMD_ERROR;
371         command_errmsg = "unable to start installation";
372
373  fail:
374         sprintf(buf, "%s (error %d)", command_errmsg, error);
375         cleanup();
376         unload();
377         exclusive_file_system = NULL;
378         command_errmsg = buf;   /* buf is static. */
379         return (CMD_ERROR);
380
381  invalid_url:
382         command_errmsg = "invalid URL";
383         return (CMD_ERROR);
384 }
385
386 static int
387 command_install(int argc, char *argv[])
388 {
389         int argidx;
390
391         unsetenv("install_format");
392
393         argidx = 1;
394         while (1) {
395                 if (argc == argidx) {
396                         command_errmsg =
397                             "usage: install [--format] <URL>";
398                         return (CMD_ERROR);
399                 }
400                 if (!strcmp(argv[argidx], "--format")) {
401                         setenv("install_format", "yes", 1);
402                         argidx++;
403                         continue;
404                 }
405                 break;
406         }
407
408         return (install(argv[argidx]));
409 }