static void get_findroot_cap(const char *osroot) { FILE *fp; char path[PATH_MAX]; char buf[BAM_MAXLINE]; struct stat sb; int dboot; int error; int ret; const char *fcn = "get_findroot_cap()"; assert(is_grub(osroot)); (void) snprintf(path, sizeof (path), "%s/%s", osroot, "boot/grub/capability"); if (stat(path, &sb) == -1) { bam_is_findroot = BAM_FINDROOT_ABSENT; BAM_DPRINTF((D_FINDROOT_ABSENT, fcn)); return; } fp = fopen(path, "r"); error = errno; INJECT_ERROR1("GET_CAP_FINDROOT_FOPEN", fp = NULL); if (fp == NULL) { bam_error(OPEN_FAIL, path, strerror(error)); return; } dboot = 0; while (s_fgets(buf, sizeof (buf), fp) != NULL) { if (strcmp(buf, "findroot") == 0) { BAM_DPRINTF((D_FINDROOT_PRESENT, fcn)); bam_is_findroot = BAM_FINDROOT_PRESENT; } if (strcmp(buf, "dboot") == 0) { BAM_DPRINTF((D_DBOOT_PRESENT, fcn)); dboot = 1; } } assert(dboot); if (bam_is_findroot == BAM_FINDROOT_UNKNOWN) { bam_is_findroot = BAM_FINDROOT_ABSENT; BAM_DPRINTF((D_FINDROOT_ABSENT, fcn)); } out: ret = fclose(fp); error = errno; INJECT_ERROR1("GET_CAP_FINDROOT_FCLOSE", ret = 1); if (ret != 0) { bam_error(CLOSE_FAIL, path, strerror(error)); } }
/* * Append the string pointed to by "str" to the string pointed to by "orig" * adding the delimeter "delim" in between. * * Return a pointer to the new string or NULL, if we were passed a bad string. */ static char * append_str(char *orig, char *str, char *delim) { char *newstr; int len; if ((str == NULL) || (delim == NULL)) return (NULL); if ((orig == NULL) || (*orig == NULL)) { /* * Return a pointer to a copy of the path so a caller can * always rely upon being able to free() a returned pointer. */ return (s_strdup(str)); } len = strlen(orig) + strlen(str) + strlen(delim) + 1; if ((newstr = malloc(len)) == NULL) { bam_error(NO_MEM, len); bam_exit(1); } (void) snprintf(newstr, len, "%s%s%s", orig, delim, str); return (newstr); }
/* * Replace the substring "old_str" in a path with the substring "new_str" * * Return a pointer to the modified string. */ static char * modify_path(char *path, char *old_str, char *new_str) { char *newpath; char *pc; int len; /* * Return a pointer to a copy of the path so a caller can always rely * upon being able to free() a returned pointer. */ if ((pc = strstr(path, old_str)) == NULL) return (s_strdup(path)); /* * Allocate space for duplicate of path with name changes and * NULL terminating byte */ len = strlen(path) - strlen(old_str) + strlen(new_str) + 1; if ((newpath = malloc(len)) == NULL) { bam_error(NO_MEM, len); bam_exit(1); } (void) strlcpy(newpath, path, (pc - path) + 1); pc += strlen(old_str); (void) strcat(newpath, new_str); (void) strcat(newpath, pc); return (newpath); }
/* * Set "token" to be the the string starting from the pointer "str" delimited * by any character in the string "delim" or the end of the string, but IGNORE * any characters between single or double quotes. * * Return a pointer to the next non-whitespace character after the delimiter * or NULL if we hit the end of the string. Also return NULL upon failure to * find any characters from the delimeter string or upon failure to allocate * memory for the new token string. */ static char * get_token(char **token, char *str, char *delim) { char *dp; char *start = str; unsigned len; *token = NULL; if ((str == NULL) || (*str == NULL)) return (NULL); do { if ((*str == '\'') || (*str == '"')) { char quote = *str++; while ((*str != NULL) && (*str != quote)) str++; /* no matching quote found in string */ if (*str++ == NULL) return (NULL); } /* look for a character from the delimiter string */ for (dp = delim; ((*dp != NULL) && (*dp != *str)); dp++) ; if (*dp != NULL) { len = str - start + 1; /* found a delimiter, so create a token string */ if ((*token = malloc(len)) == NULL) { bam_error(NO_MEM, len); bam_exit(1); } (void) strlcpy(*token, start, len); while (isspace((int)*++str)) ; return (str); } } while (*str++ != NULL); /* if we hit the end of the string, the token is the whole string */ *token = s_strdup(start); return (NULL); }
/* * Parse a hardware kernel's "kernel$" specifier into parameters we can then * use to construct an appropriate "module$" line that can be used to specify * how to boot the hypervisor's dom0. * * Return values: * * -1: error parsing kernel path * 0: success * 1: kernel already a hypervisor kernel */ static int cvt_metal_kernel(char *kernstr, char **path) { char *token, *parsestr; parsestr = get_token(path, kernstr, " \t,"); if (*path == NULL) return (-1); /* * If the metal kernel specified contains the name of the hypervisor, * we're probably trying to convert an entry already setup to run the * hypervisor, so error out now. */ if (strstr(*path, XEN_MENU) != NULL) { bam_error(ALREADY_HYPER); free(*path); *path = NULL; return (1); } /* if the path was the last item on the line, that's OK. */ if ((parsestr = get_token(&token, parsestr, " \t,")) == NULL) { if (token != NULL) free(token); return (0); } /* if the next token is "-B" process boot options */ if (strncmp(token, BFLAG, strlen(BFLAG)) != 0) { free(token); return (0); } free(token); while ((parsestr = get_token(&token, parsestr, ",")) != NULL) { (void) cvt_metal_option(token); free(token); } if (token != NULL) { (void) cvt_metal_option(token); free(token); } return (0); }
/* * Parse a hypervisor's "kernel$" line into parameters that can be used to * help build an appropriate "kernel$" line for booting a bare metal kernel. * * Return 0 on success, non-zero on failure. */ static int cvt_hyper_kernel(char *kernel) { char *token, *parsestr; parsestr = get_token(&token, kernel, " \t,"); if (token == NULL) return (-1); /* * If the hypervisor kernel specified lives in the metal kernel * directory, we're probably trying to convert an entry already setup * to run on bare metal, so error out now. */ if (strncmp(token, METAL_KERNEL_DIR, strlen(METAL_KERNEL_DIR)) == 0) { bam_error(ALREADY_METAL); free(token); return (-1); } free(token); /* check for kernel options */ while ((parsestr = get_token(&token, parsestr, " ")) != NULL) { (void) cvt_hyper_option(token); free(token); } if (token != NULL) { (void) cvt_hyper_option(token); free(token); } return (0); }
/* * The parse_kernel_line function examines a menu.lst kernel line. For * multiboot, this is: * * kernel <multiboot path> <flags1> <kernel path> <flags2> * * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot * * <kernel path> may be missing, or may be any full or relative path to unix. * We check for it by looking for a word ending in "/unix". If it ends * in "kernel/unix", we upgrade it to a 32-bit entry. If it ends in * "kernel/amd64/unix", we upgrade it to the default entry. Otherwise, * it's a custom kernel, and we skip it. * * <flags*> are anything that doesn't fit either of the above - these will be * copied over. * * For direct boot, the defaults are * * kernel$ <kernel path> <flags> * * <kernel path> is one of: * /platform/i86pc/kernel/$ISADIR/unix * /boot/platform/i86pc/kernel/$ISADIR/unix * /platform/i86pc/kernel/unix * /platform/i86pc/kernel/amd64/unix * /boot/platform/i86pc/kernel/unix * /boot/platform/i86pc/kernel/amd64/unix * * If <kernel path> is any of the last four, the command may also be "kernel". * * <flags> is anything that isn't <kernel path>. * * This function is only called to convert a multiboot entry to a dboot entry * * For safety, we do one more check: if the kernel path starts with /boot, * we verify that the new kernel exists before changing it. This is mainly * done for bfu, as it may cause the failsafe archives to be a different * boot architecture from the newly bfu'ed system. */ static error_t cvt_kernel_line(line_t *line, const char *osroot, entry_t *entry) { char path[PATH_MAX], path_64[PATH_MAX]; char linebuf[PATH_MAX]; char new_arg[PATH_MAX]; struct stat sb, sb_64; char *old_ptr; char *unix_ptr; char *flags1_ptr; char *flags2_ptr; const char *fcn = "cvt_kernel_line()"; BAM_DPRINTF((D_FUNC_ENTRY2, fcn, line->line, osroot)); /* * We only convert multiboot to dboot and nothing else. */ if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) { BAM_DPRINTF((D_NOT_MULTIBOOT_CONVERT, fcn)); return (BAM_SUCCESS); } if (entry->flags & BAM_ENTRY_FAILSAFE) { /* * We're attempting to change failsafe to dboot. * In the bfu case, we may not have a dboot failsafe * kernel i.e. a "unix" under the "/boot" hierarchy. * If so, just emit a message in verbose mode and * return success. */ BAM_DPRINTF((D_TRYING_FAILSAFE_CVT_TO_DBOOT, fcn)); (void) snprintf(path, PATH_MAX, "%s%s", osroot, DIRECT_BOOT_FAILSAFE_32); (void) snprintf(path_64, PATH_MAX, "%s%s", osroot, DIRECT_BOOT_FAILSAFE_64); if (stat(path, &sb) != 0 && stat(path_64, &sb_64) != 0) { if (bam_verbose) { bam_error(FAILSAFE_MISSING, line->lineNum); } BAM_DPRINTF((D_NO_FAILSAFE_UNIX_CONVERT, fcn)); return (BAM_SUCCESS); } } /* * Make sure we have the correct cmd */ free(line->cmd); line->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]); BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, line->cmd)); assert(sizeof (linebuf) > strlen(line->arg) + 32); (void) strlcpy(linebuf, line->arg, sizeof (linebuf)); old_ptr = strpbrk(linebuf, " \t\n"); old_ptr = skip_wspace(old_ptr); if (old_ptr == NULL) { /* * only multiboot and nothing else * i.e. flags1 = unix = flags2 = NULL */ flags1_ptr = unix_ptr = flags2_ptr = NULL; BAM_DPRINTF((D_FLAGS1_UNIX_FLAGS2_NULL, fcn)) goto create; }
error_t get_boot_cap(const char *osroot) { char fname[PATH_MAX]; char *image; uchar_t *ident; int fd; int m; multiboot_header_t *mbh; struct stat sb; int error; const char *fcn = "get_boot_cap()"; if (is_sparc()) { /* there is no non dboot sparc new-boot */ bam_direct = BAM_DIRECT_DBOOT; BAM_DPRINTF((D_IS_SPARC_DBOOT, fcn)); return (BAM_SUCCESS); } if (!is_grub(osroot)) { bam_error(NOT_GRUB_ROOT, osroot); return (BAM_ERROR); } (void) snprintf(fname, PATH_MAX, "%s/%s", osroot, "platform/i86pc/kernel/unix"); fd = open(fname, O_RDONLY); error = errno; INJECT_ERROR1("GET_CAP_UNIX_OPEN", fd = -1); if (fd < 0) { bam_error(OPEN_FAIL, fname, strerror(error)); return (BAM_ERROR); } /* * Verify that this is a sane unix at least 8192 bytes in length */ if (fstat(fd, &sb) == -1 || sb.st_size < 8192) { (void) close(fd); bam_error(INVALID_BINARY, fname); return (BAM_ERROR); } /* * mmap the first 8K */ image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0); error = errno; INJECT_ERROR1("GET_CAP_MMAP", image = MAP_FAILED); if (image == MAP_FAILED) { bam_error(MMAP_FAIL, fname, strerror(error)); return (BAM_ERROR); } ident = (uchar_t *)image; if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) { bam_error(NOT_ELF_FILE, fname); return (BAM_ERROR); } if (ident[EI_CLASS] != ELFCLASS32) { bam_error(WRONG_ELF_CLASS, fname, ident[EI_CLASS]); return (BAM_ERROR); } /* * The GRUB multiboot header must be 32-bit aligned and completely * contained in the 1st 8K of the file. If the unix binary has * a multiboot header, then it is a 'dboot' kernel. Otherwise, * this kernel must be booted via multiboot -- we call this a * 'multiboot' kernel. */ bam_direct = BAM_DIRECT_MULTIBOOT; for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) { mbh = (void *)(image + m); if (mbh->magic == MB_HEADER_MAGIC) { BAM_DPRINTF((D_IS_DBOOT, fcn)); bam_direct = BAM_DIRECT_DBOOT; break; } } (void) munmap(image, 8192); (void) close(fd); INJECT_ERROR1("GET_CAP_MULTIBOOT", bam_direct = BAM_DIRECT_MULTIBOOT); if (bam_direct == BAM_DIRECT_DBOOT) { if (bam_is_hv == BAM_HV_PRESENT) { BAM_DPRINTF((D_IS_XVM, fcn)); } else { BAM_DPRINTF((D_IS_NOT_XVM, fcn)); } } else { BAM_DPRINTF((D_IS_MULTIBOOT, fcn)); } /* Not a fatal error if this fails */ get_findroot_cap(osroot); BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); return (BAM_SUCCESS); }
/*ARGSUSED*/ error_t cvt_to_metal(menu_t *mp, char *osroot, char *menu_root) { const char *fcn = "cvt_to_metal()"; line_t *lp; entry_t *ent; size_t len, zfslen; char *delim = ","; char *newstr; char *osdev; char *title = NULL; char *findroot = NULL; char *bootfs = NULL; char *kernel = NULL; char *module = NULL; char *barchive_path = DIRECT_BOOT_ARCHIVE; char *kern_path = NULL; int curdef, newdef; int emit_bflag = 1; int ret = BAM_ERROR; assert(osroot); BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, "")); /* * First just check to verify osroot is a sane directory. */ if ((osdev = get_special(osroot)) == NULL) { bam_error(CANT_FIND_SPECIAL, osroot); return (BAM_ERROR); } free(osdev); /* * Found the GRUB signature on the target partitions, so now get the * default GRUB boot entry number from the menu.lst file */ curdef = atoi(mp->curdefault->arg); /* look for the first line of the matching boot entry */ for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != curdef)); ent = ent->next) ; /* couldn't find it, so error out */ if (ent == NULL) { bam_error(CANT_FIND_DEFAULT, curdef); goto abort; } /* * Now process the entry itself. */ for (lp = ent->start; lp != NULL; lp = lp->next) { /* * Process important lines from menu.lst boot entry. */ if (lp->flags == BAM_TITLE) { title = alloca(strlen(lp->arg) + 1); (void) strcpy(title, lp->arg); } else if (strcmp(lp->cmd, "findroot") == 0) { findroot = alloca(strlen(lp->arg) + 1); (void) strcpy(findroot, lp->arg); } else if (strcmp(lp->cmd, "bootfs") == 0) { bootfs = alloca(strlen(lp->arg) + 1); (void) strcpy(bootfs, lp->arg); } else if (strcmp(lp->cmd, menu_cmds[MODULE_DOLLAR_CMD]) == 0) { if (strstr(lp->arg, "boot_archive") == NULL) { module = alloca(strlen(lp->arg) + 1); (void) strcpy(module, lp->arg); cvt_hyper_module(module, &kern_path); } else { barchive_path = alloca(strlen(lp->arg) + 1); (void) strcpy(barchive_path, lp->arg); } } else if ((strcmp(lp->cmd, menu_cmds[KERNEL_DOLLAR_CMD]) == 0) && (cvt_hyper_kernel(lp->arg) < 0)) { ret = BAM_NOCHANGE; goto abort; } if (lp == ent->end) break; } /* * If findroot, module or kern_path are NULL, the boot entry is * malformed. */ if (findroot == NULL) { bam_error(FINDROOT_NOT_FOUND, curdef); goto abort; } if (module == NULL) { bam_error(MODULE_NOT_PARSEABLE, curdef); goto abort; } if (kern_path == NULL) { bam_error(KERNEL_NOT_FOUND, curdef); goto abort; } /* * Assemble new kernel and module arguments from parsed values. * * First, change the kernel directory from the hypervisor version to * that needed for a metal kernel. */ newstr = modify_path(kern_path, HYPER_KERNEL_DIR, METAL_KERNEL_DIR); free(kern_path); kern_path = newstr; /* allocate initial space for the kernel path */ len = strlen(kern_path) + 1; zfslen = (zfs_boot ? (WHITESPC(1) + strlen(ZFS_BOOT)) : 0); if ((kernel = malloc(len + zfslen)) == NULL) { free(kern_path); bam_error(NO_MEM, len + zfslen); bam_exit(1); } (void) snprintf(kernel, len, "%s", kern_path); free(kern_path); if (zfs_boot) { char *zfsstr = alloca(zfslen + 1); (void) snprintf(zfsstr, zfslen + 1, " %s", ZFS_BOOT); (void) strcat(kernel, zfsstr); emit_bflag = 0; } /* * Process the bootenv.rc file to look for boot options that would be * the same as what the hypervisor had manually set, as we need not set * those explicitly. * * If there's no bootenv.rc, it's not an issue. */ parse_bootenvrc(osroot); /* * Don't emit a console setting if it's the same as what would be * set by bootenv.rc. */ if ((console_dev != NULL) && (bootenv_rc_console == NULL || (strcmp(console_dev, bootenv_rc_console) != 0))) { if (emit_bflag) { newstr = append_str(kernel, BFLAG, " "); free(kernel); kernel = append_str(newstr, "console=", " "); free(newstr); newstr = append_str(kernel, console_dev, ""); free(kernel); kernel = newstr; emit_bflag = 0; } else { newstr = append_str(kernel, "console=", ","); free(kernel); kernel = append_str(newstr, console_dev, ""); free(newstr); } } /* * We have to do some strange processing here because the hypervisor's * serial ports default to "9600,8,n,1,-" if "comX=auto" is specified, * or to "auto" if nothing is specified. * * This could result in a serial mode setting string being added when * it would otherwise not be needed, but it's better to play it safe. */ if (emit_bflag) { newstr = append_str(kernel, BFLAG, " "); free(kernel); kernel = newstr; delim = " "; emit_bflag = 0; } if ((serial_config[0] != NULL) && (bootenv_rc_serial[0] == NULL || (strcmp(serial_config[0], bootenv_rc_serial[0]) != 0))) { newstr = append_str(kernel, "ttya-mode='", delim); free(kernel); /* * Pass the serial configuration as the delimiter to * append_str() as it will be inserted between the current * string and the string we're appending, in this case the * closing single quote. */ kernel = append_str(newstr, "'", serial_config[0]); free(newstr); delim = ","; } if ((serial_config[1] != NULL) && (bootenv_rc_serial[1] == NULL || (strcmp(serial_config[1], bootenv_rc_serial[1]) != 0))) { newstr = append_str(kernel, "ttyb-mode='", delim); free(kernel); /* * Pass the serial configuration as the delimiter to * append_str() as it will be inserted between the current * string and the string we're appending, in this case the * closing single quote. */ kernel = append_str(newstr, "'", serial_config[1]); free(newstr); delim = ","; } /* shut off warning messages from the entry line parser */ if (ent->flags & BAM_ENTRY_BOOTADM) ent->flags &= ~BAM_ENTRY_BOOTADM; BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, kernel)); BAM_DPRINTF((D_CVT_CMD_MOD_DOLLAR, fcn, module)); if ((newdef = add_boot_entry(mp, title, findroot, kernel, NULL, barchive_path, bootfs)) == BAM_ERROR) { free(kernel); return (newdef); } /* * Now try to delete the current default entry from the menu and add * the new hypervisor entry with the parameters we've setup. */ if (delete_boot_entry(mp, curdef, DBE_QUIET) == BAM_SUCCESS) newdef--; else bam_print(NEW_BOOT_ENTRY, title); free(kernel); /* * If we successfully created the new entry, set the default boot * entry to that entry and let the caller know the new menu should * be written out. */ return (set_global(mp, menu_cmds[DEFAULT_CMD], newdef)); abort: if (ret != BAM_NOCHANGE) bam_error(METAL_ABORT, osroot); return (ret); }
error_t cvt_to_hyper(menu_t *mp, char *osroot, char *extra_args) { const char *fcn = "cvt_to_hyper()"; line_t *lp; entry_t *ent; size_t len, zfslen; char *newstr; char *osdev; char *title = NULL; char *findroot = NULL; char *bootfs = NULL; char *kernel = NULL; char *mod_kernel = NULL; char *module = NULL; char *kern_path = NULL; char *kern_bargs = NULL; int curdef, newdef; int kp_allocated = 0; int ret = BAM_ERROR; assert(osroot); BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, extra_args)); /* * First just check to verify osroot is a sane directory. */ if ((osdev = get_special(osroot)) == NULL) { bam_error(CANT_FIND_SPECIAL, osroot); return (BAM_ERROR); } free(osdev); /* * While the effect is purely cosmetic, if osroot is "/" don't * bother prepending it to any paths as they are constructed to * begin with "/" anyway. */ if (strcmp(osroot, "/") == 0) osroot = ""; /* * Found the GRUB signature on the target partitions, so now get the * default GRUB boot entry number from the menu.lst file */ curdef = atoi(mp->curdefault->arg); /* look for the first line of the matching boot entry */ for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != curdef)); ent = ent->next) ; /* couldn't find it, so error out */ if (ent == NULL) { bam_error(CANT_FIND_DEFAULT, curdef); goto abort; } /* * We found the proper menu entry, so first we need to process the * bootenv.rc file to look for boot options the hypervisor might need * passed as kernel start options such as the console device and serial * port parameters. * * If there's no bootenv.rc, it's not an issue. */ parse_bootenvrc(osroot); if (bootenv_rc_console != NULL) console_metal_to_hyper(bootenv_rc_console); if (bootenv_rc_serial[0] != NULL) (void) serial_metal_to_hyper("ttya-mode", bootenv_rc_serial[0]); if (bootenv_rc_serial[1] != NULL) (void) serial_metal_to_hyper("ttyb-mode", bootenv_rc_serial[1]); /* * Now process the entry itself. */ for (lp = ent->start; lp != NULL; lp = lp->next) { /* * Process important lines from menu.lst boot entry. */ if (lp->flags == BAM_TITLE) { title = alloca(strlen(lp->arg) + 1); (void) strcpy(title, lp->arg); } else if (strcmp(lp->cmd, "findroot") == 0) { findroot = alloca(strlen(lp->arg) + 1); (void) strcpy(findroot, lp->arg); } else if (strcmp(lp->cmd, "bootfs") == 0) { bootfs = alloca(strlen(lp->arg) + 1); (void) strcpy(bootfs, lp->arg); } else if (strcmp(lp->cmd, menu_cmds[MODULE_DOLLAR_CMD]) == 0) { module = alloca(strlen(lp->arg) + 1); (void) strcpy(module, lp->arg); } else if ((strcmp(lp->cmd, menu_cmds[KERNEL_DOLLAR_CMD]) == 0) && (ret = cvt_metal_kernel(lp->arg, &kern_path)) != 0) { if (ret < 0) { ret = BAM_ERROR; bam_error(KERNEL_NOT_PARSEABLE, curdef); } else ret = BAM_NOCHANGE; goto abort; } if (lp == ent->end) break; } /* * If findroot, module or kern_path are NULL, the boot entry is * malformed. */ if (findroot == NULL) { bam_error(FINDROOT_NOT_FOUND, curdef); goto abort; } if (module == NULL) { bam_error(MODULE_NOT_PARSEABLE, curdef); goto abort; } if (kern_path == NULL) { bam_error(KERNEL_NOT_FOUND, curdef); goto abort; } /* assemble new kernel and module arguments from parsed values */ if (console_dev != NULL) { kern_bargs = s_strdup(console_dev); if (serial_config[0] != NULL) { newstr = append_str(kern_bargs, serial_config[0], " "); free(kern_bargs); kern_bargs = newstr; } if (serial_config[1] != NULL) { newstr = append_str(kern_bargs, serial_config[1], " "); free(kern_bargs); kern_bargs = newstr; } } if ((extra_args != NULL) && (*extra_args != NULL)) { newstr = append_str(kern_bargs, extra_args, " "); free(kern_bargs); kern_bargs = newstr; } len = strlen(osroot) + strlen(XEN_MENU) + strlen(kern_bargs) + WHITESPC(1) + 1; kernel = alloca(len); if (kern_bargs != NULL) { if (*kern_bargs != NULL) (void) snprintf(kernel, len, "%s%s %s", osroot, XEN_MENU, kern_bargs); free(kern_bargs); } else { (void) snprintf(kernel, len, "%s%s", osroot, XEN_MENU); } /* * Change the kernel directory from the metal version to that needed for * the hypervisor. Convert either "direct boot" path to the default * path. */ if ((strcmp(kern_path, DIRECT_BOOT_32) == 0) || (strcmp(kern_path, DIRECT_BOOT_64) == 0)) { kern_path = HYPERVISOR_KERNEL; } else { newstr = modify_path(kern_path, METAL_KERNEL_DIR, HYPER_KERNEL_DIR); free(kern_path); kern_path = newstr; kp_allocated = 1; } /* * We need to allocate space for the kernel path (twice) plus an * intervening space, possibly the ZFS boot string, and NULL, * of course. */ len = (strlen(kern_path) * 2) + WHITESPC(1) + 1; zfslen = (zfs_boot ? (WHITESPC(1) + strlen(ZFS_BOOT)) : 0); mod_kernel = alloca(len + zfslen); (void) snprintf(mod_kernel, len, "%s %s", kern_path, kern_path); if (kp_allocated) free(kern_path); if (zfs_boot) { char *zfsstr = alloca(zfslen + 1); (void) snprintf(zfsstr, zfslen + 1, " %s", ZFS_BOOT); (void) strcat(mod_kernel, zfsstr); } /* shut off warning messages from the entry line parser */ if (ent->flags & BAM_ENTRY_BOOTADM) ent->flags &= ~BAM_ENTRY_BOOTADM; BAM_DPRINTF((D_CVT_CMD_KERN_DOLLAR, fcn, kernel)); BAM_DPRINTF((D_CVT_CMD_MOD_DOLLAR, fcn, mod_kernel)); if ((newdef = add_boot_entry(mp, title, findroot, kernel, mod_kernel, module, bootfs)) == BAM_ERROR) return (newdef); /* * Now try to delete the current default entry from the menu and add * the new hypervisor entry with the parameters we've setup. */ if (delete_boot_entry(mp, curdef, DBE_QUIET) == BAM_SUCCESS) newdef--; else bam_print(NEW_BOOT_ENTRY, title); /* * If we successfully created the new entry, set the default boot * entry to that entry and let the caller know the new menu should * be written out. */ return (set_global(mp, menu_cmds[DEFAULT_CMD], newdef)); abort: if (ret != BAM_NOCHANGE) bam_error(HYPER_ABORT, ((*osroot == NULL) ? "/" : osroot)); return (ret); }
/* * The parse_kernel_line function examines a menu.lst kernel line. For * multiboot, this is: * * kernel <multiboot path> <flags1> <kernel path> <flags2> * * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot * * <kernel path> may be missing, or may be any full or relative path to unix. * We check for it by looking for a word ending in "/unix". If it ends * in "kernel/unix", we upgrade it to a 32-bit entry. If it ends in * "kernel/amd64/unix", we upgrade it to the default entry. Otherwise, * it's a custom kernel, and we skip it. * * <flags*> are anything that doesn't fit either of the above - these will be * copied over. * * For direct boot, the defaults are * * kernel$ <kernel path> <flags> * * <kernel path> is one of: * /platform/i86pc/kernel/$ISADIR/unix * /boot/platform/i86pc/kernel/$ISADIR/unix * /platform/i86pc/kernel/unix * /platform/i86pc/kernel/amd64/unix * /boot/platform/i86pc/kernel/unix * /boot/platform/i86pc/kernel/amd64/unix * * If <kernel path> is any of the last four, the command may also be "kernel". * * <flags> is anything that isn't <kernel path>. * * This function is only called to convert a multiboot entry to a dboot entry * * For safety, we do one more check: if the kernel path starts with /boot, * we verify that the new kernel exists before changing it. This is mainly * done for bfu, as it may cause the failsafe archives to be a different * boot architecture from the newly bfu'ed system. */ static error_t cvt_kernel_line(line_t *line, const char *osroot, entry_t *entry) { char path[PATH_MAX], path_64[PATH_MAX]; char linebuf[PATH_MAX]; char new_arg[PATH_MAX]; struct stat sb, sb_64; char *old_ptr; char *unix_ptr; char *flags1_ptr; char *flags2_ptr; const char *fcn = "cvt_kernel_line()"; BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, line->line, osroot)); /* * We only convert multiboot to dboot and nothing else. */ if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) { BAM_DPRINTF(("%s: not MULTIBOOT, not converting\n", fcn)); return (BAM_SUCCESS); } if (entry->flags & BAM_ENTRY_FAILSAFE) { /* * We're attempting to change failsafe to dboot. * In the bfu case, we may not have a dboot failsafe * kernel i.e. a "unix" under the "/boot" hierarchy. * If so, just emit a message in verbose mode and * return success. */ BAM_DPRINTF(("%s: trying to convert failsafe to DBOOT\n", fcn)); (void) snprintf(path, PATH_MAX, "%s%s", osroot, DIRECT_BOOT_FAILSAFE_32); (void) snprintf(path_64, PATH_MAX, "%s%s", osroot, DIRECT_BOOT_FAILSAFE_64); if (stat(path, &sb) != 0 && stat(path_64, &sb_64) != 0) { if (bam_verbose) { bam_error(_("bootadm -m upgrade run, but the " "failsafe archives have not been\nupdated. " "Not updating line %d\n"), line->lineNum); } BAM_DPRINTF(("%s: no FAILSAFE unix, not converting\n", fcn)); return (BAM_SUCCESS); } } /* * Make sure we have the correct cmd */ free(line->cmd); line->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]); BAM_DPRINTF(("%s: converted kernel cmd to %s\n", fcn, line->cmd)); assert(sizeof (linebuf) > strlen(line->arg) + 32); (void) strlcpy(linebuf, line->arg, sizeof (linebuf)); old_ptr = strpbrk(linebuf, " \t\n"); old_ptr = skip_wspace(old_ptr); if (old_ptr == NULL) { /* * only multiboot and nothing else * i.e. flags1 = unix = flags2 = NULL */ flags1_ptr = unix_ptr = flags2_ptr = NULL; BAM_DPRINTF(("%s: NULL flags1, unix, flags2\n", fcn)) goto create; }