install.c revision 269621
1254721Semaste/*- 2254721Semaste * Copyright (c) 2008-2014, Juniper Networks, Inc. 3254721Semaste * All rights reserved. 4254721Semaste * 5254721Semaste * Redistribution and use in source and binary forms, with or without 6254721Semaste * modification, are permitted provided that the following conditions 7254721Semaste * are met: 8254721Semaste * 1. Redistributions of source code must retain the above copyright 9254721Semaste * notice, this list of conditions and the following disclaimer. 10254721Semaste * 2. Redistributions in binary form must reproduce the above copyright 11254721Semaste * notice, this list of conditions and the following disclaimer in the 12254721Semaste * documentation and/or other materials provided with the distribution. 13254721Semaste * 14254721Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15254721Semaste * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16254721Semaste * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17254721Semaste * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18254721Semaste * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19254721Semaste * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20254721Semaste * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21254721Semaste * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22254721Semaste * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23254721Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24254721Semaste * SUCH DAMAGE. 25254721Semaste */ 26254721Semaste 27254721Semaste#include <sys/cdefs.h> 28254721Semaste__FBSDID("$FreeBSD: head/sys/boot/common/install.c 269621 2014-08-06 00:36:04Z marcel $"); 29254721Semaste 30254721Semaste#include <sys/param.h> 31254721Semaste#include <sys/socket.h> 32254721Semaste#include <net/if.h> 33254721Semaste#include <netinet/in.h> 34254721Semaste#include <netinet/in_systm.h> 35254721Semaste 36254721Semaste#include <stand.h> 37254721Semaste#include <net.h> 38254721Semaste#include <string.h> 39254721Semaste 40254721Semaste#include "bootstrap.h" 41254721Semaste 42254721Semasteextern struct in_addr rootip; 43254721Semasteextern struct in_addr servip; 44254721Semaste 45254721Semasteextern int pkgfs_init(const char *, struct fs_ops *); 46254721Semasteextern void pkgfs_cleanup(void); 47254721Semaste 48254721SemasteCOMMAND_SET(install, "install", "install software package", command_install); 49254721Semaste 50254721Semastestatic char *inst_kernel; 51254721Semastestatic char **inst_modules; 52254721Semastestatic char *inst_rootfs; 53254721Semaste 54254721Semastestatic int 55254721Semastesetpath(char **what, char *val) 56254721Semaste{ 57254721Semaste char *path; 58254721Semaste size_t len; 59254721Semaste int rel; 60254721Semaste 61254721Semaste len = strlen(val) + 1; 62254721Semaste rel = (val[0] != '/') ? 1 : 0; 63254721Semaste path = malloc(len + rel); 64254721Semaste if (path == NULL) 65254721Semaste return (ENOMEM); 66254721Semaste path[0] = '/'; 67254721Semaste strcpy(path + rel, val); 68254721Semaste 69254721Semaste *what = path; 70254721Semaste return (0); 71254721Semaste} 72254721Semaste 73254721Semastestatic int 74254721Semastesetmultipath(char ***what, char *val) 75254721Semaste{ 76254721Semaste char *s, *v; 77254721Semaste int count, error, idx; 78254721Semaste 79254721Semaste count = 0; 80254721Semaste v = val; 81254721Semaste do { 82254721Semaste count++; 83254721Semaste s = strchr(v, ','); 84254721Semaste v = (s == NULL) ? NULL : s + 1; 85254721Semaste } while (v != NULL); 86254721Semaste 87254721Semaste *what = calloc(count + 1, sizeof(char *)); 88254721Semaste if (*what == NULL) 89254721Semaste return (ENOMEM); 90254721Semaste 91254721Semaste for (idx = 0; idx < count; idx++) { 92254721Semaste s = strchr(val, ','); 93254721Semaste if (s != NULL) 94254721Semaste *s++ = '\0'; 95254721Semaste error = setpath(*what + idx, val); 96254721Semaste if (error) 97254721Semaste return (error); 98254721Semaste val = s; 99254721Semaste } 100254721Semaste 101254721Semaste return (0); 102254721Semaste} 103254721Semaste 104254721Semastestatic int 105254721Semasteread_metatags(int fd) 106254721Semaste{ 107254721Semaste char buf[1024]; 108254721Semaste char *p, *tag, *val; 109254721Semaste ssize_t fsize; 110254721Semaste int error; 111254721Semaste 112254721Semaste fsize = read(fd, buf, sizeof(buf)); 113254721Semaste if (fsize == -1) 114254721Semaste return (errno); 115254721Semaste 116254721Semaste /* 117254721Semaste * Assume that if we read a whole buffer worth of data, we 118254721Semaste * haven't read the entire file. In other words, the buffer 119254721Semaste * size must always be larger than the file size. That way 120254721Semaste * we can append a '\0' and use standard string operations. 121254721Semaste * Return an error if this is not possible. 122254721Semaste */ 123254721Semaste if (fsize == sizeof(buf)) 124254721Semaste return (ENOMEM); 125254721Semaste 126254721Semaste buf[fsize] = '\0'; 127254721Semaste error = 0; 128254721Semaste tag = buf; 129254721Semaste while (!error && *tag != '\0') { 130254721Semaste val = strchr(tag, '='); 131254721Semaste if (val == NULL) { 132254721Semaste error = EINVAL; 133254721Semaste break; 134254721Semaste } 135254721Semaste *val++ = '\0'; 136254721Semaste p = strchr(val, '\n'); 137254721Semaste if (p == NULL) { 138254721Semaste error = EINVAL; 139254721Semaste break; 140254721Semaste } 141254721Semaste *p++ = '\0'; 142254721Semaste 143254721Semaste if (strcmp(tag, "KERNEL") == 0) 144254721Semaste error = setpath(&inst_kernel, val); 145254721Semaste else if (strcmp(tag, "MODULES") == 0) 146254721Semaste error = setmultipath(&inst_modules, val); 147254721Semaste else if (strcmp(tag, "ROOTFS") == 0) 148254721Semaste error = setpath(&inst_rootfs, val); 149254721Semaste 150254721Semaste tag = p; 151254721Semaste } 152254721Semaste 153254721Semaste return (error); 154254721Semaste} 155254721Semaste 156254721Semastestatic void 157254721Semastecleanup(void) 158254721Semaste{ 159254721Semaste u_int i; 160254721Semaste 161254721Semaste if (inst_kernel != NULL) { 162254721Semaste free(inst_kernel); 163254721Semaste inst_kernel = NULL; 164254721Semaste } 165254721Semaste if (inst_modules != NULL) { 166254721Semaste i = 0; 167254721Semaste while (inst_modules[i] != NULL) 168254721Semaste free(inst_modules[i++]); 169254721Semaste free(inst_modules); 170254721Semaste inst_modules = NULL; 171254721Semaste } 172254721Semaste if (inst_rootfs != NULL) { 173254721Semaste free(inst_rootfs); 174254721Semaste inst_rootfs = NULL; 175254721Semaste } 176254721Semaste pkgfs_cleanup(); 177254721Semaste} 178254721Semaste 179254721Semaste/* 180254721Semaste * usage: install URL 181254721Semaste * where: URL = (tftp|file)://[host]/<package> 182254721Semaste */ 183254721Semastestatic int 184254721Semasteinstall(char *pkgname) 185254721Semaste{ 186254721Semaste static char buf[256]; 187254721Semaste struct fs_ops *proto; 188254721Semaste struct preloaded_file *fp; 189254721Semaste char *s, *currdev; 190254721Semaste const char *devname; 191254721Semaste int error, fd, i, local; 192254721Semaste 193254721Semaste s = strstr(pkgname, "://"); 194254721Semaste if (s == NULL) 195254721Semaste goto invalid_url; 196254721Semaste 197254721Semaste i = s - pkgname; 198254721Semaste if (i == 4 && !strncasecmp(pkgname, "tftp", i)) { 199254721Semaste devname = "net0"; 200254721Semaste proto = &tftp_fsops; 201254721Semaste local = 0; 202254721Semaste } else if (i == 4 && !strncasecmp(pkgname, "file", i)) { 203254721Semaste currdev = getenv("currdev"); 204254721Semaste if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) { 205254721Semaste devname = "pxe0"; 206254721Semaste proto = NULL; 207254721Semaste } else { 208254721Semaste devname = "disk1"; 209254721Semaste proto = &dosfs_fsops; 210254721Semaste } 211254721Semaste local = 1; 212254721Semaste } else 213254721Semaste goto invalid_url; 214254721Semaste 215254721Semaste s += 3; 216254721Semaste if (*s == '\0') 217254721Semaste goto invalid_url; 218254721Semaste 219254721Semaste if (*s != '/' ) { 220254721Semaste if (local) 221254721Semaste goto invalid_url; 222254721Semaste 223254721Semaste pkgname = strchr(s, '/'); 224254721Semaste if (pkgname == NULL) 225254721Semaste goto invalid_url; 226254721Semaste 227254721Semaste *pkgname = '\0'; 228254721Semaste servip.s_addr = inet_addr(s); 229254721Semaste if (servip.s_addr == htonl(INADDR_NONE)) 230254721Semaste goto invalid_url; 231254721Semaste 232254721Semaste setenv("serverip", inet_ntoa(servip), 1); 233254721Semaste 234254721Semaste *pkgname = '/'; 235254721Semaste } else 236254721Semaste pkgname = s; 237254721Semaste 238254721Semaste if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) { 239254721Semaste command_errmsg = "package name too long"; 240254721Semaste return (CMD_ERROR); 241254721Semaste } 242254721Semaste sprintf(buf, "%s:%s", devname, pkgname); 243254721Semaste setenv("install_package", buf, 1); 244254721Semaste 245254721Semaste error = pkgfs_init(buf, proto); 246254721Semaste if (error) { 247254721Semaste command_errmsg = "cannot open package"; 248254721Semaste goto fail; 249254721Semaste } 250254721Semaste 251254721Semaste /* 252254721Semaste * Point of no return: unload anything that may have been 253254721Semaste * loaded and prune the environment from harmful variables. 254254721Semaste */ 255254721Semaste unload(); 256254721Semaste unsetenv("vfs.root.mountfrom"); 257254721Semaste 258254721Semaste /* 259254721Semaste * read the metatags file. 260254721Semaste */ 261254721Semaste fd = open("/metatags", O_RDONLY); 262254721Semaste if (fd != -1) { 263254721Semaste error = read_metatags(fd); 264254721Semaste close(fd); 265254721Semaste if (error) { 266254721Semaste command_errmsg = "cannot load metatags"; 267254721Semaste goto fail; 268254721Semaste } 269254721Semaste } 270254721Semaste 271254721Semaste s = (inst_kernel == NULL) ? "/kernel" : inst_kernel; 272254721Semaste error = mod_loadkld(s, 0, NULL); 273254721Semaste if (error) { 274254721Semaste command_errmsg = "cannot load kernel from package"; 275254721Semaste goto fail; 276254721Semaste } 277254721Semaste 278254721Semaste i = 0; 279254721Semaste while (inst_modules != NULL && inst_modules[i] != NULL) { 280254721Semaste error = mod_loadkld(inst_modules[i], 0, NULL); 281254721Semaste if (error) { 282254721Semaste command_errmsg = "cannot load module(s) from package"; 283254721Semaste goto fail; 284254721Semaste } 285254721Semaste i++; 286254721Semaste } 287254721Semaste 288254721Semaste s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs; 289254721Semaste if (file_loadraw("mfs_root", s) == NULL) { 290254721Semaste error = errno; 291254721Semaste command_errmsg = "cannot load root file system"; 292254721Semaste goto fail; 293254721Semaste } 294254721Semaste 295254721Semaste cleanup(); 296254721Semaste 297254721Semaste fp = file_findfile(NULL, NULL); 298254721Semaste if (fp != NULL) 299254721Semaste file_formats[fp->f_loader]->l_exec(fp); 300254721Semaste error = CMD_ERROR; 301254721Semaste command_errmsg = "unable to start installation"; 302254721Semaste 303254721Semaste fail: 304254721Semaste sprintf(buf, "%s (error %d)", command_errmsg, error); 305254721Semaste cleanup(); 306254721Semaste unload(); 307254721Semaste exclusive_file_system = NULL; 308254721Semaste command_errmsg = buf; /* buf is static. */ 309254721Semaste return (CMD_ERROR); 310254721Semaste 311254721Semaste invalid_url: 312254721Semaste command_errmsg = "invalid URL"; 313254721Semaste return (CMD_ERROR); 314254721Semaste} 315254721Semaste 316254721Semastestatic int 317254721Semastecommand_install(int argc, char *argv[]) 318254721Semaste{ 319254721Semaste int argidx; 320254721Semaste 321254721Semaste unsetenv("install_format"); 322254721Semaste 323254721Semaste argidx = 1; 324254721Semaste while (1) { 325254721Semaste if (argc == argidx) { 326254721Semaste command_errmsg = 327254721Semaste "usage: install [--format] <URL>"; 328254721Semaste return (CMD_ERROR); 329254721Semaste } 330254721Semaste if (!strcmp(argv[argidx], "--format")) { 331254721Semaste setenv("install_format", "yes", 1); 332254721Semaste argidx++; 333254721Semaste continue; 334254721Semaste } 335254721Semaste break; 336254721Semaste } 337254721Semaste 338254721Semaste return (install(argv[argidx])); 339254721Semaste} 340254721Semaste