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|file)://[host]/<package> 188 */ 189 static int 190 install(char *pkgname) 191 { 192 static char buf[256]; 193 struct fs_ops *proto; 194 struct preloaded_file *fp; 195 char *s, *currdev; 196 const char *devname; 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 if (i == 4 && !strncasecmp(pkgname, "tftp", i)) { 205 devname = "net0"; 206 proto = &tftp_fsops; 207 local = 0; 208 } else if (i == 4 && !strncasecmp(pkgname, "file", i)) { 209 currdev = getenv("currdev"); 210 if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) { 211 devname = "pxe0"; 212 proto = NULL; 213 #ifdef HOSTPROG 214 } else if (currdev != NULL && strcmp(currdev, "host0:") == 0) { 215 extern struct fs_ops host_fsops; 216 217 devname = "host0"; 218 proto = &host_fsops; 219 #endif 220 } else { 221 devname = "disk1"; 222 proto = &dosfs_fsops; 223 } 224 local = 1; 225 } else 226 goto invalid_url; 227 228 s += 3; 229 if (*s == '\0') 230 goto invalid_url; 231 232 if (*s != '/' ) { 233 if (local) 234 goto invalid_url; 235 236 pkgname = strchr(s, '/'); 237 if (pkgname == NULL) 238 goto invalid_url; 239 240 *pkgname = '\0'; 241 servip.s_addr = inet_addr(s); 242 if (servip.s_addr == htonl(INADDR_NONE)) 243 goto invalid_url; 244 245 setenv("serverip", inet_ntoa(servip), 1); 246 247 if (proto == &tftp_fsops) { 248 tftpip.s_addr = servip.s_addr; 249 } 250 251 *pkgname = '/'; 252 } else 253 pkgname = s; 254 255 if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) { 256 command_errmsg = "package name too long"; 257 return (CMD_ERROR); 258 } 259 sprintf(buf, "%s:%s", devname, pkgname); 260 setenv("install_package", buf, 1); 261 262 error = pkgfs_init(buf, proto); 263 if (error) { 264 command_errmsg = "cannot open package"; 265 goto fail; 266 } 267 268 /* 269 * Point of no return: unload anything that may have been 270 * loaded and prune the environment from harmful variables. 271 */ 272 unload(); 273 unsetenv("vfs.root.mountfrom"); 274 275 /* 276 * read the metatags file. 277 */ 278 fd = open("/metatags", O_RDONLY); 279 if (fd != -1) { 280 error = read_metatags(fd); 281 close(fd); 282 if (error) { 283 command_errmsg = "cannot load metatags"; 284 goto fail; 285 } 286 } 287 288 s = (inst_kernel == NULL) ? "/kernel" : inst_kernel; 289 error = mod_loadkld(s, 0, NULL); 290 if (error) { 291 command_errmsg = "cannot load kernel from package"; 292 goto fail; 293 } 294 295 /* If there is a loader.rc in the package, execute it */ 296 s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc; 297 fd = open(s, O_RDONLY); 298 if (fd != -1) { 299 close(fd); 300 error = inter_include(s); 301 if (error == CMD_ERROR) 302 goto fail; 303 } 304 305 i = 0; 306 while (inst_modules != NULL && inst_modules[i] != NULL) { 307 error = mod_loadkld(inst_modules[i], 0, NULL); 308 if (error) { 309 command_errmsg = "cannot load module(s) from package"; 310 goto fail; 311 } 312 i++; 313 } 314 315 s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs; 316 if (file_loadraw(s, "mfs_root", 1) == NULL) { 317 error = errno; 318 command_errmsg = "cannot load root file system"; 319 goto fail; 320 } 321 322 cleanup(); 323 324 fp = file_findfile(NULL, NULL); 325 if (fp != NULL) 326 file_formats[fp->f_loader]->l_exec(fp); 327 error = CMD_ERROR; 328 command_errmsg = "unable to start installation"; 329 330 fail: 331 sprintf(buf, "%s (error %d)", command_errmsg, error); 332 cleanup(); 333 unload(); 334 exclusive_file_system = NULL; 335 command_errmsg = buf; /* buf is static. */ 336 return (CMD_ERROR); 337 338 invalid_url: 339 command_errmsg = "invalid URL"; 340 return (CMD_ERROR); 341 } 342 343 static int 344 command_install(int argc, char *argv[]) 345 { 346 int argidx; 347 348 unsetenv("install_format"); 349 350 argidx = 1; 351 while (1) { 352 if (argc == argidx) { 353 command_errmsg = 354 "usage: install [--format] <URL>"; 355 return (CMD_ERROR); 356 } 357 if (!strcmp(argv[argidx], "--format")) { 358 setenv("install_format", "yes", 1); 359 argidx++; 360 continue; 361 } 362 break; 363 } 364 365 return (install(argv[argidx])); 366 } 367