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 rootip;
43 extern struct in_addr servip;
44
45 extern int pkgfs_init(const char *, struct fs_ops *);
46 extern void pkgfs_cleanup(void);
47
48 COMMAND_SET(install, "install", "install software package", command_install);
49
50 static char *inst_kernel;
51 static char **inst_modules;
52 static char *inst_rootfs;
53
54 static int
setpath(char ** what,char * val)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
setmultipath(char *** what,char * val)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
read_metatags(int fd)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
150 tag = p;
151 }
152
153 return (error);
154 }
155
156 static void
cleanup(void)157 cleanup(void)
158 {
159 u_int i;
160
161 if (inst_kernel != NULL) {
162 free(inst_kernel);
163 inst_kernel = NULL;
164 }
165 if (inst_modules != NULL) {
166 i = 0;
167 while (inst_modules[i] != NULL)
168 free(inst_modules[i++]);
169 free(inst_modules);
170 inst_modules = NULL;
171 }
172 if (inst_rootfs != NULL) {
173 free(inst_rootfs);
174 inst_rootfs = NULL;
175 }
176 pkgfs_cleanup();
177 }
178
179 /*
180 * usage: install URL
181 * where: URL = (tftp|file)://[host]/<package>
182 */
183 static int
install(char * pkgname)184 install(char *pkgname)
185 {
186 static char buf[256];
187 struct fs_ops *proto;
188 struct preloaded_file *fp;
189 char *s, *currdev;
190 const char *devname;
191 int error, fd, i, local;
192
193 s = strstr(pkgname, "://");
194 if (s == NULL)
195 goto invalid_url;
196
197 i = s - pkgname;
198 if (i == 4 && !strncasecmp(pkgname, "tftp", i)) {
199 devname = "net0";
200 proto = &tftp_fsops;
201 local = 0;
202 } else if (i == 4 && !strncasecmp(pkgname, "file", i)) {
203 currdev = getenv("currdev");
204 if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) {
205 devname = "pxe0";
206 proto = NULL;
207 } else {
208 devname = "disk1";
209 proto = &dosfs_fsops;
210 }
211 local = 1;
212 } else
213 goto invalid_url;
214
215 s += 3;
216 if (*s == '\0')
217 goto invalid_url;
218
219 if (*s != '/' ) {
220 if (local)
221 goto invalid_url;
222
223 pkgname = strchr(s, '/');
224 if (pkgname == NULL)
225 goto invalid_url;
226
227 *pkgname = '\0';
228 servip.s_addr = inet_addr(s);
229 if (servip.s_addr == htonl(INADDR_NONE))
230 goto invalid_url;
231
232 setenv("serverip", inet_ntoa(servip), 1);
233
234 *pkgname = '/';
235 } else
236 pkgname = s;
237
238 if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) {
239 command_errmsg = "package name too long";
240 return (CMD_ERROR);
241 }
242 sprintf(buf, "%s:%s", devname, pkgname);
243 setenv("install_package", buf, 1);
244
245 error = pkgfs_init(buf, proto);
246 if (error) {
247 command_errmsg = "cannot open package";
248 goto fail;
249 }
250
251 /*
252 * Point of no return: unload anything that may have been
253 * loaded and prune the environment from harmful variables.
254 */
255 unload();
256 unsetenv("vfs.root.mountfrom");
257
258 /*
259 * read the metatags file.
260 */
261 fd = open("/metatags", O_RDONLY);
262 if (fd != -1) {
263 error = read_metatags(fd);
264 close(fd);
265 if (error) {
266 command_errmsg = "cannot load metatags";
267 goto fail;
268 }
269 }
270
271 s = (inst_kernel == NULL) ? "/kernel" : inst_kernel;
272 error = mod_loadkld(s, 0, NULL);
273 if (error) {
274 command_errmsg = "cannot load kernel from package";
275 goto fail;
276 }
277
278 i = 0;
279 while (inst_modules != NULL && inst_modules[i] != NULL) {
280 error = mod_loadkld(inst_modules[i], 0, NULL);
281 if (error) {
282 command_errmsg = "cannot load module(s) from package";
283 goto fail;
284 }
285 i++;
286 }
287
288 s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs;
289 if (file_loadraw(s, "mfs_root") == NULL) {
290 error = errno;
291 command_errmsg = "cannot load root file system";
292 goto fail;
293 }
294
295 cleanup();
296
297 fp = file_findfile(NULL, NULL);
298 if (fp != NULL)
299 file_formats[fp->f_loader]->l_exec(fp);
300 error = CMD_ERROR;
301 command_errmsg = "unable to start installation";
302
303 fail:
304 sprintf(buf, "%s (error %d)", command_errmsg, error);
305 cleanup();
306 unload();
307 exclusive_file_system = NULL;
308 command_errmsg = buf; /* buf is static. */
309 return (CMD_ERROR);
310
311 invalid_url:
312 command_errmsg = "invalid URL";
313 return (CMD_ERROR);
314 }
315
316 static int
command_install(int argc,char * argv[])317 command_install(int argc, char *argv[])
318 {
319 int argidx;
320
321 unsetenv("install_format");
322
323 argidx = 1;
324 while (1) {
325 if (argc == argidx) {
326 command_errmsg =
327 "usage: install [--format] <URL>";
328 return (CMD_ERROR);
329 }
330 if (!strcmp(argv[argidx], "--format")) {
331 setenv("install_format", "yes", 1);
332 argidx++;
333 continue;
334 }
335 break;
336 }
337
338 return (install(argv[argidx]));
339 }
340