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 #include <sys/param.h> 29 #include <sys/socket.h> 30 #include <net/if.h> 31 #include <netinet/in.h> 32 #include <netinet/in_systm.h> 33 34 #include <stand.h> 35 #include <net.h> 36 #include <string.h> 37 38 #include "bootstrap.h" 39 40 extern struct in_addr servip; 41 42 extern int pkgfs_init(const char *, struct fs_ops *); 43 extern void pkgfs_cleanup(void); 44 45 COMMAND_SET(install, "install", "install software package", command_install); 46 47 static char *inst_kernel; 48 static char **inst_modules; 49 static char *inst_rootfs; 50 static char *inst_loader_rc; 51 52 static int 53 setpath(char **what, char *val) 54 { 55 char *path; 56 size_t len; 57 int rel; 58 59 len = strlen(val) + 1; 60 rel = (val[0] != '/') ? 1 : 0; 61 path = malloc(len + rel); 62 if (path == NULL) 63 return (ENOMEM); 64 path[0] = '/'; 65 strcpy(path + rel, val); 66 67 *what = path; 68 return (0); 69 } 70 71 static int 72 setmultipath(char ***what, char *val) 73 { 74 char *s, *v; 75 int count, error, idx; 76 77 count = 0; 78 v = val; 79 do { 80 count++; 81 s = strchr(v, ','); 82 v = (s == NULL) ? NULL : s + 1; 83 } while (v != NULL); 84 85 *what = calloc(count + 1, sizeof(char *)); 86 if (*what == NULL) 87 return (ENOMEM); 88 89 for (idx = 0; idx < count; idx++) { 90 s = strchr(val, ','); 91 if (s != NULL) 92 *s++ = '\0'; 93 error = setpath(*what + idx, val); 94 if (error) 95 return (error); 96 val = s; 97 } 98 99 return (0); 100 } 101 102 static int 103 read_metatags(int fd) 104 { 105 char buf[1024]; 106 char *p, *tag, *val; 107 ssize_t fsize; 108 int error; 109 110 fsize = read(fd, buf, sizeof(buf)); 111 if (fsize == -1) 112 return (errno); 113 114 /* 115 * Assume that if we read a whole buffer worth of data, we 116 * haven't read the entire file. In other words, the buffer 117 * size must always be larger than the file size. That way 118 * we can append a '\0' and use standard string operations. 119 * Return an error if this is not possible. 120 */ 121 if (fsize == sizeof(buf)) 122 return (ENOMEM); 123 124 buf[fsize] = '\0'; 125 error = 0; 126 tag = buf; 127 while (!error && *tag != '\0') { 128 val = strchr(tag, '='); 129 if (val == NULL) { 130 error = EINVAL; 131 break; 132 } 133 *val++ = '\0'; 134 p = strchr(val, '\n'); 135 if (p == NULL) { 136 error = EINVAL; 137 break; 138 } 139 *p++ = '\0'; 140 141 if (strcmp(tag, "KERNEL") == 0) 142 error = setpath(&inst_kernel, val); 143 else if (strcmp(tag, "MODULES") == 0) 144 error = setmultipath(&inst_modules, val); 145 else if (strcmp(tag, "ROOTFS") == 0) 146 error = setpath(&inst_rootfs, val); 147 else if (strcmp(tag, "LOADER_RC") == 0) 148 error = setpath(&inst_loader_rc, val); 149 150 tag = p; 151 } 152 153 return (error); 154 } 155 156 static 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 if (inst_loader_rc != NULL) { 177 free(inst_loader_rc); 178 inst_loader_rc = NULL; 179 } 180 pkgfs_cleanup(); 181 } 182 183 /* 184 * usage: install URL 185 * where: URL = tftp://[host]/<package> 186 * or file://[devname[:fstype]]/<package> 187 */ 188 static int 189 install(char *pkgname) 190 { 191 static char buf[256]; 192 struct fs_ops *proto; 193 struct preloaded_file *fp; 194 char *e, *s, *currdev; 195 char *devname; 196 size_t devnamelen; 197 int error, fd, i, local; 198 199 s = strstr(pkgname, "://"); 200 if (s == NULL) 201 goto invalid_url; 202 203 i = s - pkgname; 204 s += 3; 205 if (*s == '\0') 206 goto invalid_url; 207 208 devname = NULL; 209 devnamelen = 0; 210 proto = NULL; 211 local = 0; 212 213 if (i == 4 && !strncasecmp(pkgname, "tftp", i)) { 214 devname = "net0"; 215 devnamelen = 4; 216 proto = &tftp_fsops; 217 } else if (i == 4 && !strncasecmp(pkgname, "file", i)) { 218 currdev = getenv("currdev"); 219 local = 1; 220 221 if (*s == '/') { /* file:/// */ 222 if (devname == NULL) 223 devname = currdev; 224 if (devname == NULL) 225 devname = "disk1"; 226 } else { /* file://devname[:fstype]/ */ 227 devname = s; 228 e = strchr(devname, '/'); 229 if (!e) 230 goto invalid_url; 231 devnamelen = e - devname; 232 s = e; /* consume devname */ 233 } 234 if ((e = strchr(devname, ':')) != NULL) { 235 /* could be :fstype */ 236 devnamelen = e - devname; 237 switch (e[1]) { 238 case '\0': /* just currdev */ 239 break; 240 case 'd': 241 proto = &dosfs_fsops; 242 break; 243 #ifdef HOSTPROG 244 case 'h': 245 { 246 extern struct fs_ops host_fsops; 247 248 proto = &host_fsops; 249 } 250 break; 251 #endif 252 case 'u': 253 proto = &ufs_fsops; 254 break; 255 } 256 } 257 if (proto == NULL && strncmp(devname, "disk", 4) == 0) { 258 proto = &dosfs_fsops; 259 } 260 } 261 262 if (devname == NULL) 263 goto invalid_url; 264 265 if (devnamelen == 0) { 266 /* default is currdev which ends with ':' */ 267 devnamelen = strlen(devname); 268 if (devname[devnamelen - 1] == ':') 269 devnamelen--; 270 } 271 272 if (*s != '/' ) { 273 if (local) 274 goto invalid_url; 275 276 pkgname = strchr(s, '/'); 277 if (pkgname == NULL) 278 goto invalid_url; 279 280 *pkgname = '\0'; 281 servip.s_addr = inet_addr(s); 282 if (servip.s_addr == htonl(INADDR_NONE)) 283 goto invalid_url; 284 285 setenv("serverip", inet_ntoa(servip), 1); 286 287 *pkgname = '/'; 288 } else 289 pkgname = s; 290 291 i = snprintf(buf, sizeof(buf), "%.*s:%s", 292 (int) devnamelen, devname, pkgname); 293 if (i >= (int) sizeof(buf)) { 294 command_errmsg = "package name too long"; 295 return (CMD_ERROR); 296 } 297 setenv("install_package", buf, 1); 298 299 error = pkgfs_init(buf, proto); 300 if (error) { 301 command_errmsg = "cannot open package"; 302 goto fail; 303 } 304 305 /* 306 * Point of no return: unload anything that may have been 307 * loaded and prune the environment from harmful variables. 308 */ 309 unload(); 310 unsetenv("vfs.root.mountfrom"); 311 312 /* 313 * read the metatags file. 314 */ 315 fd = open("/metatags", O_RDONLY); 316 if (fd != -1) { 317 error = read_metatags(fd); 318 close(fd); 319 if (error) { 320 command_errmsg = "cannot load metatags"; 321 goto fail; 322 } 323 } 324 325 s = (inst_kernel == NULL) ? "/kernel" : inst_kernel; 326 error = mod_loadkld(s, 0, NULL); 327 if (error) { 328 command_errmsg = "cannot load kernel from package"; 329 goto fail; 330 } 331 332 /* If there is a loader.rc in the package, execute it */ 333 s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc; 334 fd = open(s, O_RDONLY); 335 if (fd != -1) { 336 close(fd); 337 error = interp_include(s); 338 if (error == CMD_ERROR) 339 goto fail; 340 } 341 342 i = 0; 343 while (inst_modules != NULL && inst_modules[i] != NULL) { 344 error = mod_loadkld(inst_modules[i], 0, NULL); 345 if (error) { 346 command_errmsg = "cannot load module(s) from package"; 347 goto fail; 348 } 349 i++; 350 } 351 352 s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs; 353 if (file_loadraw(s, "mfs_root", 1) == NULL) { 354 error = errno; 355 command_errmsg = "cannot load root file system"; 356 goto fail; 357 } 358 359 cleanup(); 360 361 fp = file_findfile(NULL, NULL); 362 if (fp != NULL) 363 file_formats[fp->f_loader]->l_exec(fp); 364 error = CMD_ERROR; 365 command_errmsg = "unable to start installation"; 366 367 fail: 368 sprintf(buf, "%s (error %d)", command_errmsg, error); 369 cleanup(); 370 unload(); 371 exclusive_file_system = NULL; 372 command_errmsg = buf; /* buf is static. */ 373 return (CMD_ERROR); 374 375 invalid_url: 376 command_errmsg = "invalid URL"; 377 return (CMD_ERROR); 378 } 379 380 static int 381 command_install(int argc, char *argv[]) 382 { 383 int argidx; 384 385 unsetenv("install_format"); 386 387 argidx = 1; 388 while (1) { 389 if (argc == argidx) { 390 command_errmsg = 391 "usage: install [--format] <URL>"; 392 return (CMD_ERROR); 393 } 394 if (!strcmp(argv[argidx], "--format")) { 395 setenv("install_format", "yes", 1); 396 argidx++; 397 continue; 398 } 399 break; 400 } 401 402 return (install(argv[argidx])); 403 } 404