1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2017 Toomas Soome <tsoome@me.com>
25 */
26
27 #include <stdio.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <limits.h>
35 #include <fcntl.h>
36 #include <strings.h>
37
38 #include <sys/mman.h>
39 #include <sys/elf.h>
40 #include <sys/multiboot.h>
41
42 #include "bootadm.h"
43
44 direct_or_multi_t bam_direct = BAM_DIRECT_NOT_SET;
45 hv_t bam_is_hv = BAM_HV_UNKNOWN;
46 findroot_t bam_is_findroot = BAM_FINDROOT_UNKNOWN;
47
48 static void
get_findroot_cap(const char * osroot)49 get_findroot_cap(const char *osroot)
50 {
51 FILE *fp;
52 char path[PATH_MAX];
53 char buf[BAM_MAXLINE];
54 struct stat sb;
55 int dboot;
56 int error;
57 int ret;
58 const char *fcn = "get_findroot_cap()";
59
60 (void) snprintf(path, sizeof (path), "%s/%s",
61 osroot, "boot/grub/capability");
62
63 if (stat(path, &sb) == -1) {
64 bam_is_findroot = BAM_FINDROOT_ABSENT;
65 BAM_DPRINTF(("%s: findroot capability absent\n", fcn));
66 return;
67 }
68
69 fp = fopen(path, "r");
70 error = errno;
71 INJECT_ERROR1("GET_CAP_FINDROOT_FOPEN", fp = NULL);
72 if (fp == NULL) {
73 bam_error(_("failed to open file: %s: %s\n"), path,
74 strerror(error));
75 return;
76 }
77
78 dboot = 0;
79 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
80 if (strcmp(buf, "findroot") == 0) {
81 BAM_DPRINTF(("%s: findroot capability present\n", fcn));
82 bam_is_findroot = BAM_FINDROOT_PRESENT;
83 }
84 if (strcmp(buf, "dboot") == 0) {
85 BAM_DPRINTF(("%s: dboot capability present\n", fcn));
86 dboot = 1;
87 }
88 }
89
90 assert(dboot);
91
92 if (bam_is_findroot == BAM_FINDROOT_UNKNOWN) {
93 bam_is_findroot = BAM_FINDROOT_ABSENT;
94 BAM_DPRINTF(("%s: findroot capability absent\n", fcn));
95 }
96
97 ret = fclose(fp);
98 error = errno;
99 INJECT_ERROR1("GET_CAP_FINDROOT_FCLOSE", ret = 1);
100 if (ret != 0) {
101 bam_error(_("failed to close file: %s: %s\n"),
102 path, strerror(error));
103 }
104 }
105
106 error_t
get_boot_cap(const char * osroot)107 get_boot_cap(const char *osroot)
108 {
109 char fname[PATH_MAX];
110 char *image;
111 uchar_t *ident;
112 uchar_t class;
113 int fd;
114 int m;
115 multiboot_header_t *mbh;
116 struct stat sb;
117 int error;
118 const char *fcn = "get_boot_cap()";
119
120 if (is_sparc()) {
121 /* there is no non dboot sparc new-boot */
122 bam_direct = BAM_DIRECT_DBOOT;
123 BAM_DPRINTF(("%s: is sparc - always DBOOT\n", fcn));
124 return (BAM_SUCCESS);
125 }
126
127 /*
128 * The install media can support both 64 and 32 bit boot
129 * by using boot archive as ramdisk image. However, to save
130 * the memory, the ramdisk may only have either 32 or 64
131 * bit kernel files. To avoid error message about missing unix,
132 * we should try both variants here and only complain if neither
133 * is found. Since the 64-bit systems are more common, we start
134 * from amd64.
135 */
136 class = ELFCLASS64;
137 (void) snprintf(fname, PATH_MAX, "%s/%s", osroot,
138 "platform/i86pc/kernel/amd64/unix");
139 fd = open(fname, O_RDONLY);
140 if (fd < 0) {
141 class = ELFCLASS32;
142 (void) snprintf(fname, PATH_MAX, "%s/%s", osroot,
143 "platform/i86pc/kernel/unix");
144 fd = open(fname, O_RDONLY);
145 }
146 error = errno;
147 INJECT_ERROR1("GET_CAP_UNIX_OPEN", fd = -1);
148 if (fd < 0) {
149 bam_error(_("failed to open file: %s: %s\n"), fname,
150 strerror(error));
151 return (BAM_ERROR);
152 }
153
154 /*
155 * Verify that this is a sane unix at least 8192 bytes in length
156 */
157 if (fstat(fd, &sb) == -1 || sb.st_size < 8192) {
158 (void) close(fd);
159 bam_error(_("invalid or corrupted binary: %s\n"), fname);
160 return (BAM_ERROR);
161 }
162
163 /*
164 * mmap the first 8K
165 */
166 image = mmap(NULL, 8192, PROT_READ, MAP_SHARED, fd, 0);
167 error = errno;
168 INJECT_ERROR1("GET_CAP_MMAP", image = MAP_FAILED);
169 if (image == MAP_FAILED) {
170 bam_error(_("failed to mmap file: %s: %s\n"), fname,
171 strerror(error));
172 return (BAM_ERROR);
173 }
174
175 ident = (uchar_t *)image;
176 if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 ||
177 ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) {
178 bam_error(_("%s is not an ELF file.\n"), fname);
179 return (BAM_ERROR);
180 }
181 if (ident[EI_CLASS] != class) {
182 bam_error(_("%s is wrong ELF class 0x%x\n"), fname,
183 ident[EI_CLASS]);
184 return (BAM_ERROR);
185 }
186
187 /*
188 * The GRUB multiboot header must be 32-bit aligned and completely
189 * contained in the 1st 8K of the file. If the unix binary has
190 * a multiboot header, then it is a 'dboot' kernel. Otherwise,
191 * this kernel must be booted via multiboot -- we call this a
192 * 'multiboot' kernel.
193 */
194 bam_direct = BAM_DIRECT_MULTIBOOT;
195 for (m = 0; m < 8192 - sizeof (multiboot_header_t); m += 4) {
196 mbh = (void *)(image + m);
197 if (mbh->magic == MB_HEADER_MAGIC) {
198 BAM_DPRINTF(("%s: is DBOOT unix\n", fcn));
199 bam_direct = BAM_DIRECT_DBOOT;
200 break;
201 }
202 }
203 (void) munmap(image, 8192);
204 (void) close(fd);
205
206 INJECT_ERROR1("GET_CAP_MULTIBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
207 if (bam_direct == BAM_DIRECT_DBOOT) {
208 if (bam_is_hv == BAM_HV_PRESENT) {
209 BAM_DPRINTF(("%s: is xVM system\n", fcn));
210 } else {
211 BAM_DPRINTF(("%s: is *NOT* xVM system\n", fcn));
212 }
213 } else {
214 BAM_DPRINTF(("%s: is MULTIBOOT unix\n", fcn));
215 }
216
217 /* Not a fatal error if this fails */
218 get_findroot_cap(osroot);
219
220 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
221 return (BAM_SUCCESS);
222 }
223
224 #define INST_RELEASE "var/sadm/system/admin/INST_RELEASE"
225
226 /*
227 * Return true if root has been bfu'ed. bfu will blow away
228 * var/sadm/system/admin/INST_RELEASE, so if it's still there, we can
229 * assume the system has not been bfu'ed.
230 */
231 static int
is_bfu_system(const char * root)232 is_bfu_system(const char *root)
233 {
234 static int is_bfu = -1;
235 char path[PATH_MAX];
236 struct stat sb;
237 const char *fcn = "is_bfu_system()";
238
239 if (is_bfu != -1) {
240 BAM_DPRINTF(("%s: already done bfu test. bfu is %s present\n",
241 fcn, is_bfu ? "" : "NOT"));
242 return (is_bfu);
243 }
244
245 (void) snprintf(path, sizeof (path), "%s/%s", root, INST_RELEASE);
246 if (stat(path, &sb) != 0) {
247 is_bfu = 1;
248 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
249 } else {
250 is_bfu = 0;
251 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
252 }
253 return (is_bfu);
254 }
255
256 #define MENU_URL(root) (is_bfu_system(root) ? \
257 "http://illumos.org/msg/SUNOS-8000-CF" : \
258 "http://illumos.org/msg/SUNOS-8000-AK")
259
260 /*
261 * Simply allocate a new line and copy in cmd + sep + arg
262 */
263 void
update_line(line_t * linep)264 update_line(line_t *linep)
265 {
266 size_t size;
267 const char *fcn = "update_line()";
268
269 BAM_DPRINTF(("%s: line before update: %s\n", fcn, linep->line));
270 free(linep->line);
271 size = strlen(linep->cmd) + strlen(linep->sep) + strlen(linep->arg) + 1;
272 linep->line = s_calloc(1, size);
273 (void) snprintf(linep->line, size, "%s%s%s", linep->cmd, linep->sep,
274 linep->arg);
275 BAM_DPRINTF(("%s: line after update: %s\n", fcn, linep->line));
276 }
277
278 static char *
skip_wspace(char * ptr)279 skip_wspace(char *ptr)
280 {
281 const char *fcn = "skip_wspace()";
282
283 INJECT_ERROR1("SKIP_WSPACE", ptr = NULL);
284 if (ptr == NULL) {
285 BAM_DPRINTF(("%s: NULL ptr\n", fcn));
286 return (NULL);
287 }
288
289 BAM_DPRINTF(("%s: ptr on entry: %s\n", fcn, ptr));
290 for (; *ptr != '\0'; ptr++) {
291 if ((*ptr != ' ') && (*ptr != '\t') &&
292 (*ptr != '\n'))
293 break;
294 }
295
296 ptr = (*ptr == '\0' ? NULL : ptr);
297
298 BAM_DPRINTF(("%s: ptr on exit: %s\n", fcn, ptr ? ptr : "NULL"));
299
300 return (ptr);
301 }
302
303 static char *
rskip_bspace(char * bound,char * ptr)304 rskip_bspace(char *bound, char *ptr)
305 {
306 const char *fcn = "rskip_bspace()";
307 assert(bound);
308 assert(ptr);
309 assert(bound <= ptr);
310 assert(*bound != ' ' && *bound != '\t' && *bound != '\n');
311
312 BAM_DPRINTF(("%s: ptr on entry: %s\n", fcn, ptr));
313 for (; ptr > bound; ptr--) {
314 if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
315 break;
316 }
317
318 BAM_DPRINTF(("%s: ptr on exit: %s\n", fcn, ptr));
319 return (ptr);
320 }
321
322 /*
323 * The parse_kernel_line function examines a menu.lst kernel line. For
324 * multiboot, this is:
325 *
326 * kernel <multiboot path> <flags1> <kernel path> <flags2>
327 *
328 * <multiboot path> is either /platform/i86pc/multiboot or /boot/multiboot
329 *
330 * <kernel path> may be missing, or may be any full or relative path to unix.
331 * We check for it by looking for a word ending in "/unix". If it ends
332 * in "kernel/unix", we upgrade it to a 32-bit entry. If it ends in
333 * "kernel/amd64/unix", we upgrade it to the default entry. Otherwise,
334 * it's a custom kernel, and we skip it.
335 *
336 * <flags*> are anything that doesn't fit either of the above - these will be
337 * copied over.
338 *
339 * For direct boot, the defaults are
340 *
341 * kernel$ <kernel path> <flags>
342 *
343 * <kernel path> is one of:
344 * /platform/i86pc/kernel/$ISADIR/unix
345 * /boot/platform/i86pc/kernel/$ISADIR/unix
346 * /platform/i86pc/kernel/unix
347 * /platform/i86pc/kernel/amd64/unix
348 * /boot/platform/i86pc/kernel/unix
349 * /boot/platform/i86pc/kernel/amd64/unix
350 *
351 * If <kernel path> is any of the last four, the command may also be "kernel".
352 *
353 * <flags> is anything that isn't <kernel path>.
354 *
355 * This function is only called to convert a multiboot entry to a dboot entry
356 *
357 * For safety, we do one more check: if the kernel path starts with /boot,
358 * we verify that the new kernel exists before changing it. This is mainly
359 * done for bfu, as it may cause the failsafe archives to be a different
360 * boot architecture from the newly bfu'ed system.
361 */
362 static error_t
cvt_kernel_line(line_t * line,const char * osroot,entry_t * entry)363 cvt_kernel_line(line_t *line, const char *osroot, entry_t *entry)
364 {
365 char path[PATH_MAX], path_64[PATH_MAX];
366 char linebuf[PATH_MAX];
367 char new_arg[PATH_MAX];
368 struct stat sb, sb_64;
369 char *old_ptr;
370 char *unix_ptr;
371 char *flags1_ptr;
372 char *flags2_ptr;
373 const char *fcn = "cvt_kernel_line()";
374
375 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, line->line, osroot));
376
377 /*
378 * We only convert multiboot to dboot and nothing else.
379 */
380 if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) {
381 BAM_DPRINTF(("%s: not MULTIBOOT, not converting\n", fcn));
382 return (BAM_SUCCESS);
383 }
384
385 if (entry->flags & BAM_ENTRY_FAILSAFE) {
386 /*
387 * We're attempting to change failsafe to dboot.
388 * In the bfu case, we may not have a dboot failsafe
389 * kernel i.e. a "unix" under the "/boot" hierarchy.
390 * If so, just emit a message in verbose mode and
391 * return success.
392 */
393 BAM_DPRINTF(("%s: trying to convert failsafe to DBOOT\n", fcn));
394 (void) snprintf(path, PATH_MAX, "%s%s", osroot,
395 DIRECT_BOOT_FAILSAFE_32);
396 (void) snprintf(path_64, PATH_MAX, "%s%s", osroot,
397 DIRECT_BOOT_FAILSAFE_64);
398 if (stat(path, &sb) != 0 && stat(path_64, &sb_64) != 0) {
399 if (bam_verbose) {
400 bam_error(_("bootadm -m upgrade run, but the "
401 "failsafe archives have not been\nupdated. "
402 "Not updating line %d\n"), line->lineNum);
403 }
404 BAM_DPRINTF(("%s: no FAILSAFE unix, not converting\n",
405 fcn));
406 return (BAM_SUCCESS);
407 }
408 }
409
410 /*
411 * Make sure we have the correct cmd
412 */
413
414 free(line->cmd);
415 line->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
416 BAM_DPRINTF(("%s: converted kernel cmd to %s\n", fcn, line->cmd));
417
418 assert(sizeof (linebuf) > strlen(line->arg) + 32);
419 (void) strlcpy(linebuf, line->arg, sizeof (linebuf));
420
421 old_ptr = strpbrk(linebuf, " \t\n");
422 old_ptr = skip_wspace(old_ptr);
423 if (old_ptr == NULL) {
424 /*
425 * only multiboot and nothing else
426 * i.e. flags1 = unix = flags2 = NULL
427 */
428 flags1_ptr = unix_ptr = flags2_ptr = NULL;
429 BAM_DPRINTF(("%s: NULL flags1, unix, flags2\n", fcn))
430 goto create;
431 }
432
433 /*
434 *
435 * old_ptr is either at "flags1" or "unix"
436 */
437 if ((unix_ptr = strstr(old_ptr, "/unix")) != NULL) {
438
439 /*
440 * There is a unix.
441 */
442 BAM_DPRINTF(("%s: unix present\n", fcn));
443
444 /* See if there's a flags2 past unix */
445 flags2_ptr = unix_ptr + strlen("/unix");
446 flags2_ptr = skip_wspace(flags2_ptr);
447 if (flags2_ptr) {
448 BAM_DPRINTF(("%s: flags2 present: %s\n", fcn,
449 flags2_ptr));
450 } else {
451 BAM_DPRINTF(("%s: flags2 absent\n", fcn));
452 }
453
454 /* see if there is a flags1 before unix */
455 unix_ptr = rskip_bspace(old_ptr, unix_ptr);
456
457 if (unix_ptr == old_ptr) {
458 flags1_ptr = NULL;
459 BAM_DPRINTF(("%s: flags1 absent\n", fcn));
460 } else {
461 flags1_ptr = old_ptr;
462 *unix_ptr = '\0';
463 unix_ptr++;
464 BAM_DPRINTF(("%s: flags1 present: %s\n", fcn,
465 flags1_ptr));
466 }
467
468 } else {
469 /* There is no unix, there is only a bunch of flags */
470 flags1_ptr = old_ptr;
471 unix_ptr = flags2_ptr = NULL;
472 BAM_DPRINTF(("%s: flags1 present: %s, unix, flags2 absent\n",
473 fcn, flags1_ptr));
474 }
475
476 /*
477 * With dboot, unix is fixed and is at the beginning. We need to
478 * migrate flags1 and flags2
479 */
480 create:
481 if (entry->flags & BAM_ENTRY_FAILSAFE) {
482 (void) snprintf(new_arg, sizeof (new_arg), "%s",
483 DIRECT_BOOT_FAILSAFE_KERNEL);
484 } else {
485 (void) snprintf(new_arg, sizeof (new_arg), "%s",
486 DIRECT_BOOT_KERNEL);
487 }
488 BAM_DPRINTF(("%s: converted unix: %s\n", fcn, new_arg));
489
490 if (flags1_ptr != NULL) {
491 (void) strlcat(new_arg, " ", sizeof (new_arg));
492 (void) strlcat(new_arg, flags1_ptr, sizeof (new_arg));
493 }
494
495 if (flags2_ptr != NULL) {
496 (void) strlcat(new_arg, " ", sizeof (new_arg));
497 (void) strlcat(new_arg, flags2_ptr, sizeof (new_arg));
498 }
499
500 BAM_DPRINTF(("%s: converted unix with flags : %s\n", fcn, new_arg));
501
502 free(line->arg);
503 line->arg = s_strdup(new_arg);
504 update_line(line);
505 BAM_DPRINTF(("%s: converted line is: %s\n", fcn, line->line));
506 return (BAM_SUCCESS);
507 }
508
509 /*
510 * Similar to above, except this time we're looking at a module line,
511 * which is quite a bit simpler.
512 *
513 * Under multiboot, the archive line is:
514 *
515 * module /platform/i86pc/boot_archive
516 *
517 * Under directboot, the archive line is:
518 *
519 * module$ /platform/i86pc/$ISADIR/boot_archive
520 *
521 * which may be specified exactly as either of:
522 *
523 * module /platform/i86pc/boot_archive
524 * module /platform/i86pc/amd64/boot_archive
525 *
526 * Under multiboot, the failsafe is:
527 *
528 * module /boot/x86.miniroot-safe
529 *
530 * Under dboot, the failsafe is:
531 *
532 * module$ /boot/$ISADIR/x86.miniroot-safe
533 *
534 * which may be specified exactly as either of:
535 *
536 * module /boot/x86.miniroot-safe
537 * module /boot/amd64/x86.miniroot-safe
538 */
539 static error_t
cvt_module_line(line_t * line,entry_t * entry)540 cvt_module_line(line_t *line, entry_t *entry)
541 {
542 const char *fcn = "cvt_module_line()";
543
544 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, line->line));
545
546 /*
547 * We only convert multiboot to dboot and nothing else
548 */
549 if (!(entry->flags & BAM_ENTRY_MULTIBOOT)) {
550 BAM_DPRINTF(("%s: not MULTIBOOT, not converting\n", fcn));
551 return (BAM_SUCCESS);
552 }
553
554 if (entry->flags & BAM_ENTRY_FAILSAFE) {
555 if (strcmp(line->arg, FAILSAFE_ARCHIVE) == 0) {
556 BAM_DPRINTF(("%s: failsafe module line needs no "
557 "conversion: %s\n", fcn, line->arg));
558 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
559 return (BAM_SUCCESS);
560 }
561 } else if (strcmp(line->arg, MULTIBOOT_ARCHIVE) != 0) {
562 bam_error(_("module command on line %d not recognized.\n"),
563 line->lineNum);
564 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
565 return (BAM_MSG);
566 }
567
568 free(line->cmd);
569 free(line->arg);
570 line->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
571
572 line->arg = s_strdup(entry->flags & BAM_ENTRY_FAILSAFE ?
573 FAILSAFE_ARCHIVE : DIRECT_BOOT_ARCHIVE);
574
575 update_line(line);
576 BAM_DPRINTF(("%s: converted module line is: %s\n", fcn, line->line));
577 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
578 return (BAM_SUCCESS);
579 }
580
581 static void
bam_warn_hand_entries(menu_t * mp,char * osroot)582 bam_warn_hand_entries(menu_t *mp, char *osroot)
583 {
584 int hand_num;
585 int hand_max;
586 int *hand_list;
587 int i;
588 entry_t *entry;
589 const char *fcn = "bam_warn_hand_entries()";
590
591 if (bam_force) {
592 /*
593 * No warning needed, we are automatically converting
594 * the "hand" entries
595 */
596 BAM_DPRINTF(("%s: force specified, no warnings about hand "
597 "entries\n", fcn));
598 return;
599 }
600
601 hand_num = 0;
602 hand_max = BAM_ENTRY_NUM;
603 hand_list = s_calloc(1, hand_max);
604
605 for (entry = mp->entries; entry; entry = entry->next) {
606 if (entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))
607 continue;
608 BAM_DPRINTF(("%s: found hand entry #: %d\n", fcn,
609 entry->entryNum));
610 if (++hand_num > hand_max) {
611 hand_max *= 2;
612 hand_list = s_realloc(hand_list,
613 hand_max * sizeof (int));
614 }
615 hand_list[hand_num - 1] = entry->entryNum;
616 }
617
618 bam_error(_("bootadm(8) will only upgrade GRUB menu entries added "
619 "by \nbootadm(8) or lu(8). The following entries on %s will "
620 "not be upgraded.\nFor details on manually updating entries, "
621 "see %s\n"), osroot, MENU_URL(osroot));
622 bam_print_stderr("Entry Number%s: ", (hand_num > 1) ?
623 "s" : "");
624 for (i = 0; i < hand_num; i++) {
625 bam_print_stderr("%d ", hand_list[i]);
626 }
627 bam_print_stderr("\n");
628 }
629
630 static entry_t *
find_matching_entry(entry_t * estart,char * grubsign,char * grubroot,int root_opt)631 find_matching_entry(
632 entry_t *estart,
633 char *grubsign,
634 char *grubroot,
635 int root_opt)
636 {
637 entry_t *entry;
638 line_t *line;
639 char opt[10];
640 const char *fcn = "find_matching_entry()";
641
642 assert(grubsign);
643 assert(root_opt == 0 || root_opt == 1);
644
645 (void) snprintf(opt, sizeof (opt), "%d", root_opt);
646 BAM_DPRINTF(("%s: entered. args: %s %s %s\n", fcn, grubsign,
647 grubroot, opt));
648
649 for (entry = estart; entry; entry = entry->next) {
650
651 if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) &&
652 !bam_force) {
653 BAM_DPRINTF(("%s: skipping hand entry #: %d\n",
654 fcn, entry->entryNum));
655 continue;
656 }
657
658 if (entry->flags & BAM_ENTRY_ROOT) {
659 for (line = entry->start; line; line = line->next) {
660 if (line->cmd == NULL || line->arg == NULL) {
661 if (line == entry->end) {
662 BAM_DPRINTF(("%s: entry has "
663 "ended\n", fcn));
664 break;
665 } else {
666 BAM_DPRINTF(("%s: skipping "
667 "NULL line\n", fcn));
668 continue;
669 }
670 }
671 if (strcmp(line->cmd, menu_cmds[ROOT_CMD])
672 == 0 && strcmp(line->arg, grubroot) == 0) {
673 BAM_DPRINTF(("%s: found matching root "
674 "line: %s,%s\n", fcn,
675 line->line, grubsign));
676 return (entry);
677 }
678 if (line == entry->end) {
679 BAM_DPRINTF(("%s: entry has ended\n",
680 fcn));
681 break;
682 }
683 }
684 } else if (entry->flags & BAM_ENTRY_FINDROOT) {
685 for (line = entry->start; line; line = line->next) {
686 if (line->cmd == NULL || line->arg == NULL) {
687 if (line == entry->end) {
688 BAM_DPRINTF(("%s: entry has "
689 "ended\n", fcn));
690 break;
691 } else {
692 BAM_DPRINTF(("%s: skipping "
693 "NULL line\n", fcn));
694 continue;
695 }
696 }
697 if (strcmp(line->cmd, menu_cmds[FINDROOT_CMD])
698 == 0 && strcmp(line->arg, grubsign) == 0) {
699 BAM_DPRINTF(("%s: found matching "
700 "findroot line: %s,%s\n", fcn,
701 line->line, grubsign));
702 return (entry);
703 }
704 if (line == entry->end) {
705 BAM_DPRINTF(("%s: entry has ended\n",
706 fcn));
707 break;
708 }
709 }
710 } else if (root_opt) {
711 /* Neither root nor findroot */
712 BAM_DPRINTF(("%s: no root or findroot and root is "
713 "opt: %d\n", fcn, entry->entryNum));
714 return (entry);
715 }
716 }
717
718 BAM_DPRINTF(("%s: no matching entry found\n", fcn));
719 return (NULL);
720 }
721
722 /*
723 * The following is a set of routines that attempt to convert the
724 * menu entries for the supplied osroot into a format compatible
725 * with the GRUB installation on osroot.
726 *
727 * Each of these conversion routines make no assumptions about
728 * the current state of the menu entry, it does its best to
729 * convert the menu entry to the new state. In the process
730 * we may either upgrade or downgrade.
731 *
732 * We don't make any heroic efforts at conversion. It is better
733 * to be conservative and bail out at the first sign of error. We will
734 * in such cases, point the user at the knowledge-base article
735 * so that they can upgrade manually.
736 */
737 static error_t
bam_add_findroot(menu_t * mp,char * grubsign,char * grubroot,int root_opt)738 bam_add_findroot(menu_t *mp, char *grubsign, char *grubroot, int root_opt)
739 {
740 entry_t *entry;
741 line_t *line;
742 line_t *newlp;
743 int update_num;
744 char linebuf[PATH_MAX];
745 const char *fcn = "bam_add_findroot()";
746
747 update_num = 0;
748
749 bam_print(_("converting entries to findroot...\n"));
750
751 entry = find_matching_entry(mp->entries, grubsign, grubroot, root_opt);
752 while (entry != NULL) {
753 if (entry->flags & BAM_ENTRY_FINDROOT) {
754 /* already converted */
755 BAM_DPRINTF(("%s: entry %d already converted to "
756 "findroot\n", fcn, entry->entryNum));
757 entry = find_matching_entry(entry->next, grubsign,
758 grubroot, root_opt);
759 continue;
760 }
761 for (line = entry->start; line; line = line->next) {
762 if (line->cmd == NULL || line->arg == NULL) {
763 if (line == entry->end) {
764 BAM_DPRINTF(("%s: entry has ended\n",
765 fcn));
766 break;
767 } else {
768 BAM_DPRINTF(("%s: skipping NULL line\n",
769 fcn));
770 continue;
771 }
772 }
773 if (strcmp(line->cmd, menu_cmds[TITLE_CMD]) == 0) {
774 newlp = s_calloc(1, sizeof (line_t));
775 newlp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
776 newlp->sep = s_strdup(" ");
777 newlp->arg = s_strdup(grubsign);
778 (void) snprintf(linebuf, sizeof (linebuf),
779 "%s%s%s", newlp->cmd, newlp->sep,
780 newlp->arg);
781 newlp->line = s_strdup(linebuf);
782 bam_add_line(mp, entry, line, newlp);
783 update_num = 1;
784 entry->flags &= ~BAM_ENTRY_ROOT;
785 entry->flags |= BAM_ENTRY_FINDROOT;
786 BAM_DPRINTF(("%s: added findroot line: %s\n",
787 fcn, newlp->line));
788 line = newlp;
789 }
790 if (strcmp(line->cmd, menu_cmds[ROOT_CMD]) == 0) {
791 BAM_DPRINTF(("%s: freeing root line: %s\n",
792 fcn, line->line));
793 unlink_line(mp, line);
794 line_free(line);
795 }
796 if (line == entry->end) {
797 BAM_DPRINTF(("%s: entry has ended\n", fcn));
798 break;
799 }
800 }
801 entry = find_matching_entry(entry->next, grubsign, grubroot,
802 root_opt);
803 }
804
805 if (update_num) {
806 BAM_DPRINTF(("%s: updated numbering\n", fcn));
807 update_numbering(mp);
808 }
809
810 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
811 return (BAM_SUCCESS);
812 }
813
814 static error_t
bam_add_hv(menu_t * mp,char * grubsign,char * grubroot,int root_opt)815 bam_add_hv(menu_t *mp, char *grubsign, char *grubroot, int root_opt)
816 {
817 entry_t *entry;
818 const char *fcn = "bam_add_hv()";
819
820 bam_print(_("adding xVM entries...\n"));
821
822 entry = find_matching_entry(mp->entries, grubsign, grubroot, root_opt);
823 while (entry != NULL) {
824 if (entry->flags & BAM_ENTRY_HV) {
825 BAM_DPRINTF(("%s: entry %d already converted to "
826 "xvm HV\n", fcn, entry->entryNum));
827 return (BAM_SUCCESS);
828 }
829 entry = find_matching_entry(entry->next, grubsign, grubroot,
830 root_opt);
831 }
832
833 (void) add_boot_entry(mp, NEW_HV_ENTRY, grubsign, XEN_MENU,
834 XEN_KERNEL_MODULE_LINE, DIRECT_BOOT_ARCHIVE, NULL);
835
836 BAM_DPRINTF(("%s: added xVM HV entry via add_boot_entry()\n", fcn));
837
838 update_numbering(mp);
839
840 BAM_DPRINTF(("%s: returning SUCCESS\n", fcn));
841
842 return (BAM_SUCCESS);
843 }
844
845 static error_t
bam_add_dboot(menu_t * mp,char * osroot,char * grubsign,char * grubroot,int root_opt)846 bam_add_dboot(
847 menu_t *mp,
848 char *osroot,
849 char *grubsign,
850 char *grubroot,
851 int root_opt)
852 {
853 int msg = 0;
854 entry_t *entry;
855 line_t *line;
856 error_t ret;
857 const char *fcn = "bam_add_dboot()";
858
859 bam_print(_("converting entries to dboot...\n"));
860
861 entry = find_matching_entry(mp->entries, grubsign, grubroot, root_opt);
862 while (entry != NULL) {
863 for (line = entry->start; line; line = line->next) {
864 if (line->cmd == NULL || line->arg == NULL) {
865 if (line == entry->end) {
866 BAM_DPRINTF(("%s: entry has ended\n",
867 fcn));
868 break;
869 } else {
870 BAM_DPRINTF(("%s: skipping NULL line\n",
871 fcn));
872 continue;
873 }
874 }
875
876 /*
877 * If we have a kernel$ command, assume it
878 * is dboot already. If it is not a dboot
879 * entry, something funny is going on and
880 * we will leave it alone
881 */
882 if (strcmp(line->cmd, menu_cmds[KERNEL_CMD]) == 0) {
883 ret = cvt_kernel_line(line, osroot, entry);
884 INJECT_ERROR1("ADD_DBOOT_KERN_ERR",
885 ret = BAM_ERROR);
886 INJECT_ERROR1("ADD_DBOOT_KERN_MSG",
887 ret = BAM_MSG);
888 if (ret == BAM_ERROR) {
889 BAM_DPRINTF(("%s: cvt_kernel_line() "
890 "failed\n", fcn));
891 return (ret);
892 } else if (ret == BAM_MSG) {
893 msg = 1;
894 BAM_DPRINTF(("%s: BAM_MSG returned "
895 "from cvt_kernel_line()\n", fcn));
896 }
897 }
898 if (strcmp(line->cmd, menu_cmds[MODULE_CMD]) == 0) {
899 ret = cvt_module_line(line, entry);
900 INJECT_ERROR1("ADD_DBOOT_MOD_ERR",
901 ret = BAM_ERROR);
902 INJECT_ERROR1("ADD_DBOOT_MOD_MSG",
903 ret = BAM_MSG);
904 if (ret == BAM_ERROR) {
905 BAM_DPRINTF(("%s: cvt_module_line() "
906 "failed\n", fcn));
907 return (ret);
908 } else if (ret == BAM_MSG) {
909 BAM_DPRINTF(("%s: BAM_MSG returned "
910 "from cvt_module_line()\n", fcn));
911 msg = 1;
912 }
913 }
914
915 if (line == entry->end) {
916 BAM_DPRINTF(("%s: entry has ended\n", fcn));
917 break;
918 }
919 }
920 entry = find_matching_entry(entry->next, grubsign, grubroot,
921 root_opt);
922 }
923
924 ret = msg ? BAM_MSG : BAM_SUCCESS;
925 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, ret));
926 return (ret);
927 }
928
929 /*ARGSUSED*/
930 error_t
upgrade_menu(menu_t * mp,char * osroot,char * menu_root)931 upgrade_menu(menu_t *mp, char *osroot, char *menu_root)
932 {
933 char *osdev;
934 char *grubsign;
935 char *grubroot;
936 int ret1;
937 int ret2;
938 int ret3;
939 const char *fcn = "upgrade_menu()";
940
941 assert(osroot);
942 assert(menu_root);
943
944 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, osroot, menu_root));
945
946 /*
947 * We only support upgrades. Xen may not be present
948 * on smaller metaclusters so we don't check for that.
949 */
950 if (bam_is_findroot != BAM_FINDROOT_PRESENT ||
951 bam_direct != BAM_DIRECT_DBOOT) {
952 bam_error(_("automated downgrade of GRUB menu to older "
953 "version not supported.\n"));
954 return (BAM_ERROR);
955 }
956
957 /*
958 * First get the GRUB signature
959 */
960 osdev = get_special(osroot);
961 INJECT_ERROR1("UPGRADE_OSDEV", osdev = NULL);
962 if (osdev == NULL) {
963 bam_error(_("cant find special file for mount-point %s\n"),
964 osroot);
965 return (BAM_ERROR);
966 }
967
968 grubsign = get_grubsign(osroot, osdev);
969 INJECT_ERROR1("UPGRADE_GRUBSIGN", grubsign = NULL);
970 if (grubsign == NULL) {
971 free(osdev);
972 bam_error(_("cannot find GRUB signature for %s\n"), osroot);
973 return (BAM_ERROR);
974 }
975
976 /* not fatal if we can't get grubroot */
977 grubroot = get_grubroot(osroot, osdev, menu_root);
978 INJECT_ERROR1("UPGRADE_GRUBROOT", grubroot = NULL);
979
980 free(osdev);
981
982 ret1 = bam_add_findroot(mp, grubsign,
983 grubroot, root_optional(osroot, menu_root));
984 INJECT_ERROR1("UPGRADE_ADD_FINDROOT", ret1 = BAM_ERROR);
985 if (ret1 == BAM_ERROR)
986 goto abort;
987
988 if (bam_is_hv == BAM_HV_PRESENT) {
989 ret2 = bam_add_hv(mp, grubsign, grubroot,
990 root_optional(osroot, menu_root));
991 INJECT_ERROR1("UPGRADE_ADD_HV", ret2 = BAM_ERROR);
992 if (ret2 == BAM_ERROR)
993 goto abort;
994 } else
995 ret2 = BAM_SUCCESS;
996
997 ret3 = bam_add_dboot(mp, osroot, grubsign,
998 grubroot, root_optional(osroot, menu_root));
999 INJECT_ERROR1("UPGRADE_ADD_DBOOT", ret3 = BAM_ERROR);
1000 if (ret3 == BAM_ERROR)
1001 goto abort;
1002
1003 if (ret1 == BAM_MSG || ret2 == BAM_MSG || ret3 == BAM_MSG) {
1004 bam_error(_("one or more GRUB menu entries were not "
1005 "automatically upgraded\nFor details on manually "
1006 "updating entries, see %s\n"), MENU_URL(osroot));
1007 } else {
1008 bam_warn_hand_entries(mp, osroot);
1009 }
1010
1011 free(grubsign);
1012
1013 BAM_DPRINTF(("%s: returning ret = %d\n", fcn, BAM_WRITE));
1014 return (BAM_WRITE);
1015
1016 abort:
1017 free(grubsign);
1018 bam_error(_("error upgrading GRUB menu entries on %s. Aborting.\n"
1019 "For details on manually updating entries, see %s\n"), osroot,
1020 MENU_URL(osroot));
1021 return (BAM_ERROR);
1022 }
1023