Magellan Linux

Contents of /tags/grubby-8_15/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1871 - (show annotations) (download)
Mon Jul 2 13:28:01 2012 UTC (11 years, 9 months ago) by niro
File MIME type: text/plain
File size: 116616 byte(s)
tagged 'grubby-8_15'
1 /*
2 * grubby.c
3 *
4 * Copyright (C) 2001-2008 Red Hat, Inc.
5 * All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifndef _GNU_SOURCE
22 #define _GNU_SOURCE
23 #endif
24 #include <ctype.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <mntent.h>
28 #include <popt.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <libgen.h>
35 #include <execinfo.h>
36 #include <signal.h>
37 #include <blkid/blkid.h>
38
39 #ifndef DEBUG
40 #define DEBUG 0
41 #endif
42
43 #if DEBUG
44 #define dbgPrintf(format, args...) fprintf(stderr, format , ## args)
45 #else
46 #define dbgPrintf(format, args...)
47 #endif
48
49 int debug = 0; /* Currently just for template debugging */
50
51 #define _(A) (A)
52
53 #define MAX_EXTRA_INITRDS 16 /* code segment checked by --bootloader-probe */
54 #define CODE_SEG_SIZE 128 /* code segment checked by --bootloader-probe */
55
56 #define NOOP_OPCODE 0x90
57 #define JMP_SHORT_OPCODE 0xeb
58
59 /* comments get lumped in with indention */
60 struct lineElement {
61 char * item;
62 char * indent;
63 };
64
65 enum lineType_e {
66 LT_WHITESPACE = 1 << 0,
67 LT_TITLE = 1 << 1,
68 LT_KERNEL = 1 << 2,
69 LT_INITRD = 1 << 3,
70 LT_HYPER = 1 << 4,
71 LT_DEFAULT = 1 << 5,
72 LT_MBMODULE = 1 << 6,
73 LT_ROOT = 1 << 7,
74 LT_FALLBACK = 1 << 8,
75 LT_KERNELARGS = 1 << 9,
76 LT_BOOT = 1 << 10,
77 LT_BOOTROOT = 1 << 11,
78 LT_LBA = 1 << 12,
79 LT_OTHER = 1 << 13,
80 LT_GENERIC = 1 << 14,
81 LT_ECHO = 1 << 16,
82 LT_MENUENTRY = 1 << 17,
83 LT_ENTRY_END = 1 << 18,
84 LT_SET_VARIABLE = 1 << 19,
85 LT_UNKNOWN = 1 << 20,
86 };
87
88 struct singleLine {
89 char * indent;
90 int numElements;
91 struct lineElement * elements;
92 struct singleLine * next;
93 enum lineType_e type;
94 };
95
96 struct singleEntry {
97 struct singleLine * lines;
98 int skip;
99 int multiboot;
100 struct singleEntry * next;
101 };
102
103 #define GRUBBY_BADIMAGE_OKAY (1 << 0)
104
105 #define GRUB_CONFIG_NO_DEFAULT (1 << 0) /* don't write out default=0 */
106
107 /* These defines are (only) used in addNewKernel() */
108 #define NEED_KERNEL (1 << 0)
109 #define NEED_INITRD (1 << 1)
110 #define NEED_TITLE (1 << 2)
111 #define NEED_ARGS (1 << 3)
112 #define NEED_MB (1 << 4)
113 #define NEED_END (1 << 5)
114
115 #define MAIN_DEFAULT (1 << 0)
116 #define DEFAULT_SAVED -2
117 #define DEFAULT_SAVED_GRUB2 -3
118
119 struct keywordTypes {
120 char * key;
121 enum lineType_e type;
122 char nextChar;
123 char separatorChar;
124 };
125
126 struct configFileInfo;
127
128 typedef const char *(*findConfigFunc)(struct configFileInfo *);
129 typedef const int (*writeLineFunc)(struct configFileInfo *,
130 struct singleLine *line);
131
132 struct configFileInfo {
133 char * defaultConfig;
134 findConfigFunc findConfig;
135 writeLineFunc writeLine;
136 struct keywordTypes * keywords;
137 int defaultIsIndex;
138 int defaultIsVariable;
139 int defaultSupportSaved;
140 enum lineType_e entryStart;
141 enum lineType_e entryEnd;
142 int needsBootPrefix;
143 int argsInQuotes;
144 int maxTitleLength;
145 int titleBracketed;
146 int titlePosition;
147 int mbHyperFirst;
148 int mbInitRdIsModule;
149 int mbConcatArgs;
150 int mbAllowExtraInitRds;
151 };
152
153 struct keywordTypes grubKeywords[] = {
154 { "title", LT_TITLE, ' ' },
155 { "root", LT_BOOTROOT, ' ' },
156 { "default", LT_DEFAULT, ' ' },
157 { "fallback", LT_FALLBACK, ' ' },
158 { "kernel", LT_KERNEL, ' ' },
159 { "initrd", LT_INITRD, ' ', ' ' },
160 { "module", LT_MBMODULE, ' ' },
161 { "kernel", LT_HYPER, ' ' },
162 { NULL, 0, 0 },
163 };
164
165 const char *grubFindConfig(struct configFileInfo *cfi) {
166 static const char *configFiles[] = {
167 "/boot/grub/grub.conf",
168 "/boot/grub/menu.lst",
169 "/etc/grub.conf",
170 NULL
171 };
172 static int i = -1;
173
174 if (i == -1) {
175 for (i = 0; configFiles[i] != NULL; i++) {
176 dbgPrintf("Checking \"%s\": ", configFiles[i]);
177 if (!access(configFiles[i], R_OK)) {
178 dbgPrintf("found\n");
179 return configFiles[i];
180 }
181 dbgPrintf("not found\n");
182 }
183 }
184 return configFiles[i];
185 }
186
187 struct configFileInfo grubConfigType = {
188 .findConfig = grubFindConfig,
189 .keywords = grubKeywords,
190 .defaultIsIndex = 1,
191 .defaultSupportSaved = 1,
192 .entryStart = LT_TITLE,
193 .needsBootPrefix = 1,
194 .mbHyperFirst = 1,
195 .mbInitRdIsModule = 1,
196 .mbAllowExtraInitRds = 1,
197 };
198
199 struct keywordTypes grub2Keywords[] = {
200 { "menuentry", LT_MENUENTRY, ' ' },
201 { "}", LT_ENTRY_END, ' ' },
202 { "echo", LT_ECHO, ' ' },
203 { "set", LT_SET_VARIABLE,' ', '=' },
204 { "root", LT_BOOTROOT, ' ' },
205 { "default", LT_DEFAULT, ' ' },
206 { "fallback", LT_FALLBACK, ' ' },
207 { "linux", LT_KERNEL, ' ' },
208 { "initrd", LT_INITRD, ' ', ' ' },
209 { "module", LT_MBMODULE, ' ' },
210 { "kernel", LT_HYPER, ' ' },
211 { NULL, 0, 0 },
212 };
213
214 const char *grub2FindConfig(struct configFileInfo *cfi) {
215 static const char *configFiles[] = {
216 "/boot/grub/grub-efi.cfg",
217 "/boot/grub/grub.cfg",
218 NULL
219 };
220 static int i = -1;
221 static const char *grub_cfg = "/boot/grub/grub.cfg";
222
223 if (i == -1) {
224 for (i = 0; configFiles[i] != NULL; i++) {
225 dbgPrintf("Checking \"%s\": ", configFiles[i]);
226 if (!access(configFiles[i], R_OK)) {
227 dbgPrintf("found\n");
228 return configFiles[i];
229 }
230 }
231 }
232
233 /* Ubuntu renames grub2 to grub, so check for the grub.d directory
234 * that isn't in grub1, and if it exists, return the config file path
235 * that they use. */
236 if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) {
237 dbgPrintf("found\n");
238 return grub_cfg;
239 }
240
241 dbgPrintf("not found\n");
242 return configFiles[i];
243 }
244
245 int sizeOfSingleLine(struct singleLine * line) {
246 int count = 0;
247
248 for (int i = 0; i < line->numElements; i++) {
249 int indentSize = 0;
250
251 count = count + strlen(line->elements[i].item);
252
253 indentSize = strlen(line->elements[i].indent);
254 if (indentSize > 0)
255 count = count + indentSize;
256 else
257 /* be extra safe and add room for whitespaces */
258 count = count + 1;
259 }
260
261 /* room for trailing terminator */
262 count = count + 1;
263
264 return count;
265 }
266
267 static int isquote(char q)
268 {
269 if (q == '\'' || q == '\"')
270 return 1;
271 return 0;
272 }
273
274 char *grub2ExtractTitle(struct singleLine * line) {
275 char * current;
276 char * current_indent;
277 int current_len;
278 int current_indent_len;
279 int i;
280
281 /* bail out if line does not start with menuentry */
282 if (strcmp(line->elements[0].item, "menuentry"))
283 return NULL;
284
285 i = 1;
286 current = line->elements[i].item;
287 current_len = strlen(current);
288
289 /* if second word is quoted, strip the quotes and return single word */
290 if (isquote(*current) && isquote(current[current_len - 1])) {
291 char *tmp;
292
293 tmp = strdup(current);
294 *(tmp + current_len - 1) = '\0';
295 return ++tmp;
296 }
297
298 /* if no quotes, return second word verbatim */
299 if (!isquote(*current))
300 return current;
301
302 /* second element start with a quote, so we have to find the element
303 * whose last character is also quote (assuming it's the closing one) */
304 int resultMaxSize;
305 char * result;
306
307 resultMaxSize = sizeOfSingleLine(line);
308 result = malloc(resultMaxSize);
309 snprintf(result, resultMaxSize, "%s", ++current);
310
311 i++;
312 for (; i < line->numElements; ++i) {
313 current = line->elements[i].item;
314 current_len = strlen(current);
315 current_indent = line->elements[i].indent;
316 current_indent_len = strlen(current_indent);
317
318 strncat(result, current_indent, current_indent_len);
319 if (!isquote(current[current_len-1])) {
320 strncat(result, current, current_len);
321 } else {
322 strncat(result, current, current_len - 1);
323 break;
324 }
325 }
326 return result;
327 }
328
329 struct configFileInfo grub2ConfigType = {
330 .findConfig = grub2FindConfig,
331 .keywords = grub2Keywords,
332 .defaultIsIndex = 1,
333 .defaultSupportSaved = 1,
334 .defaultIsVariable = 1,
335 .entryStart = LT_MENUENTRY,
336 .entryEnd = LT_ENTRY_END,
337 .titlePosition = 1,
338 .needsBootPrefix = 1,
339 .mbHyperFirst = 1,
340 .mbInitRdIsModule = 1,
341 .mbAllowExtraInitRds = 1,
342 };
343
344 struct keywordTypes yabootKeywords[] = {
345 { "label", LT_TITLE, '=' },
346 { "root", LT_ROOT, '=' },
347 { "default", LT_DEFAULT, '=' },
348 { "image", LT_KERNEL, '=' },
349 { "bsd", LT_GENERIC, '=' },
350 { "macos", LT_GENERIC, '=' },
351 { "macosx", LT_GENERIC, '=' },
352 { "magicboot", LT_GENERIC, '=' },
353 { "darwin", LT_GENERIC, '=' },
354 { "timeout", LT_GENERIC, '=' },
355 { "install", LT_GENERIC, '=' },
356 { "fstype", LT_GENERIC, '=' },
357 { "hfstype", LT_GENERIC, '=' },
358 { "delay", LT_GENERIC, '=' },
359 { "defaultos", LT_GENERIC, '=' },
360 { "init-message", LT_GENERIC, '=' },
361 { "enablecdboot", LT_GENERIC, ' ' },
362 { "enableofboot", LT_GENERIC, ' ' },
363 { "enablenetboot", LT_GENERIC, ' ' },
364 { "nonvram", LT_GENERIC, ' ' },
365 { "hide", LT_GENERIC, ' ' },
366 { "protect", LT_GENERIC, ' ' },
367 { "nobless", LT_GENERIC, ' ' },
368 { "nonvram", LT_GENERIC, ' ' },
369 { "brokenosx", LT_GENERIC, ' ' },
370 { "usemount", LT_GENERIC, ' ' },
371 { "mntpoint", LT_GENERIC, '=' },
372 { "partition", LT_GENERIC, '=' },
373 { "device", LT_GENERIC, '=' },
374 { "fstype", LT_GENERIC, '=' },
375 { "initrd", LT_INITRD, '=', ';' },
376 { "append", LT_KERNELARGS, '=' },
377 { "boot", LT_BOOT, '=' },
378 { "lba", LT_LBA, ' ' },
379 { NULL, 0, 0 },
380 };
381
382 struct keywordTypes liloKeywords[] = {
383 { "label", LT_TITLE, '=' },
384 { "root", LT_ROOT, '=' },
385 { "default", LT_DEFAULT, '=' },
386 { "image", LT_KERNEL, '=' },
387 { "other", LT_OTHER, '=' },
388 { "initrd", LT_INITRD, '=' },
389 { "append", LT_KERNELARGS, '=' },
390 { "boot", LT_BOOT, '=' },
391 { "lba", LT_LBA, ' ' },
392 { NULL, 0, 0 },
393 };
394
395 struct keywordTypes eliloKeywords[] = {
396 { "label", LT_TITLE, '=' },
397 { "root", LT_ROOT, '=' },
398 { "default", LT_DEFAULT, '=' },
399 { "image", LT_KERNEL, '=' },
400 { "initrd", LT_INITRD, '=' },
401 { "append", LT_KERNELARGS, '=' },
402 { "vmm", LT_HYPER, '=' },
403 { NULL, 0, 0 },
404 };
405
406 struct keywordTypes siloKeywords[] = {
407 { "label", LT_TITLE, '=' },
408 { "root", LT_ROOT, '=' },
409 { "default", LT_DEFAULT, '=' },
410 { "image", LT_KERNEL, '=' },
411 { "other", LT_OTHER, '=' },
412 { "initrd", LT_INITRD, '=' },
413 { "append", LT_KERNELARGS, '=' },
414 { "boot", LT_BOOT, '=' },
415 { NULL, 0, 0 },
416 };
417
418 struct keywordTypes ziplKeywords[] = {
419 { "target", LT_BOOTROOT, '=' },
420 { "image", LT_KERNEL, '=' },
421 { "ramdisk", LT_INITRD, '=' },
422 { "parameters", LT_KERNELARGS, '=' },
423 { "default", LT_DEFAULT, '=' },
424 { NULL, 0, 0 },
425 };
426
427 struct keywordTypes extlinuxKeywords[] = {
428 { "label", LT_TITLE, ' ' },
429 { "root", LT_ROOT, ' ' },
430 { "default", LT_DEFAULT, ' ' },
431 { "kernel", LT_KERNEL, ' ' },
432 { "initrd", LT_INITRD, ' ', ',' },
433 { "append", LT_KERNELARGS, ' ' },
434 { "prompt", LT_UNKNOWN, ' ' },
435 { NULL, 0, 0 },
436 };
437 int useextlinuxmenu;
438 struct configFileInfo eliloConfigType = {
439 .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf",
440 .keywords = eliloKeywords,
441 .entryStart = LT_KERNEL,
442 .needsBootPrefix = 1,
443 .argsInQuotes = 1,
444 .mbConcatArgs = 1,
445 };
446
447 struct configFileInfo liloConfigType = {
448 .defaultConfig = "/etc/lilo.conf",
449 .keywords = liloKeywords,
450 .entryStart = LT_KERNEL,
451 .argsInQuotes = 1,
452 .maxTitleLength = 15,
453 };
454
455 struct configFileInfo yabootConfigType = {
456 .defaultConfig = "/etc/yaboot.conf",
457 .keywords = yabootKeywords,
458 .entryStart = LT_KERNEL,
459 .needsBootPrefix = 1,
460 .argsInQuotes = 1,
461 .maxTitleLength = 15,
462 .mbAllowExtraInitRds = 1,
463 };
464
465 struct configFileInfo siloConfigType = {
466 .defaultConfig = "/etc/silo.conf",
467 .keywords = siloKeywords,
468 .entryStart = LT_KERNEL,
469 .needsBootPrefix = 1,
470 .argsInQuotes = 1,
471 .maxTitleLength = 15,
472 };
473
474 struct configFileInfo ziplConfigType = {
475 .defaultConfig = "/etc/zipl.conf",
476 .keywords = ziplKeywords,
477 .entryStart = LT_TITLE,
478 .argsInQuotes = 1,
479 .titleBracketed = 1,
480 };
481
482 struct configFileInfo extlinuxConfigType = {
483 .defaultConfig = "/boot/extlinux/extlinux.conf",
484 .keywords = extlinuxKeywords,
485 .entryStart = LT_TITLE,
486 .needsBootPrefix = 1,
487 .maxTitleLength = 255,
488 .mbAllowExtraInitRds = 1,
489 };
490
491 struct grubConfig {
492 struct singleLine * theLines;
493 struct singleEntry * entries;
494 char * primaryIndent;
495 char * secondaryIndent;
496 int defaultImage; /* -1 if none specified -- this value is
497 * written out, overriding original */
498 int fallbackImage; /* just like defaultImage */
499 int flags;
500 struct configFileInfo * cfi;
501 };
502
503 blkid_cache blkid;
504
505 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);
506 struct singleEntry * findEntryByPath(struct grubConfig * cfg,
507 const char * path, const char * prefix,
508 int * index);
509 static int readFile(int fd, char ** bufPtr);
510 static void lineInit(struct singleLine * line);
511 struct singleLine * lineDup(struct singleLine * line);
512 static void lineFree(struct singleLine * line);
513 static int lineWrite(FILE * out, struct singleLine * line,
514 struct configFileInfo * cfi);
515 static int getNextLine(char ** bufPtr, struct singleLine * line,
516 struct configFileInfo * cfi);
517 static char * getRootSpecifier(char * str);
518 static void requote(struct singleLine *line, struct configFileInfo * cfi);
519 static void insertElement(struct singleLine * line,
520 const char * item, int insertHere,
521 struct configFileInfo * cfi);
522 static void removeElement(struct singleLine * line, int removeHere);
523 static struct keywordTypes * getKeywordByType(enum lineType_e type,
524 struct configFileInfo * cfi);
525 static enum lineType_e getTypeByKeyword(char * keyword,
526 struct configFileInfo * cfi);
527 static struct singleLine * getLineByType(enum lineType_e type,
528 struct singleLine * line);
529 static int checkForExtLinux(struct grubConfig * config);
530 struct singleLine * addLineTmpl(struct singleEntry * entry,
531 struct singleLine * tmplLine,
532 struct singleLine * prevLine,
533 const char * val,
534 struct configFileInfo * cfi);
535 struct singleLine * addLine(struct singleEntry * entry,
536 struct configFileInfo * cfi,
537 enum lineType_e type, char * defaultIndent,
538 const char * val);
539
540 static char * sdupprintf(const char *format, ...)
541 #ifdef __GNUC__
542 __attribute__ ((format (printf, 1, 2)));
543 #else
544 ;
545 #endif
546
547 static char * sdupprintf(const char *format, ...) {
548 char *buf = NULL;
549 char c;
550 va_list args;
551 size_t size = 0;
552 va_start(args, format);
553
554 /* XXX requires C99 vsnprintf behavior */
555 size = vsnprintf(&c, 1, format, args) + 1;
556 if (size == -1) {
557 printf("ERROR: vsnprintf behavior is not C99\n");
558 abort();
559 }
560
561 va_end(args);
562 va_start(args, format);
563
564 buf = malloc(size);
565 if (buf == NULL)
566 return NULL;
567 vsnprintf(buf, size, format, args);
568 va_end (args);
569
570 return buf;
571 }
572
573 static struct keywordTypes * getKeywordByType(enum lineType_e type,
574 struct configFileInfo * cfi) {
575 for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
576 if (kw->type == type)
577 return kw;
578 }
579 return NULL;
580 }
581
582 static char *getKeyByType(enum lineType_e type, struct configFileInfo * cfi) {
583 struct keywordTypes *kt = getKeywordByType(type, cfi);
584 if (kt)
585 return kt->key;
586 return "unknown";
587 }
588
589 static char * getpathbyspec(char *device) {
590 if (!blkid)
591 blkid_get_cache(&blkid, NULL);
592
593 return blkid_get_devname(blkid, device, NULL);
594 }
595
596 static char * getuuidbydev(char *device) {
597 if (!blkid)
598 blkid_get_cache(&blkid, NULL);
599
600 return blkid_get_tag_value(blkid, "UUID", device);
601 }
602
603 static enum lineType_e getTypeByKeyword(char * keyword,
604 struct configFileInfo * cfi) {
605 for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
606 if (!strcmp(keyword, kw->key))
607 return kw->type;
608 }
609 return LT_UNKNOWN;
610 }
611
612 static struct singleLine * getLineByType(enum lineType_e type,
613 struct singleLine * line) {
614 dbgPrintf("getLineByType(%d): ", type);
615 for (; line; line = line->next) {
616 dbgPrintf("%d:%s ", line->type,
617 line->numElements ? line->elements[0].item : "(empty)");
618 if (line->type & type) break;
619 }
620 dbgPrintf(line ? "\n" : " (failed)\n");
621 return line;
622 }
623
624 static int isBracketedTitle(struct singleLine * line) {
625 if (line->numElements == 1 && *line->elements[0].item == '[') {
626 int len = strlen(line->elements[0].item);
627 if (*(line->elements[0].item + len - 1) == ']') {
628 /* FIXME: this is a hack... */
629 if (strcmp(line->elements[0].item, "[defaultboot]")) {
630 return 1;
631 }
632 }
633 }
634 return 0;
635 }
636
637 static int isEntryStart(struct singleLine * line,
638 struct configFileInfo * cfi) {
639 return line->type == cfi->entryStart || line->type == LT_OTHER ||
640 (cfi->titleBracketed && isBracketedTitle(line));
641 }
642
643 /* extract the title from within brackets (for zipl) */
644 static char * extractTitle(struct singleLine * line) {
645 /* bracketed title... let's extract it (leaks a byte) */
646 char * title;
647 title = strdup(line->elements[0].item);
648 title++;
649 *(title + strlen(title) - 1) = '\0';
650 return title;
651 }
652
653 static int readFile(int fd, char ** bufPtr) {
654 int alloced = 0, size = 0, i = 0;
655 char * buf = NULL;
656
657 do {
658 size += i;
659 if ((size + 1024) > alloced) {
660 alloced += 4096;
661 buf = realloc(buf, alloced + 1);
662 }
663 } while ((i = read(fd, buf + size, 1024)) > 0);
664
665 if (i < 0) {
666 fprintf(stderr, _("error reading input: %s\n"), strerror(errno));
667 free(buf);
668 return 1;
669 }
670
671 buf = realloc(buf, size + 2);
672 if (size == 0)
673 buf[size++] = '\n';
674 else
675 if (buf[size - 1] != '\n')
676 buf[size++] = '\n';
677 buf[size] = '\0';
678
679 *bufPtr = buf;
680
681 return 0;
682 }
683
684 static void lineInit(struct singleLine * line) {
685 line->indent = NULL;
686 line->elements = NULL;
687 line->numElements = 0;
688 line->next = NULL;
689 }
690
691 struct singleLine * lineDup(struct singleLine * line) {
692 struct singleLine * newLine = malloc(sizeof(*newLine));
693
694 newLine->indent = strdup(line->indent);
695 newLine->next = NULL;
696 newLine->type = line->type;
697 newLine->numElements = line->numElements;
698 newLine->elements = malloc(sizeof(*newLine->elements) *
699 newLine->numElements);
700
701 for (int i = 0; i < newLine->numElements; i++) {
702 newLine->elements[i].indent = strdup(line->elements[i].indent);
703 newLine->elements[i].item = strdup(line->elements[i].item);
704 }
705
706 return newLine;
707 }
708
709 static void lineFree(struct singleLine * line) {
710 if (line->indent) free(line->indent);
711
712 for (int i = 0; i < line->numElements; i++) {
713 free(line->elements[i].item);
714 free(line->elements[i].indent);
715 }
716
717 if (line->elements) free(line->elements);
718 lineInit(line);
719 }
720
721 static int lineWrite(FILE * out, struct singleLine * line,
722 struct configFileInfo * cfi) {
723 if (fprintf(out, "%s", line->indent) == -1) return -1;
724
725 for (int i = 0; i < line->numElements; i++) {
726 /* Need to handle this, because we strip the quotes from
727 * menuentry when read it. */
728 if (line->type == LT_MENUENTRY && i == 1) {
729 if(!isquote(*line->elements[i].item))
730 fprintf(out, "\'%s\'", line->elements[i].item);
731 else
732 fprintf(out, "%s", line->elements[i].item);
733 fprintf(out, "%s", line->elements[i].indent);
734
735 continue;
736 }
737
738 if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)
739 if (fputc('"', out) == EOF) return -1;
740
741 if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;
742 if (i < line->numElements - 1)
743 if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;
744 }
745
746 if (line->type == LT_KERNELARGS && cfi->argsInQuotes)
747 if (fputc('"', out) == EOF) return -1;
748
749 if (fprintf(out, "\n") == -1) return -1;
750
751 return 0;
752 }
753
754 /* we've guaranteed that the buffer ends w/ \n\0 */
755 static int getNextLine(char ** bufPtr, struct singleLine * line,
756 struct configFileInfo * cfi) {
757 char * end;
758 char * start = *bufPtr;
759 char * chptr;
760 int elementsAlloced = 0;
761 struct lineElement * element;
762 int first = 1;
763
764 lineFree(line);
765
766 end = strchr(start, '\n');
767 *end = '\0';
768 *bufPtr = end + 1;
769
770 for (chptr = start; *chptr && isspace(*chptr); chptr++) ;
771
772 line->indent = strndup(start, chptr - start);
773 start = chptr;
774
775 while (start < end) {
776 /* we know !isspace(*start) */
777
778 if (elementsAlloced == line->numElements) {
779 elementsAlloced += 5;
780 line->elements = realloc(line->elements,
781 sizeof(*line->elements) * elementsAlloced);
782 }
783
784 element = line->elements + line->numElements;
785
786 chptr = start;
787 while (*chptr && !isspace(*chptr)) {
788 if (first && *chptr == '=') break;
789 chptr++;
790 }
791 element->item = strndup(start, chptr - start);
792 start = chptr;
793
794 /* lilo actually accepts the pathological case of append = " foo " */
795 if (*start == '=')
796 chptr = start + 1;
797 else
798 chptr = start;
799
800 do {
801 for (; *chptr && isspace(*chptr); chptr++);
802 if (*chptr == '=')
803 chptr = chptr + 1;
804 } while (isspace(*chptr));
805
806 element->indent = strndup(start, chptr - start);
807 start = chptr;
808
809 line->numElements++;
810 first = 0;
811 }
812
813 if (!line->numElements)
814 line->type = LT_WHITESPACE;
815 else {
816 line->type = getTypeByKeyword(line->elements[0].item, cfi);
817 if (line->type == LT_UNKNOWN) {
818 /* zipl does [title] instead of something reasonable like all
819 * the other boot loaders. kind of ugly */
820 if (cfi->titleBracketed && isBracketedTitle(line)) {
821 line->type = LT_TITLE;
822 }
823
824 /* this is awkward, but we need to be able to handle keywords
825 that begin with a # (specifically for #boot in grub.conf),
826 but still make comments lines with no elements (everything
827 stored in the indent */
828 if (*line->elements[0].item == '#') {
829 char * fullLine;
830 int len;
831
832 len = strlen(line->indent);
833 for (int i = 0; i < line->numElements; i++)
834 len += strlen(line->elements[i].item) +
835 strlen(line->elements[i].indent);
836
837 fullLine = malloc(len + 1);
838 strcpy(fullLine, line->indent);
839 free(line->indent);
840 line->indent = fullLine;
841
842 for (int i = 0; i < line->numElements; i++) {
843 strcat(fullLine, line->elements[i].item);
844 strcat(fullLine, line->elements[i].indent);
845 free(line->elements[i].item);
846 free(line->elements[i].indent);
847 }
848
849 line->type = LT_WHITESPACE;
850 line->numElements = 0;
851 }
852 } else {
853 struct keywordTypes *kw;
854
855 kw = getKeywordByType(line->type, cfi);
856
857 /* space isn't the only separator, we need to split
858 * elements up more
859 */
860 if (!isspace(kw->separatorChar)) {
861 char indent[2] = "";
862 indent[0] = kw->separatorChar;
863 for (int i = 1; i < line->numElements; i++) {
864 char *p;
865 int numNewElements;
866
867 numNewElements = 0;
868 p = line->elements[i].item;
869 while (*p != '\0') {
870 if (*p == kw->separatorChar)
871 numNewElements++;
872 p++;
873 }
874 if (line->numElements + numNewElements >= elementsAlloced) {
875 elementsAlloced += numNewElements + 5;
876 line->elements = realloc(line->elements,
877 sizeof(*line->elements) * elementsAlloced);
878 }
879
880 for (int j = line->numElements; j > i; j--) {
881 line->elements[j + numNewElements] = line->elements[j];
882 }
883 line->numElements += numNewElements;
884
885 p = line->elements[i].item;
886 while (*p != '\0') {
887
888 while (*p != kw->separatorChar && *p != '\0') p++;
889 if (*p == '\0') {
890 break;
891 }
892
893 line->elements[i + 1].indent = line->elements[i].indent;
894 line->elements[i].indent = strdup(indent);
895 *p++ = '\0';
896 i++;
897 line->elements[i].item = strdup(p);
898 }
899 }
900 }
901 }
902 }
903
904 return 0;
905 }
906
907 static struct grubConfig * readConfig(const char * inName,
908 struct configFileInfo * cfi) {
909 int in;
910 char * incoming = NULL, * head;
911 int rc;
912 int sawEntry = 0;
913 int movedLine = 0;
914 struct grubConfig * cfg;
915 struct singleLine * last = NULL, * line, * defaultLine = NULL;
916 char * end;
917 struct singleEntry * entry = NULL;
918 int len;
919 char * buf;
920
921 if (!strcmp(inName, "-")) {
922 in = 0;
923 } else {
924 if ((in = open(inName, O_RDONLY)) < 0) {
925 fprintf(stderr, _("error opening %s for read: %s\n"),
926 inName, strerror(errno));
927 return NULL;
928 }
929 }
930
931 rc = readFile(in, &incoming);
932 close(in);
933 if (rc) return NULL;
934
935 head = incoming;
936 cfg = malloc(sizeof(*cfg));
937 cfg->primaryIndent = strdup("");
938 cfg->secondaryIndent = strdup("\t");
939 cfg->flags = GRUB_CONFIG_NO_DEFAULT;
940 cfg->cfi = cfi;
941 cfg->theLines = NULL;
942 cfg->entries = NULL;
943 cfg->fallbackImage = 0;
944
945 /* copy everything we have */
946 while (*head) {
947 line = malloc(sizeof(*line));
948 lineInit(line);
949
950 if (getNextLine(&head, line, cfi)) {
951 free(line);
952 /* XXX memory leak of everything in cfg */
953 return NULL;
954 }
955
956 if (!sawEntry && line->numElements) {
957 free(cfg->primaryIndent);
958 cfg->primaryIndent = strdup(line->indent);
959 } else if (line->numElements) {
960 free(cfg->secondaryIndent);
961 cfg->secondaryIndent = strdup(line->indent);
962 }
963
964 if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
965 sawEntry = 1;
966 if (!entry) {
967 cfg->entries = malloc(sizeof(*entry));
968 entry = cfg->entries;
969 } else {
970 entry->next = malloc(sizeof(*entry));
971 entry = entry->next;
972 }
973
974 entry->skip = 0;
975 entry->multiboot = 0;
976 entry->lines = NULL;
977 entry->next = NULL;
978 }
979
980 if (line->type == LT_SET_VARIABLE) {
981 dbgPrintf("found 'set' command (%d elements): ", line->numElements);
982 dbgPrintf("%s", line->indent);
983 for (int i = 0; i < line->numElements; i++)
984 dbgPrintf("\"%s\"%s", line->elements[i].item, line->elements[i].indent);
985 dbgPrintf("\n");
986 struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi);
987 if (kwType && line->numElements == 3 &&
988 !strcmp(line->elements[1].item, kwType->key)) {
989 dbgPrintf("Line sets default config\n");
990 cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
991 defaultLine = line;
992 }
993 } else if (line->type == LT_DEFAULT && line->numElements == 2) {
994 cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
995 defaultLine = line;
996
997 } else if (line->type == LT_KERNEL) {
998 /* if by some freak chance this is multiboot and the "module"
999 * lines came earlier in the template, make sure to use LT_HYPER
1000 * instead of LT_KERNEL now
1001 */
1002 if (entry->multiboot)
1003 line->type = LT_HYPER;
1004
1005 } else if (line->type == LT_MBMODULE) {
1006 /* go back and fix the LT_KERNEL line to indicate LT_HYPER
1007 * instead, now that we know this is a multiboot entry.
1008 * This only applies to grub, but that's the only place we
1009 * should find LT_MBMODULE lines anyway.
1010 */
1011 for (struct singleLine *l = entry->lines; l; l = l->next) {
1012 if (l->type == LT_HYPER)
1013 break;
1014 else if (l->type == LT_KERNEL) {
1015 l->type = LT_HYPER;
1016 break;
1017 }
1018 }
1019 entry->multiboot = 1;
1020
1021 } else if (line->type == LT_HYPER) {
1022 entry->multiboot = 1;
1023
1024 } else if (line->type == LT_FALLBACK && line->numElements == 2) {
1025 cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);
1026 if (*end) cfg->fallbackImage = -1;
1027
1028 } else if (line->type == LT_TITLE && line->numElements > 1) {
1029 /* make the title a single argument (undoing our parsing) */
1030 len = 0;
1031 for (int i = 1; i < line->numElements; i++) {
1032 len += strlen(line->elements[i].item);
1033 len += strlen(line->elements[i].indent);
1034 }
1035 buf = malloc(len + 1);
1036 *buf = '\0';
1037
1038 for (int i = 1; i < line->numElements; i++) {
1039 strcat(buf, line->elements[i].item);
1040 free(line->elements[i].item);
1041
1042 if ((i + 1) != line->numElements) {
1043 strcat(buf, line->elements[i].indent);
1044 free(line->elements[i].indent);
1045 }
1046 }
1047
1048 line->elements[1].indent =
1049 line->elements[line->numElements - 1].indent;
1050 line->elements[1].item = buf;
1051 line->numElements = 2;
1052 } else if (line->type == LT_MENUENTRY && line->numElements > 3) {
1053 /* let --remove-kernel="TITLE=what" work */
1054 len = 0;
1055 char *extras;
1056 char *title;
1057
1058 for (int i = 1; i < line->numElements; i++) {
1059 len += strlen(line->elements[i].item);
1060 len += strlen(line->elements[i].indent);
1061 }
1062 buf = malloc(len + 1);
1063 *buf = '\0';
1064
1065 /* allocate mem for extra flags. */
1066 extras = malloc(len + 1);
1067 *extras = '\0';
1068
1069 /* get title. */
1070 for (int i = 0; i < line->numElements; i++) {
1071 if (!strcmp(line->elements[i].item, "menuentry"))
1072 continue;
1073 if (isquote(*line->elements[i].item))
1074 title = line->elements[i].item + 1;
1075 else
1076 title = line->elements[i].item;
1077
1078 len = strlen(title);
1079 if (isquote(title[len-1])) {
1080 strncat(buf, title,len-1);
1081 break;
1082 } else {
1083 strcat(buf, title);
1084 strcat(buf, line->elements[i].indent);
1085 }
1086 }
1087
1088 /* get extras */
1089 int count = 0;
1090 for (int i = 0; i < line->numElements; i++) {
1091 if (count >= 2) {
1092 strcat(extras, line->elements[i].item);
1093 strcat(extras, line->elements[i].indent);
1094 }
1095
1096 if (!strcmp(line->elements[i].item, "menuentry"))
1097 continue;
1098
1099 /* count ' or ", there should be two in menuentry line. */
1100 if (isquote(*line->elements[i].item))
1101 count++;
1102
1103 len = strlen(line->elements[i].item);
1104
1105 if (isquote(line->elements[i].item[len -1]))
1106 count++;
1107
1108 /* ok, we get the final ' or ", others are extras. */
1109 }
1110 line->elements[1].indent =
1111 line->elements[line->numElements - 2].indent;
1112 line->elements[1].item = buf;
1113 line->elements[2].indent =
1114 line->elements[line->numElements - 2].indent;
1115 line->elements[2].item = extras;
1116 line->numElements = 3;
1117 } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {
1118 /* Strip off any " which may be present; they'll be put back
1119 on write. This is one of the few (the only?) places that grubby
1120 canonicalizes the output */
1121
1122 if (line->numElements >= 2) {
1123 int last, len;
1124
1125 if (isquote(*line->elements[1].item))
1126 memmove(line->elements[1].item, line->elements[1].item + 1,
1127 strlen(line->elements[1].item + 1) + 1);
1128
1129 last = line->numElements - 1;
1130 len = strlen(line->elements[last].item) - 1;
1131 if (isquote(line->elements[last].item[len]))
1132 line->elements[last].item[len] = '\0';
1133 }
1134 }
1135
1136 /* If we find a generic config option which should live at the
1137 top of the file, move it there. Old versions of grubby were
1138 probably responsible for putting new images in the wrong
1139 place in front of it anyway. */
1140 if (sawEntry && line->type == LT_GENERIC) {
1141 struct singleLine **l = &cfg->theLines;
1142 struct singleLine **last_nonws = &cfg->theLines;
1143 while (*l) {
1144 if ((*l)->type != LT_WHITESPACE)
1145 last_nonws = &((*l)->next);
1146 l = &((*l)->next);
1147 }
1148 line->next = *last_nonws;
1149 *last_nonws = line;
1150 movedLine = 1;
1151 continue; /* without setting 'last' */
1152 }
1153
1154 /* If a second line of whitespace happens after a generic option
1155 which was moved, drop it. */
1156 if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {
1157 lineFree(line);
1158 free(line);
1159 movedLine = 0;
1160 continue;
1161 }
1162 movedLine = 0;
1163
1164 if (sawEntry) {
1165 if (!entry->lines)
1166 entry->lines = line;
1167 else
1168 last->next = line;
1169 dbgPrintf("readConfig added %s to %p\n", getKeyByType(line->type, cfi), entry);
1170
1171 /* we could have seen this outside of an entry... if so, we
1172 * ignore it like any other line we don't grok */
1173 if (line->type == LT_ENTRY_END && sawEntry)
1174 sawEntry = 0;
1175 } else {
1176 if (!cfg->theLines)
1177 cfg->theLines = line;
1178 else
1179 last->next = line;
1180 dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi));
1181 }
1182
1183 last = line;
1184 }
1185
1186 free(incoming);
1187
1188 dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");
1189 if (defaultLine) {
1190 if (defaultLine->numElements > 2 &&
1191 cfi->defaultSupportSaved &&
1192 !strncmp(defaultLine->elements[2].item,"\"${saved_entry}\"", 16)) {
1193 cfg->defaultImage = DEFAULT_SAVED_GRUB2;
1194 } else if (cfi->defaultIsVariable) {
1195 char *value = defaultLine->elements[2].item;
1196 while (*value && (*value == '"' || *value == '\'' ||
1197 *value == ' ' || *value == '\t'))
1198 value++;
1199 cfg->defaultImage = strtol(value, &end, 10);
1200 while (*end && (*end == '"' || *end == '\'' ||
1201 *end == ' ' || *end == '\t'))
1202 end++;
1203 if (*end) cfg->defaultImage = -1;
1204 } else if (cfi->defaultSupportSaved &&
1205 !strncmp(defaultLine->elements[1].item, "saved", 5)) {
1206 cfg->defaultImage = DEFAULT_SAVED;
1207 } else if (cfi->defaultIsIndex) {
1208 cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);
1209 if (*end) cfg->defaultImage = -1;
1210 } else if (defaultLine->numElements >= 2) {
1211 int i = 0;
1212 while ((entry = findEntryByIndex(cfg, i))) {
1213 for (line = entry->lines; line; line = line->next)
1214 if (line->type == LT_TITLE) break;
1215
1216 if (!cfi->titleBracketed) {
1217 if (line && (line->numElements >= 2) &&
1218 !strcmp(defaultLine->elements[1].item,
1219 line->elements[1].item)) break;
1220 } else if (line) {
1221 if (!strcmp(defaultLine->elements[1].item,
1222 extractTitle(line))) break;
1223 }
1224 i++;
1225 entry = NULL;
1226 }
1227
1228 if (entry){
1229 cfg->defaultImage = i;
1230 }else{
1231 cfg->defaultImage = -1;
1232 }
1233 }
1234 } else {
1235 cfg->defaultImage = 0;
1236 }
1237
1238 return cfg;
1239 }
1240
1241 static void writeDefault(FILE * out, char * indent,
1242 char * separator, struct grubConfig * cfg) {
1243 struct singleEntry * entry;
1244 struct singleLine * line;
1245 int i;
1246
1247 if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT) return;
1248
1249 if (cfg->defaultImage == DEFAULT_SAVED)
1250 fprintf(out, "%sdefault%ssaved\n", indent, separator);
1251 else if (cfg->defaultImage == DEFAULT_SAVED_GRUB2)
1252 fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1253 else if (cfg->defaultImage > -1) {
1254 if (cfg->cfi->defaultIsIndex) {
1255 if (cfg->cfi->defaultIsVariable) {
1256 fprintf(out, "%sset default=\"%d\"\n", indent,
1257 cfg->defaultImage);
1258 } else {
1259 fprintf(out, "%sdefault%s%d\n", indent, separator,
1260 cfg->defaultImage);
1261 }
1262 } else {
1263 int image = cfg->defaultImage;
1264
1265 entry = cfg->entries;
1266 while (entry && entry->skip) entry = entry->next;
1267
1268 i = 0;
1269 while (entry && i < image) {
1270 entry = entry->next;
1271
1272 while (entry && entry->skip) entry = entry->next;
1273 i++;
1274 }
1275
1276 if (!entry) return;
1277
1278 line = getLineByType(LT_TITLE, entry->lines);
1279
1280 if (line && line->numElements >= 2)
1281 fprintf(out, "%sdefault%s%s\n", indent, separator,
1282 line->elements[1].item);
1283 else if (line && (line->numElements == 1) &&
1284 cfg->cfi->titleBracketed) {
1285 fprintf(out, "%sdefault%s%s\n", indent, separator,
1286 extractTitle(line));
1287 }
1288 }
1289 }
1290 }
1291
1292 static int writeConfig(struct grubConfig * cfg, char * outName,
1293 const char * prefix) {
1294 FILE * out;
1295 struct singleLine * line;
1296 struct singleEntry * entry;
1297 char * tmpOutName;
1298 int needs = MAIN_DEFAULT;
1299 struct stat sb;
1300 int i;
1301
1302 if (!strcmp(outName, "-")) {
1303 out = stdout;
1304 tmpOutName = NULL;
1305 } else {
1306 if (!lstat(outName, &sb) && S_ISLNK(sb.st_mode)) {
1307 char * buf;
1308 int len = 256;
1309 int rc;
1310
1311 /* most likely the symlink is relative, so change our
1312 directory to the dir of the symlink */
1313 char *dir = strdupa(outName);
1314 rc = chdir(dirname(dir));
1315 free(dir);
1316 do {
1317 buf = alloca(len + 1);
1318 rc = readlink(basename(outName), buf, len);
1319 if (rc == len) len += 256;
1320 } while (rc == len);
1321
1322 if (rc < 0) {
1323 fprintf(stderr, _("grubby: error readlink link %s: %s\n"),
1324 outName, strerror(errno));
1325 return 1;
1326 }
1327
1328 outName = buf;
1329 outName[rc] = '\0';
1330 }
1331
1332 tmpOutName = alloca(strlen(outName) + 2);
1333 sprintf(tmpOutName, "%s-", outName);
1334 out = fopen(tmpOutName, "w");
1335 if (!out) {
1336 fprintf(stderr, _("grubby: error creating %s: %s\n"), tmpOutName,
1337 strerror(errno));
1338 return 1;
1339 }
1340
1341 if (!stat(outName, &sb)) {
1342 if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1343 fprintf(stderr, _("grubby: error setting perms on %s: %s\n"),
1344 tmpOutName, strerror(errno));
1345 fclose(out);
1346 unlink(tmpOutName);
1347 return 1;
1348 }
1349 }
1350 }
1351
1352 line = cfg->theLines;
1353 struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1354 while (line) {
1355 if (line->type == LT_SET_VARIABLE && defaultKw &&
1356 line->numElements == 3 &&
1357 !strcmp(line->elements[1].item, defaultKw->key)) {
1358 writeDefault(out, line->indent, line->elements[0].indent, cfg);
1359 needs &= ~MAIN_DEFAULT;
1360 } else if (line->type == LT_DEFAULT) {
1361 writeDefault(out, line->indent, line->elements[0].indent, cfg);
1362 needs &= ~MAIN_DEFAULT;
1363 } else if (line->type == LT_FALLBACK) {
1364 if (cfg->fallbackImage > -1)
1365 fprintf(out, "%s%s%s%d\n", line->indent,
1366 line->elements[0].item, line->elements[0].indent,
1367 cfg->fallbackImage);
1368 } else {
1369 if (lineWrite(out, line, cfg->cfi) == -1) {
1370 fprintf(stderr, _("grubby: error writing %s: %s\n"),
1371 tmpOutName, strerror(errno));
1372 fclose(out);
1373 unlink(tmpOutName);
1374 return 1;
1375 }
1376 }
1377
1378 line = line->next;
1379 }
1380
1381 if (needs & MAIN_DEFAULT) {
1382 writeDefault(out, cfg->primaryIndent, "=", cfg);
1383 needs &= ~MAIN_DEFAULT;
1384 }
1385
1386 i = 0;
1387 while ((entry = findEntryByIndex(cfg, i++))) {
1388 if (entry->skip) continue;
1389
1390 line = entry->lines;
1391 while (line) {
1392 if (lineWrite(out, line, cfg->cfi) == -1) {
1393 fprintf(stderr, _("grubby: error writing %s: %s\n"),
1394 tmpOutName, strerror(errno));
1395 fclose(out);
1396 unlink(tmpOutName);
1397 return 1;
1398 }
1399 line = line->next;
1400 }
1401 }
1402
1403 if (tmpOutName) {
1404 if (rename(tmpOutName, outName)) {
1405 fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),
1406 tmpOutName, outName, strerror(errno));
1407 unlink(outName);
1408 return 1;
1409 }
1410 }
1411
1412 return 0;
1413 }
1414
1415 static int numEntries(struct grubConfig *cfg) {
1416 int i = 0;
1417 struct singleEntry * entry;
1418
1419 entry = cfg->entries;
1420 while (entry) {
1421 if (!entry->skip)
1422 i++;
1423 entry = entry->next;
1424 }
1425 return i;
1426 }
1427
1428 static char *findDiskForRoot()
1429 {
1430 int fd;
1431 char buf[65536];
1432 char *devname;
1433 char *chptr;
1434 int rc;
1435
1436 if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1437 fprintf(stderr, "grubby: failed to open %s: %s\n",
1438 _PATH_MOUNTED, strerror(errno));
1439 return NULL;
1440 }
1441
1442 rc = read(fd, buf, sizeof(buf) - 1);
1443 if (rc <= 0) {
1444 fprintf(stderr, "grubby: failed to read %s: %s\n",
1445 _PATH_MOUNTED, strerror(errno));
1446 close(fd);
1447 return NULL;
1448 }
1449 close(fd);
1450 buf[rc] = '\0';
1451 chptr = buf;
1452
1453 char *foundanswer = NULL;
1454
1455 while (chptr && chptr != buf+rc) {
1456 devname = chptr;
1457
1458 /*
1459 * The first column of a mtab entry is the device, but if the entry is a
1460 * special device it won't start with /, so move on to the next line.
1461 */
1462 if (*devname != '/') {
1463 chptr = strchr(chptr, '\n');
1464 if (chptr)
1465 chptr++;
1466 continue;
1467 }
1468
1469 /* Seek to the next space */
1470 chptr = strchr(chptr, ' ');
1471 if (!chptr) {
1472 fprintf(stderr, "grubby: error parsing %s: %s\n",
1473 _PATH_MOUNTED, strerror(errno));
1474 return NULL;
1475 }
1476
1477 /*
1478 * The second column of a mtab entry is the mount point, we are looking
1479 * for '/' obviously.
1480 */
1481 if (*(++chptr) == '/' && *(++chptr) == ' ') {
1482 /* remember the last / entry in mtab */
1483 foundanswer = devname;
1484 }
1485
1486 /* Next line */
1487 chptr = strchr(chptr, '\n');
1488 if (chptr)
1489 chptr++;
1490 }
1491
1492 /* Return the last / entry found */
1493 if (foundanswer) {
1494 chptr = strchr(foundanswer, ' ');
1495 *chptr = '\0';
1496 return strdup(foundanswer);
1497 }
1498
1499 return NULL;
1500 }
1501
1502 void printEntry(struct singleEntry * entry) {
1503 int i;
1504 struct singleLine * line;
1505
1506 for (line = entry->lines; line; line = line->next) {
1507 fprintf(stderr, "DBG: %s", line->indent);
1508 for (i = 0; i < line->numElements; i++) {
1509 /* Need to handle this, because we strip the quotes from
1510 * menuentry when read it. */
1511 if (line->type == LT_MENUENTRY && i == 1) {
1512 if(!isquote(*line->elements[i].item))
1513 fprintf(stderr, "\'%s\'", line->elements[i].item);
1514 else
1515 fprintf(stderr, "%s", line->elements[i].item);
1516 fprintf(stderr, "%s", line->elements[i].indent);
1517
1518 continue;
1519 }
1520
1521 fprintf(stderr, "%s%s",
1522 line->elements[i].item, line->elements[i].indent);
1523 }
1524 fprintf(stderr, "\n");
1525 }
1526 }
1527
1528 void notSuitablePrintf(struct singleEntry * entry, const char *fmt, ...)
1529 {
1530 va_list argp;
1531
1532 if (!debug)
1533 return;
1534
1535 va_start(argp, fmt);
1536 fprintf(stderr, "DBG: Image entry failed: ");
1537 vfprintf(stderr, fmt, argp);
1538 printEntry(entry);
1539 va_end(argp);
1540 }
1541
1542 #define beginswith(s, c) ((s) && (s)[0] == (c))
1543
1544 static int endswith(const char *s, char c)
1545 {
1546 int slen;
1547
1548 if (!s || !s[0])
1549 return 0;
1550 slen = strlen(s) - 1;
1551
1552 return s[slen] == c;
1553 }
1554
1555 int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1556 int skipRemoved, int flags) {
1557 struct singleLine * line;
1558 char * fullName;
1559 int i;
1560 char * dev;
1561 char * rootspec;
1562 char * rootdev;
1563
1564 if (skipRemoved && entry->skip) {
1565 notSuitablePrintf(entry, "marked to skip\n");
1566 return 0;
1567 }
1568
1569 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1570 if (!line) {
1571 notSuitablePrintf(entry, "no line found\n");
1572 return 0;
1573 }
1574 if (line->numElements < 2) {
1575 notSuitablePrintf(entry, "line has only %d elements\n",
1576 line->numElements);
1577 return 0;
1578 }
1579
1580 if (flags & GRUBBY_BADIMAGE_OKAY) return 1;
1581
1582 fullName = alloca(strlen(bootPrefix) +
1583 strlen(line->elements[1].item) + 1);
1584 rootspec = getRootSpecifier(line->elements[1].item);
1585 int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1586 int hasslash = endswith(bootPrefix, '/') ||
1587 beginswith(line->elements[1].item + rootspec_offset, '/');
1588 sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1589 line->elements[1].item + rootspec_offset);
1590 if (access(fullName, R_OK)) {
1591 notSuitablePrintf(entry, "access to %s failed\n", fullName);
1592 return 0;
1593 }
1594 for (i = 2; i < line->numElements; i++)
1595 if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1596 if (i < line->numElements) {
1597 dev = line->elements[i].item + 5;
1598 } else {
1599 /* look for a lilo style LT_ROOT line */
1600 line = getLineByType(LT_ROOT, entry->lines);
1601
1602 if (line && line->numElements >= 2) {
1603 dev = line->elements[1].item;
1604 } else {
1605 /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1606 * grub+multiboot uses LT_MBMODULE for the args, so check that too.
1607 */
1608 line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
1609
1610 /* failed to find one */
1611 if (!line) {
1612 notSuitablePrintf(entry, "no line found\n");
1613 return 0;
1614 }
1615
1616 for (i = 1; i < line->numElements; i++)
1617 if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1618 if (i < line->numElements)
1619 dev = line->elements[i].item + 5;
1620 else {
1621 notSuitablePrintf(entry, "no root= entry found\n");
1622 /* it failed too... can't find root= */
1623 return 0;
1624 }
1625 }
1626 }
1627
1628 dev = getpathbyspec(dev);
1629 if (!getpathbyspec(dev)) {
1630 notSuitablePrintf(entry, "can't find blkid entry for %s\n", dev);
1631 return 0;
1632 } else
1633 dev = getpathbyspec(dev);
1634
1635 rootdev = findDiskForRoot();
1636 if (!rootdev) {
1637 notSuitablePrintf(entry, "can't find root device\n");
1638 return 0;
1639 }
1640
1641 if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1642 notSuitablePrintf(entry, "uuid missing: rootdev %s, dev %s\n",
1643 getuuidbydev(rootdev), getuuidbydev(dev));
1644 free(rootdev);
1645 return 0;
1646 }
1647
1648 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1649 notSuitablePrintf(entry, "uuid mismatch: rootdev %s, dev %s\n",
1650 getuuidbydev(rootdev), getuuidbydev(dev));
1651 free(rootdev);
1652 return 0;
1653 }
1654
1655 free(rootdev);
1656
1657 return 1;
1658 }
1659
1660 /* returns the first match on or after the one pointed to by index (if index
1661 is not NULL) which is not marked as skip */
1662 struct singleEntry * findEntryByPath(struct grubConfig * config,
1663 const char * kernel, const char * prefix,
1664 int * index) {
1665 struct singleEntry * entry = NULL;
1666 struct singleLine * line;
1667 int i;
1668 char * chptr;
1669 char * rootspec = NULL;
1670 enum lineType_e checkType = LT_KERNEL;
1671
1672 if (isdigit(*kernel)) {
1673 int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
1674
1675 i = 0;
1676 indexVars[i] = strtol(kernel, &chptr, 10);
1677 while (*chptr == ',') {
1678 i++;
1679 kernel = chptr + 1;
1680 indexVars[i] = strtol(kernel, &chptr, 10);
1681 }
1682
1683 if (*chptr) {
1684 /* can't parse it, bail */
1685 return NULL;
1686 }
1687
1688 indexVars[i + 1] = -1;
1689
1690 i = 0;
1691 if (index) {
1692 while (i < *index) i++;
1693 if (indexVars[i] == -1) return NULL;
1694 }
1695
1696 entry = findEntryByIndex(config, indexVars[i]);
1697 if (!entry) return NULL;
1698
1699 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1700 if (!line) return NULL;
1701
1702 if (index) *index = indexVars[i];
1703 return entry;
1704 }
1705
1706 if (!strcmp(kernel, "DEFAULT")) {
1707 if (index && *index > config->defaultImage) {
1708 entry = NULL;
1709 } else {
1710 entry = findEntryByIndex(config, config->defaultImage);
1711 if (entry && entry->skip)
1712 entry = NULL;
1713 else if (index)
1714 *index = config->defaultImage;
1715 }
1716 } else if (!strcmp(kernel, "ALL")) {
1717 if (index)
1718 i = *index;
1719 else
1720 i = 0;
1721
1722 while ((entry = findEntryByIndex(config, i))) {
1723 if (!entry->skip) break;
1724 i++;
1725 }
1726
1727 if (entry && index)
1728 *index = i;
1729 } else {
1730 if (index)
1731 i = *index;
1732 else
1733 i = 0;
1734
1735 if (!strncmp(kernel, "TITLE=", 6)) {
1736 prefix = "";
1737 checkType = LT_TITLE|LT_MENUENTRY;
1738 kernel += 6;
1739 }
1740
1741 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
1742 if (entry->skip) continue;
1743
1744 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
1745
1746 /* check all the lines matching checkType */
1747 for (line = entry->lines; line; line = line->next) {
1748 line = getLineByType(entry->multiboot && checkType == LT_KERNEL ?
1749 LT_KERNEL|LT_MBMODULE|LT_HYPER :
1750 checkType, line);
1751 if (!line) break; /* not found in this entry */
1752
1753 if (line && line->type != LT_MENUENTRY &&
1754 line->numElements >= 2) {
1755 rootspec = getRootSpecifier(line->elements[1].item);
1756 if (!strcmp(line->elements[1].item +
1757 ((rootspec != NULL) ? strlen(rootspec) : 0),
1758 kernel + strlen(prefix)))
1759 break;
1760 }
1761 if(line->type == LT_MENUENTRY &&
1762 !strcmp(line->elements[1].item, kernel))
1763 break;
1764 }
1765
1766 /* make sure this entry has a kernel identifier; this skips
1767 * non-Linux boot entries (could find netbsd etc, though, which is
1768 * unfortunate)
1769 */
1770 if (line && getLineByType(LT_KERNEL|LT_HYPER, entry->lines))
1771 break; /* found 'im! */
1772 }
1773
1774 if (index) *index = i;
1775 }
1776
1777 return entry;
1778 }
1779
1780 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
1781 struct singleEntry * entry;
1782
1783 entry = cfg->entries;
1784 while (index && entry) {
1785 entry = entry->next;
1786 index--;
1787 }
1788
1789 return entry;
1790 }
1791
1792 /* Find a good template to use for the new kernel. An entry is
1793 * good if the kernel and mkinitrd exist (even if the entry
1794 * is going to be removed). Try and use the default entry, but
1795 * if that doesn't work just take the first. If we can't find one,
1796 * bail. */
1797 struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
1798 int * indexPtr, int skipRemoved, int flags) {
1799 struct singleEntry * entry, * entry2;
1800 int index;
1801
1802 if (cfg->defaultImage > -1) {
1803 entry = findEntryByIndex(cfg, cfg->defaultImage);
1804 if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
1805 if (indexPtr) *indexPtr = cfg->defaultImage;
1806 return entry;
1807 }
1808 }
1809
1810 index = 0;
1811 while ((entry = findEntryByIndex(cfg, index))) {
1812 if (suitableImage(entry, prefix, skipRemoved, flags)) {
1813 int j;
1814 for (j = 0; j < index; j++) {
1815 entry2 = findEntryByIndex(cfg, j);
1816 if (entry2->skip) index--;
1817 }
1818 if (indexPtr) *indexPtr = index;
1819
1820 return entry;
1821 }
1822
1823 index++;
1824 }
1825
1826 fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
1827
1828 return NULL;
1829 }
1830
1831 char * findBootPrefix(void) {
1832 struct stat sb, sb2;
1833
1834 stat("/", &sb);
1835 #ifdef __ia64__
1836 stat("/boot/efi/EFI/redhat/", &sb2);
1837 #else
1838 stat("/boot", &sb2);
1839 #endif
1840
1841 if (sb.st_dev == sb2.st_dev)
1842 return strdup("");
1843
1844 #ifdef __ia64__
1845 return strdup("/boot/efi/EFI/redhat/");
1846 #else
1847 return strdup("/boot");
1848 #endif
1849 }
1850
1851 void markRemovedImage(struct grubConfig * cfg, const char * image,
1852 const char * prefix) {
1853 struct singleEntry * entry;
1854
1855 if (!image)
1856 return;
1857
1858 /* check and see if we're removing the default image */
1859 if (isdigit(*image)) {
1860 entry = findEntryByPath(cfg, image, prefix, NULL);
1861 if(entry)
1862 entry->skip = 1;
1863 return;
1864 }
1865
1866 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
1867 entry->skip = 1;
1868 }
1869
1870 void setDefaultImage(struct grubConfig * config, int hasNew,
1871 const char * defaultKernelPath, int newIsDefault,
1872 const char * prefix, int flags, int index) {
1873 struct singleEntry * entry, * entry2, * newDefault;
1874 int i, j;
1875
1876 if (newIsDefault) {
1877 config->defaultImage = 0;
1878 return;
1879 } else if ((index >= 0) && config->cfi->defaultIsIndex) {
1880 if (findEntryByIndex(config, index))
1881 config->defaultImage = index;
1882 else
1883 config->defaultImage = -1;
1884 return;
1885 } else if (defaultKernelPath) {
1886 i = 0;
1887 if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
1888 config->defaultImage = i;
1889 } else {
1890 config->defaultImage = -1;
1891 return;
1892 }
1893 }
1894
1895 /* defaultImage now points to what we'd like to use, but before any order
1896 changes */
1897 if ((config->defaultImage == DEFAULT_SAVED) ||
1898 (config->defaultImage == DEFAULT_SAVED_GRUB2))
1899 /* default is set to saved, we don't want to change it */
1900 return;
1901
1902 if (config->defaultImage > -1)
1903 entry = findEntryByIndex(config, config->defaultImage);
1904 else
1905 entry = NULL;
1906
1907 if (entry && !entry->skip) {
1908 /* we can preserve the default */
1909 if (hasNew)
1910 config->defaultImage++;
1911
1912 /* count the number of entries erased before this one */
1913 for (j = 0; j < config->defaultImage; j++) {
1914 entry2 = findEntryByIndex(config, j);
1915 if (entry2->skip) config->defaultImage--;
1916 }
1917 } else if (hasNew) {
1918 config->defaultImage = 0;
1919 } else {
1920 /* Either we just erased the default (or the default line was bad
1921 * to begin with) and didn't put a new one in. We'll use the first
1922 * valid image. */
1923 newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
1924 flags);
1925 if (!newDefault)
1926 config->defaultImage = -1;
1927 }
1928 }
1929
1930 void setFallbackImage(struct grubConfig * config, int hasNew) {
1931 struct singleEntry * entry, * entry2;
1932 int j;
1933
1934 if (config->fallbackImage == -1) return;
1935
1936 entry = findEntryByIndex(config, config->fallbackImage);
1937 if (!entry || entry->skip) {
1938 config->fallbackImage = -1;
1939 return;
1940 }
1941
1942 if (hasNew)
1943 config->fallbackImage++;
1944
1945 /* count the number of entries erased before this one */
1946 for (j = 0; j < config->fallbackImage; j++) {
1947 entry2 = findEntryByIndex(config, j);
1948 if (entry2->skip) config->fallbackImage--;
1949 }
1950 }
1951
1952 void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
1953 struct singleLine * line;
1954 char * root = NULL;
1955 int i;
1956
1957 printf("index=%d\n", index);
1958
1959 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1960 if (!line) {
1961 printf("non linux entry\n");
1962 return;
1963 }
1964
1965 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
1966 printf("kernel=%s\n", line->elements[1].item);
1967 else
1968 printf("kernel=%s%s\n", prefix, line->elements[1].item);
1969
1970 if (line->numElements >= 3) {
1971 printf("args=\"");
1972 i = 2;
1973 while (i < line->numElements) {
1974 if (!strncmp(line->elements[i].item, "root=", 5)) {
1975 root = line->elements[i].item + 5;
1976 } else {
1977 printf("%s%s", line->elements[i].item,
1978 line->elements[i].indent);
1979 }
1980
1981 i++;
1982 }
1983 printf("\"\n");
1984 } else {
1985 line = getLineByType(LT_KERNELARGS, entry->lines);
1986 if (line) {
1987 char * s;
1988
1989 printf("args=\"");
1990 i = 1;
1991 while (i < line->numElements) {
1992 if (!strncmp(line->elements[i].item, "root=", 5)) {
1993 root = line->elements[i].item + 5;
1994 } else {
1995 s = line->elements[i].item;
1996
1997 printf("%s%s", s, line->elements[i].indent);
1998 }
1999
2000 i++;
2001 }
2002
2003 s = line->elements[i - 1].indent;
2004 printf("\"\n");
2005 }
2006 }
2007
2008 if (!root) {
2009 line = getLineByType(LT_ROOT, entry->lines);
2010 if (line && line->numElements >= 2)
2011 root=line->elements[1].item;
2012 }
2013
2014 if (root) {
2015 char * s = alloca(strlen(root) + 1);
2016
2017 strcpy(s, root);
2018 if (s[strlen(s) - 1] == '"')
2019 s[strlen(s) - 1] = '\0';
2020 /* make sure the root doesn't have a trailing " */
2021 printf("root=%s\n", s);
2022 }
2023
2024 line = getLineByType(LT_INITRD, entry->lines);
2025
2026 if (line && line->numElements >= 2) {
2027 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2028 printf("initrd=");
2029 else
2030 printf("initrd=%s", prefix);
2031
2032 for (i = 1; i < line->numElements; i++)
2033 printf("%s%s", line->elements[i].item, line->elements[i].indent);
2034 printf("\n");
2035 }
2036
2037 line = getLineByType(LT_TITLE, entry->lines);
2038 if (line) {
2039 printf("title=%s\n", line->elements[1].item);
2040 } else {
2041 char * title;
2042 line = getLineByType(LT_MENUENTRY, entry->lines);
2043 title = grub2ExtractTitle(line);
2044 if (title)
2045 printf("title=%s\n", title);
2046 }
2047 }
2048
2049 int isSuseSystem(void) {
2050 const char * path;
2051 const static char default_path[] = "/etc/SuSE-release";
2052
2053 if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2054 path = default_path;
2055
2056 if (!access(path, R_OK))
2057 return 1;
2058 return 0;
2059 }
2060
2061 int isSuseGrubConf(const char * path) {
2062 FILE * grubConf;
2063 char * line = NULL;
2064 size_t len = 0, res = 0;
2065
2066 grubConf = fopen(path, "r");
2067 if (!grubConf) {
2068 dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2069 return 0;
2070 }
2071
2072 while ((res = getline(&line, &len, grubConf)) != -1) {
2073 if (!strncmp(line, "setup", 5)) {
2074 fclose(grubConf);
2075 free(line);
2076 return 1;
2077 }
2078 }
2079
2080 dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2081 path);
2082
2083 fclose(grubConf);
2084 free(line);
2085 return 0;
2086 }
2087
2088 int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2089 FILE * grubConf;
2090 char * line = NULL;
2091 size_t res = 0, len = 0;
2092
2093 if (!path) return 1;
2094 if (!lbaPtr) return 1;
2095
2096 grubConf = fopen(path, "r");
2097 if (!grubConf) return 1;
2098
2099 while ((res = getline(&line, &len, grubConf)) != -1) {
2100 if (line[res - 1] == '\n')
2101 line[res - 1] = '\0';
2102 else if (len > res)
2103 line[res] = '\0';
2104 else {
2105 line = realloc(line, res + 1);
2106 line[res] = '\0';
2107 }
2108
2109 if (!strncmp(line, "setup", 5)) {
2110 if (strstr(line, "--force-lba")) {
2111 *lbaPtr = 1;
2112 } else {
2113 *lbaPtr = 0;
2114 }
2115 dbgPrintf("lba: %i\n", *lbaPtr);
2116 break;
2117 }
2118 }
2119
2120 free(line);
2121 fclose(grubConf);
2122 return 0;
2123 }
2124
2125 int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2126 FILE * grubConf;
2127 char * line = NULL;
2128 size_t res = 0, len = 0;
2129 char * lastParamPtr = NULL;
2130 char * secLastParamPtr = NULL;
2131 char installDeviceNumber = '\0';
2132 char * bounds = NULL;
2133
2134 if (!path) return 1;
2135 if (!devicePtr) return 1;
2136
2137 grubConf = fopen(path, "r");
2138 if (!grubConf) return 1;
2139
2140 while ((res = getline(&line, &len, grubConf)) != -1) {
2141 if (strncmp(line, "setup", 5))
2142 continue;
2143
2144 if (line[res - 1] == '\n')
2145 line[res - 1] = '\0';
2146 else if (len > res)
2147 line[res] = '\0';
2148 else {
2149 line = realloc(line, res + 1);
2150 line[res] = '\0';
2151 }
2152
2153 lastParamPtr = bounds = line + res;
2154
2155 /* Last parameter in grub may be an optional IMAGE_DEVICE */
2156 while (!isspace(*lastParamPtr))
2157 lastParamPtr--;
2158 lastParamPtr++;
2159
2160 secLastParamPtr = lastParamPtr - 2;
2161 dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2162
2163 if (lastParamPtr + 3 > bounds) {
2164 dbgPrintf("lastParamPtr going over boundary");
2165 fclose(grubConf);
2166 free(line);
2167 return 1;
2168 }
2169 if (!strncmp(lastParamPtr, "(hd", 3))
2170 lastParamPtr += 3;
2171 dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2172
2173 /*
2174 * Second last parameter will decide wether last parameter is
2175 * an IMAGE_DEVICE or INSTALL_DEVICE
2176 */
2177 while (!isspace(*secLastParamPtr))
2178 secLastParamPtr--;
2179 secLastParamPtr++;
2180
2181 if (secLastParamPtr + 3 > bounds) {
2182 dbgPrintf("secLastParamPtr going over boundary");
2183 fclose(grubConf);
2184 free(line);
2185 return 1;
2186 }
2187 dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2188 if (!strncmp(secLastParamPtr, "(hd", 3)) {
2189 secLastParamPtr += 3;
2190 dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2191 installDeviceNumber = *secLastParamPtr;
2192 } else {
2193 installDeviceNumber = *lastParamPtr;
2194 }
2195
2196 *devicePtr = malloc(6);
2197 snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2198 dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2199 fclose(grubConf);
2200 free(line);
2201 return 0;
2202 }
2203
2204 free(line);
2205 fclose(grubConf);
2206 return 1;
2207 }
2208
2209 int grubGetBootFromDeviceMap(const char * device,
2210 char ** bootPtr) {
2211 FILE * deviceMap;
2212 char * line = NULL;
2213 size_t res = 0, len = 0;
2214 char * devicePtr;
2215 char * bounds = NULL;
2216 const char * path;
2217 const static char default_path[] = "/boot/grub/device.map";
2218
2219 if (!device) return 1;
2220 if (!bootPtr) return 1;
2221
2222 if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2223 path = default_path;
2224
2225 dbgPrintf("opening grub device.map file from: %s\n", path);
2226 deviceMap = fopen(path, "r");
2227 if (!deviceMap)
2228 return 1;
2229
2230 while ((res = getline(&line, &len, deviceMap)) != -1) {
2231 if (!strncmp(line, "#", 1))
2232 continue;
2233
2234 if (line[res - 1] == '\n')
2235 line[res - 1] = '\0';
2236 else if (len > res)
2237 line[res] = '\0';
2238 else {
2239 line = realloc(line, res + 1);
2240 line[res] = '\0';
2241 }
2242
2243 devicePtr = line;
2244 bounds = line + res;
2245
2246 while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2247 devicePtr++;
2248 dbgPrintf("device: %s\n", devicePtr);
2249
2250 if (!strncmp(devicePtr, device, strlen(device))) {
2251 devicePtr += strlen(device);
2252 while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2253 devicePtr++;
2254
2255 *bootPtr = strdup(devicePtr);
2256 break;
2257 }
2258 }
2259
2260 free(line);
2261 fclose(deviceMap);
2262 return 0;
2263 }
2264
2265 int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2266 char * grubDevice;
2267
2268 if (suseGrubConfGetInstallDevice(path, &grubDevice))
2269 dbgPrintf("error looking for grub installation device\n");
2270 else
2271 dbgPrintf("grubby installation device: %s\n", grubDevice);
2272
2273 if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2274 dbgPrintf("error looking for grub boot device\n");
2275 else
2276 dbgPrintf("grubby boot device: %s\n", *bootPtr);
2277
2278 free(grubDevice);
2279 return 0;
2280 }
2281
2282 int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2283 /*
2284 * This SuSE grub configuration file at this location is not your average
2285 * grub configuration file, but instead the grub commands used to setup
2286 * grub on that system.
2287 */
2288 const char * path;
2289 const static char default_path[] = "/etc/grub.conf";
2290
2291 if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2292 path = default_path;
2293
2294 if (!isSuseGrubConf(path)) return 1;
2295
2296 if (lbaPtr) {
2297 *lbaPtr = 0;
2298 if (suseGrubConfGetLba(path, lbaPtr))
2299 return 1;
2300 }
2301
2302 if (bootPtr) {
2303 *bootPtr = NULL;
2304 suseGrubConfGetBoot(path, bootPtr);
2305 }
2306
2307 return 0;
2308 }
2309
2310 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
2311 FILE * in;
2312 char buf[1024];
2313 char * chptr;
2314 char * start;
2315 char * param;
2316
2317 in = fopen("/etc/conf.d/grub", "r");
2318 if (!in) return 1;
2319
2320 if (lbaPtr) *lbaPtr = 0;
2321 if (bootPtr) *bootPtr = NULL;
2322
2323 while (fgets(buf, sizeof(buf), in)) {
2324 start = buf;
2325 while (isspace(*start)) start++;
2326 if (*start == '#') continue;
2327
2328 chptr = strchr(start, '=');
2329 if (!chptr) continue;
2330 chptr--;
2331 while (*chptr && isspace(*chptr)) chptr--;
2332 chptr++;
2333 *chptr = '\0';
2334
2335 param = chptr + 1;
2336 while (*param && isspace(*param)) param++;
2337 if (*param == '=') {
2338 param++;
2339 while (*param && isspace(*param)) param++;
2340 }
2341
2342 chptr = param;
2343 while (*chptr && !isspace(*chptr)) chptr++;
2344 *chptr = '\0';
2345
2346 if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
2347 *lbaPtr = 1;
2348 else if (!strcmp(start, "boot") && bootPtr)
2349 *bootPtr = strdup(param);
2350 }
2351
2352 fclose(in);
2353
2354 return 0;
2355 }
2356
2357 void dumpSysconfigGrub(void) {
2358 char * boot = NULL;
2359 int lba;
2360
2361 if (isSuseSystem()) {
2362 if (parseSuseGrubConf(&lba, &boot)) {
2363 free(boot);
2364 return;
2365 }
2366 } else {
2367 if (parseSysconfigGrub(&lba, &boot)) {
2368 free(boot);
2369 return;
2370 }
2371 }
2372
2373 if (lba) printf("lba\n");
2374 if (boot) {
2375 printf("boot=%s\n", boot);
2376 free(boot);
2377 }
2378 }
2379
2380 int displayInfo(struct grubConfig * config, char * kernel,
2381 const char * prefix) {
2382 int i = 0;
2383 struct singleEntry * entry;
2384 struct singleLine * line;
2385
2386 entry = findEntryByPath(config, kernel, prefix, &i);
2387 if (!entry) {
2388 fprintf(stderr, _("grubby: kernel not found\n"));
2389 return 1;
2390 }
2391
2392 /* this is a horrible hack to support /etc/conf.d/grub; there must
2393 be a better way */
2394 if (config->cfi == &grubConfigType) {
2395 dumpSysconfigGrub();
2396 } else {
2397 line = getLineByType(LT_BOOT, config->theLines);
2398 if (line && line->numElements >= 1) {
2399 printf("boot=%s\n", line->elements[1].item);
2400 }
2401
2402 line = getLineByType(LT_LBA, config->theLines);
2403 if (line) printf("lba\n");
2404 }
2405
2406 displayEntry(entry, prefix, i);
2407
2408 i++;
2409 while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
2410 displayEntry(entry, prefix, i);
2411 i++;
2412 }
2413
2414 return 0;
2415 }
2416
2417 struct singleLine * addLineTmpl(struct singleEntry * entry,
2418 struct singleLine * tmplLine,
2419 struct singleLine * prevLine,
2420 const char * val,
2421 struct configFileInfo * cfi)
2422 {
2423 struct singleLine * newLine = lineDup(tmplLine);
2424
2425 if (val) {
2426 /* override the inherited value with our own.
2427 * This is a little weak because it only applies to elements[1]
2428 */
2429 if (newLine->numElements > 1)
2430 removeElement(newLine, 1);
2431 insertElement(newLine, val, 1, cfi);
2432
2433 /* but try to keep the rootspec from the template... sigh */
2434 if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD)) {
2435 char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2436 if (rootspec != NULL) {
2437 free(newLine->elements[1].item);
2438 newLine->elements[1].item =
2439 sdupprintf("%s%s", rootspec, val);
2440 }
2441 }
2442 }
2443
2444 dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2445 newLine->elements[0].item : "");
2446
2447 if (!entry->lines) {
2448 /* first one on the list */
2449 entry->lines = newLine;
2450 } else if (prevLine) {
2451 /* add after prevLine */
2452 newLine->next = prevLine->next;
2453 prevLine->next = newLine;
2454 }
2455
2456 return newLine;
2457 }
2458
2459 /* val may be NULL */
2460 struct singleLine * addLine(struct singleEntry * entry,
2461 struct configFileInfo * cfi,
2462 enum lineType_e type, char * defaultIndent,
2463 const char * val) {
2464 struct singleLine * line, * prev;
2465 struct keywordTypes * kw;
2466 struct singleLine tmpl;
2467
2468 /* NB: This function shouldn't allocate items on the heap, rather on the
2469 * stack since it calls addLineTmpl which will make copies.
2470 */
2471
2472 if (type == LT_TITLE && cfi->titleBracketed) {
2473 /* we're doing a bracketed title (zipl) */
2474 tmpl.type = type;
2475 tmpl.numElements = 1;
2476 tmpl.elements = alloca(sizeof(*tmpl.elements));
2477 tmpl.elements[0].item = alloca(strlen(val)+3);
2478 sprintf(tmpl.elements[0].item, "[%s]", val);
2479 tmpl.elements[0].indent = "";
2480 val = NULL;
2481 } else if (type == LT_MENUENTRY) {
2482 char *lineend = "--class gnu-linux --class gnu --class os {";
2483 if (!val) {
2484 fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2485 abort();
2486 }
2487 kw = getKeywordByType(type, cfi);
2488 if (!kw) {
2489 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2490 abort();
2491 }
2492 tmpl.indent = "";
2493 tmpl.type = type;
2494 tmpl.numElements = 3;
2495 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2496 tmpl.elements[0].item = kw->key;
2497 tmpl.elements[0].indent = alloca(2);
2498 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2499 tmpl.elements[1].item = (char *)val;
2500 tmpl.elements[1].indent = alloca(2);
2501 sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2502 tmpl.elements[2].item = alloca(strlen(lineend)+1);
2503 strcpy(tmpl.elements[2].item, lineend);
2504 tmpl.elements[2].indent = "";
2505 } else {
2506 kw = getKeywordByType(type, cfi);
2507 if (!kw) {
2508 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2509 abort();
2510 }
2511 tmpl.type = type;
2512 tmpl.numElements = val ? 2 : 1;
2513 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2514 tmpl.elements[0].item = kw->key;
2515 tmpl.elements[0].indent = alloca(2);
2516 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2517 if (val) {
2518 tmpl.elements[1].item = (char *)val;
2519 tmpl.elements[1].indent = "";
2520 }
2521 }
2522
2523 /* The last non-empty line gives us the indention to us and the line
2524 to insert after. Note that comments are considered empty lines, which
2525 may not be ideal? If there are no lines or we are looking at the
2526 first line, we use defaultIndent (the first line is normally indented
2527 differently from the rest) */
2528 for (line = entry->lines, prev = NULL; line; line = line->next) {
2529 if (line->numElements) prev = line;
2530 /* fall back on the last line if prev isn't otherwise set */
2531 if (!line->next && !prev) prev = line;
2532 }
2533
2534 struct singleLine *menuEntry;
2535 menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2536 if (tmpl.type == LT_ENTRY_END) {
2537 if (menuEntry)
2538 tmpl.indent = menuEntry->indent;
2539 else
2540 tmpl.indent = defaultIndent ?: "";
2541 } else if (tmpl.type != LT_MENUENTRY) {
2542 if (menuEntry)
2543 tmpl.indent = "\t";
2544 else if (prev == entry->lines)
2545 tmpl.indent = defaultIndent ?: "";
2546 else
2547 tmpl.indent = prev->indent;
2548 }
2549
2550 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2551 }
2552
2553 void removeLine(struct singleEntry * entry, struct singleLine * line) {
2554 struct singleLine * prev;
2555 int i;
2556
2557 for (i = 0; i < line->numElements; i++) {
2558 free(line->elements[i].item);
2559 free(line->elements[i].indent);
2560 }
2561 free(line->elements);
2562 free(line->indent);
2563
2564 if (line == entry->lines) {
2565 entry->lines = line->next;
2566 } else {
2567 prev = entry->lines;
2568 while (prev->next != line) prev = prev->next;
2569 prev->next = line->next;
2570 }
2571
2572 free(line);
2573 }
2574
2575 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2576 {
2577 struct singleLine newLine = {
2578 .indent = tmplLine->indent,
2579 .type = tmplLine->type,
2580 .next = tmplLine->next,
2581 };
2582 int firstQuotedItem = -1;
2583 int quoteLen = 0;
2584 int j;
2585 int element = 0;
2586 char *c;
2587
2588 c = malloc(strlen(tmplLine->elements[0].item) + 1);
2589 strcpy(c, tmplLine->elements[0].item);
2590 insertElement(&newLine, c, element++, cfi);
2591 free(c);
2592 c = NULL;
2593
2594 for (j = 1; j < tmplLine->numElements; j++) {
2595 if (firstQuotedItem == -1) {
2596 quoteLen += strlen(tmplLine->elements[j].item);
2597
2598 if (isquote(tmplLine->elements[j].item[0])) {
2599 firstQuotedItem = j;
2600 quoteLen += strlen(tmplLine->elements[j].indent);
2601 } else {
2602 c = malloc(quoteLen + 1);
2603 strcpy(c, tmplLine->elements[j].item);
2604 insertElement(&newLine, c, element++, cfi);
2605 free(c);
2606 quoteLen = 0;
2607 }
2608 } else {
2609 int itemlen = strlen(tmplLine->elements[j].item);
2610 quoteLen += itemlen;
2611 quoteLen += strlen(tmplLine->elements[j].indent);
2612
2613 if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2614 c = malloc(quoteLen + 1);
2615 c[0] = '\0';
2616 for (int i = firstQuotedItem; i < j+1; i++) {
2617 strcat(c, tmplLine->elements[i].item);
2618 strcat(c, tmplLine->elements[i].indent);
2619 }
2620 insertElement(&newLine, c, element++, cfi);
2621 free(c);
2622
2623 firstQuotedItem = -1;
2624 quoteLen = 0;
2625 }
2626 }
2627 }
2628 while (tmplLine->numElements)
2629 removeElement(tmplLine, 0);
2630 if (tmplLine->elements)
2631 free(tmplLine->elements);
2632
2633 tmplLine->numElements = newLine.numElements;
2634 tmplLine->elements = newLine.elements;
2635 }
2636
2637 static void insertElement(struct singleLine * line,
2638 const char * item, int insertHere,
2639 struct configFileInfo * cfi)
2640 {
2641 struct keywordTypes * kw;
2642 char indent[2] = "";
2643
2644 /* sanity check */
2645 if (insertHere > line->numElements) {
2646 dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2647 insertHere, line->numElements);
2648 insertHere = line->numElements;
2649 }
2650
2651 line->elements = realloc(line->elements, (line->numElements + 1) *
2652 sizeof(*line->elements));
2653 memmove(&line->elements[insertHere+1],
2654 &line->elements[insertHere],
2655 (line->numElements - insertHere) *
2656 sizeof(*line->elements));
2657 line->elements[insertHere].item = strdup(item);
2658
2659 kw = getKeywordByType(line->type, cfi);
2660
2661 if (line->numElements == 0) {
2662 indent[0] = '\0';
2663 } else if (insertHere == 0) {
2664 indent[0] = kw->nextChar;
2665 } else if (kw->separatorChar != '\0') {
2666 indent[0] = kw->separatorChar;
2667 } else {
2668 indent[0] = ' ';
2669 }
2670
2671 if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
2672 /* move the end-of-line forward */
2673 line->elements[insertHere].indent =
2674 line->elements[insertHere-1].indent;
2675 line->elements[insertHere-1].indent = strdup(indent);
2676 } else {
2677 line->elements[insertHere].indent = strdup(indent);
2678 }
2679
2680 line->numElements++;
2681
2682 dbgPrintf("insertElement(%s, '%s%s', %d)\n",
2683 line->elements[0].item,
2684 line->elements[insertHere].item,
2685 line->elements[insertHere].indent,
2686 insertHere);
2687 }
2688
2689 static void removeElement(struct singleLine * line, int removeHere) {
2690 int i;
2691
2692 /* sanity check */
2693 if (removeHere >= line->numElements) return;
2694
2695 dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
2696 removeHere, line->elements[removeHere].item);
2697
2698 free(line->elements[removeHere].item);
2699
2700 if (removeHere > 1) {
2701 /* previous argument gets this argument's post-indentation */
2702 free(line->elements[removeHere-1].indent);
2703 line->elements[removeHere-1].indent =
2704 line->elements[removeHere].indent;
2705 } else {
2706 free(line->elements[removeHere].indent);
2707 }
2708
2709 /* now collapse the array, but don't bother to realloc smaller */
2710 for (i = removeHere; i < line->numElements - 1; i++)
2711 line->elements[i] = line->elements[i + 1];
2712
2713 line->numElements--;
2714 }
2715
2716 int argMatch(const char * one, const char * two) {
2717 char * first, * second;
2718 char * chptr;
2719
2720 first = strcpy(alloca(strlen(one) + 1), one);
2721 second = strcpy(alloca(strlen(two) + 1), two);
2722
2723 chptr = strchr(first, '=');
2724 if (chptr) *chptr = '\0';
2725
2726 chptr = strchr(second, '=');
2727 if (chptr) *chptr = '\0';
2728
2729 return strcmp(first, second);
2730 }
2731
2732 int updateActualImage(struct grubConfig * cfg, const char * image,
2733 const char * prefix, const char * addArgs,
2734 const char * removeArgs, int multibootArgs) {
2735 struct singleEntry * entry;
2736 struct singleLine * line, * rootLine;
2737 int index = 0;
2738 int i, k;
2739 const char ** newArgs, ** oldArgs;
2740 const char ** arg;
2741 int useKernelArgs, useRoot;
2742 int firstElement;
2743 int *usedElements;
2744 int doreplace;
2745
2746 if (!image) return 0;
2747
2748 if (!addArgs) {
2749 newArgs = malloc(sizeof(*newArgs));
2750 *newArgs = NULL;
2751 } else {
2752 if (poptParseArgvString(addArgs, NULL, &newArgs)) {
2753 fprintf(stderr,
2754 _("grubby: error separating arguments '%s'\n"), addArgs);
2755 return 1;
2756 }
2757 }
2758
2759 if (!removeArgs) {
2760 oldArgs = malloc(sizeof(*oldArgs));
2761 *oldArgs = NULL;
2762 } else {
2763 if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
2764 fprintf(stderr,
2765 _("grubby: error separating arguments '%s'\n"), removeArgs);
2766 free(newArgs);
2767 return 1;
2768 }
2769 }
2770
2771
2772 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
2773 && (!multibootArgs || cfg->cfi->mbConcatArgs));
2774
2775 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
2776 && !multibootArgs);
2777
2778 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2779
2780 if (multibootArgs && !entry->multiboot)
2781 continue;
2782
2783 /* Determine where to put the args. If this config supports
2784 * LT_KERNELARGS, use that. Otherwise use
2785 * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
2786 */
2787 if (useKernelArgs) {
2788 line = getLineByType(LT_KERNELARGS, entry->lines);
2789 if (!line) {
2790 /* no LT_KERNELARGS, need to add it */
2791 line = addLine(entry, cfg->cfi, LT_KERNELARGS,
2792 cfg->secondaryIndent, NULL);
2793 }
2794 firstElement = 1;
2795
2796 } else if (multibootArgs) {
2797 line = getLineByType(LT_HYPER, entry->lines);
2798 if (!line) {
2799 /* a multiboot entry without LT_HYPER? */
2800 continue;
2801 }
2802 firstElement = 2;
2803
2804 } else {
2805 line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines);
2806 if (!line) {
2807 /* no LT_KERNEL or LT_MBMODULE in this entry? */
2808 continue;
2809 }
2810 firstElement = 2;
2811 }
2812
2813 /* handle the elilo case which does:
2814 * append="hypervisor args -- kernel args"
2815 */
2816 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
2817 /* this is a multiboot entry, make sure there's
2818 * -- on the args line
2819 */
2820 for (i = firstElement; i < line->numElements; i++) {
2821 if (!strcmp(line->elements[i].item, "--"))
2822 break;
2823 }
2824 if (i == line->numElements) {
2825 /* assume all existing args are kernel args,
2826 * prepend -- to make it official
2827 */
2828 insertElement(line, "--", firstElement, cfg->cfi);
2829 i = firstElement;
2830 }
2831 if (!multibootArgs) {
2832 /* kernel args start after the -- */
2833 firstElement = i + 1;
2834 }
2835 } else if (cfg->cfi->mbConcatArgs) {
2836 /* this is a non-multiboot entry, remove hyper args */
2837 for (i = firstElement; i < line->numElements; i++) {
2838 if (!strcmp(line->elements[i].item, "--"))
2839 break;
2840 }
2841 if (i < line->numElements) {
2842 /* remove args up to -- */
2843 while (strcmp(line->elements[firstElement].item, "--"))
2844 removeElement(line, firstElement);
2845 /* remove -- */
2846 removeElement(line, firstElement);
2847 }
2848 }
2849
2850 usedElements = calloc(line->numElements, sizeof(*usedElements));
2851
2852 for (k = 0, arg = newArgs; *arg; arg++, k++) {
2853
2854 doreplace = 1;
2855 for (i = firstElement; i < line->numElements; i++) {
2856 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2857 !strcmp(line->elements[i].item, "--"))
2858 {
2859 /* reached the end of hyper args, insert here */
2860 doreplace = 0;
2861 break;
2862 }
2863 if (usedElements[i])
2864 continue;
2865 if (!argMatch(line->elements[i].item, *arg)) {
2866 usedElements[i]=1;
2867 break;
2868 }
2869 }
2870
2871 if (i < line->numElements && doreplace) {
2872 /* direct replacement */
2873 free(line->elements[i].item);
2874 line->elements[i].item = strdup(*arg);
2875
2876 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
2877 /* root= replacement */
2878 rootLine = getLineByType(LT_ROOT, entry->lines);
2879 if (rootLine) {
2880 free(rootLine->elements[1].item);
2881 rootLine->elements[1].item = strdup(*arg + 5);
2882 } else {
2883 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
2884 cfg->secondaryIndent, *arg + 5);
2885 }
2886 }
2887
2888 else {
2889 /* insert/append */
2890 insertElement(line, *arg, i, cfg->cfi);
2891 usedElements = realloc(usedElements, line->numElements *
2892 sizeof(*usedElements));
2893 memmove(&usedElements[i + 1], &usedElements[i],
2894 line->numElements - i - 1);
2895 usedElements[i] = 1;
2896
2897 /* if we updated a root= here even though there is a
2898 LT_ROOT available we need to remove the LT_ROOT entry
2899 (this will happen if we switch from a device to a label) */
2900 if (useRoot && !strncmp(*arg, "root=", 5)) {
2901 rootLine = getLineByType(LT_ROOT, entry->lines);
2902 if (rootLine)
2903 removeLine(entry, rootLine);
2904 }
2905 }
2906 }
2907
2908 free(usedElements);
2909
2910 for (arg = oldArgs; *arg; arg++) {
2911 for (i = firstElement; i < line->numElements; i++) {
2912 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2913 !strcmp(line->elements[i].item, "--"))
2914 /* reached the end of hyper args, stop here */
2915 break;
2916 if (!argMatch(line->elements[i].item, *arg)) {
2917 removeElement(line, i);
2918 break;
2919 }
2920 }
2921 /* handle removing LT_ROOT line too */
2922 if (useRoot && !strncmp(*arg, "root=", 5)) {
2923 rootLine = getLineByType(LT_ROOT, entry->lines);
2924 if (rootLine)
2925 removeLine(entry, rootLine);
2926 }
2927 }
2928
2929 if (line->numElements == 1) {
2930 /* don't need the line at all (note it has to be a
2931 LT_KERNELARGS for this to happen */
2932 removeLine(entry, line);
2933 }
2934 }
2935
2936 free(newArgs);
2937 free(oldArgs);
2938
2939 return 0;
2940 }
2941
2942 int updateImage(struct grubConfig * cfg, const char * image,
2943 const char * prefix, const char * addArgs,
2944 const char * removeArgs,
2945 const char * addMBArgs, const char * removeMBArgs) {
2946 int rc = 0;
2947
2948 if (!image) return rc;
2949
2950 /* update the main args first... */
2951 if (addArgs || removeArgs)
2952 rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
2953 if (rc) return rc;
2954
2955 /* and now any multiboot args */
2956 if (addMBArgs || removeMBArgs)
2957 rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
2958 return rc;
2959 }
2960
2961 int updateInitrd(struct grubConfig * cfg, const char * image,
2962 const char * prefix, const char * initrd) {
2963 struct singleEntry * entry;
2964 struct singleLine * line, * kernelLine, *endLine = NULL;
2965 int index = 0;
2966
2967 if (!image) return 0;
2968
2969 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2970 kernelLine = getLineByType(LT_KERNEL, entry->lines);
2971 if (!kernelLine) continue;
2972
2973 line = getLineByType(LT_INITRD, entry->lines);
2974 if (line)
2975 removeLine(entry, line);
2976 if (prefix) {
2977 int prefixLen = strlen(prefix);
2978 if (!strncmp(initrd, prefix, prefixLen))
2979 initrd += prefixLen;
2980 }
2981 endLine = getLineByType(LT_ENTRY_END, entry->lines);
2982 if (endLine)
2983 removeLine(entry, endLine);
2984 line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd);
2985 if (!line)
2986 return 1;
2987 if (endLine) {
2988 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
2989 if (!line)
2990 return 1;
2991 }
2992
2993 break;
2994 }
2995
2996 return 0;
2997 }
2998
2999 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3000 int fd;
3001 unsigned char bootSect[512];
3002 int offset;
3003
3004 fd = open(device, O_RDONLY);
3005 if (fd < 0) {
3006 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3007 device, strerror(errno));
3008 return 1;
3009 }
3010
3011 if (read(fd, bootSect, 512) != 512) {
3012 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3013 device, strerror(errno));
3014 return 1;
3015 }
3016 close(fd);
3017
3018 /* first three bytes should match, a jmp short should be in there */
3019 if (memcmp(boot, bootSect, 3))
3020 return 0;
3021
3022 if (boot[1] == JMP_SHORT_OPCODE) {
3023 offset = boot[2] + 2;
3024 } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3025 offset = (boot[3] << 8) + boot[2] + 2;
3026 } else if (boot[0] == JMP_SHORT_OPCODE) {
3027 offset = boot[1] + 2;
3028 /*
3029 * it looks like grub, when copying stage1 into the mbr, patches stage1
3030 * right after the JMP location, replacing other instructions such as
3031 * JMPs for NOOPs. So, relax the check a little bit by skipping those
3032 * different bytes.
3033 */
3034 if ((bootSect[offset + 1] == NOOP_OPCODE)
3035 && (bootSect[offset + 2] == NOOP_OPCODE)) {
3036 offset = offset + 3;
3037 }
3038 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3039 offset = (boot[2] << 8) + boot[1] + 2;
3040 } else {
3041 return 0;
3042 }
3043
3044 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3045 return 0;
3046
3047 return 2;
3048 }
3049
3050 int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3051 int fd;
3052 char buf[65536];
3053 char * end;
3054 char * chptr;
3055 char * chptr2;
3056 int rc;
3057
3058 /* it's on raid; we need to parse /proc/mdstat and check all of the
3059 *raw* devices listed in there */
3060
3061 if (!strncmp(mdDev, "/dev/", 5))
3062 mdDev += 5;
3063
3064 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3065 fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3066 strerror(errno));
3067 return 2;
3068 }
3069
3070 rc = read(fd, buf, sizeof(buf) - 1);
3071 if (rc < 0 || rc == (sizeof(buf) - 1)) {
3072 fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3073 strerror(errno));
3074 close(fd);
3075 return 2;
3076 }
3077 close(fd);
3078 buf[rc] = '\0';
3079
3080 chptr = buf;
3081 while (*chptr) {
3082 end = strchr(chptr, '\n');
3083 if (!end) break;
3084 *end = '\0';
3085
3086 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3087 chptr[strlen(mdDev)] == ' ') {
3088
3089 /* found the device */
3090 while (*chptr && *chptr != ':') chptr++;
3091 chptr++;
3092 while (*chptr && isspace(*chptr)) chptr++;
3093
3094 /* skip the "active" bit */
3095 while (*chptr && !isspace(*chptr)) chptr++;
3096 while (*chptr && isspace(*chptr)) chptr++;
3097
3098 /* skip the raid level */
3099 while (*chptr && !isspace(*chptr)) chptr++;
3100 while (*chptr && isspace(*chptr)) chptr++;
3101
3102 /* everything else is partition stuff */
3103 while (*chptr) {
3104 chptr2 = chptr;
3105 while (*chptr2 && *chptr2 != '[') chptr2++;
3106 if (!*chptr2) break;
3107
3108 /* yank off the numbers at the end */
3109 chptr2--;
3110 while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3111 chptr2++;
3112 *chptr2 = '\0';
3113
3114 /* Better, now we need the /dev/ back. We're done with
3115 * everything before this point, so we can just put
3116 * the /dev/ part there. There will always be room. */
3117 memcpy(chptr - 5, "/dev/", 5);
3118 rc = checkDeviceBootloader(chptr - 5, boot);
3119 if (rc != 2) {
3120 return rc;
3121 }
3122
3123 chptr = chptr2 + 1;
3124 /* skip the [11] bit */
3125 while (*chptr && !isspace(*chptr)) chptr++;
3126 /* and move to the next one */
3127 while (*chptr && isspace(*chptr)) chptr++;
3128 }
3129
3130 /* we're good to go */
3131 return 2;
3132 }
3133
3134 chptr = end + 1;
3135 }
3136
3137 fprintf(stderr,
3138 _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3139 mdDev);
3140 return 0;
3141 }
3142
3143 int checkForLilo(struct grubConfig * config) {
3144 int fd;
3145 unsigned char boot[512];
3146 struct singleLine * line;
3147
3148 for (line = config->theLines; line; line = line->next)
3149 if (line->type == LT_BOOT) break;
3150
3151 if (!line) {
3152 fprintf(stderr,
3153 _("grubby: no boot line found in lilo configuration\n"));
3154 return 1;
3155 }
3156
3157 if (line->numElements != 2) return 1;
3158
3159 fd = open("/boot/boot.b", O_RDONLY);
3160 if (fd < 0) {
3161 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3162 "/boot/boot.b", strerror(errno));
3163 return 1;
3164 }
3165
3166 if (read(fd, boot, 512) != 512) {
3167 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3168 "/boot/boot.b", strerror(errno));
3169 return 1;
3170 }
3171 close(fd);
3172
3173 if (!strncmp("/dev/md", line->elements[1].item, 7))
3174 return checkLiloOnRaid(line->elements[1].item, boot);
3175
3176 return checkDeviceBootloader(line->elements[1].item, boot);
3177 }
3178
3179 int checkForGrub2(struct grubConfig * config) {
3180 if (!access("/etc/grub.d/", R_OK))
3181 return 2;
3182
3183 return 1;
3184 }
3185
3186 int checkForGrub(struct grubConfig * config) {
3187 int fd;
3188 unsigned char bootSect[512];
3189 char * boot;
3190 int onSuse = isSuseSystem();
3191
3192
3193 if (onSuse) {
3194 if (parseSuseGrubConf(NULL, &boot))
3195 return 0;
3196 } else {
3197 if (parseSysconfigGrub(NULL, &boot))
3198 return 0;
3199 }
3200
3201 /* assume grub is not installed -- not an error condition */
3202 if (!boot)
3203 return 0;
3204
3205 fd = open("/boot/grub/stage1", O_RDONLY);
3206 if (fd < 0)
3207 /* this doesn't exist if grub hasn't been installed */
3208 return 0;
3209
3210 if (read(fd, bootSect, 512) != 512) {
3211 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3212 "/boot/grub/stage1", strerror(errno));
3213 close(fd);
3214 return 1;
3215 }
3216 close(fd);
3217
3218 /* The more elaborate checks do not work on SuSE. The checks done
3219 * seem to be reasonble (at least for now), so just return success
3220 */
3221 if (onSuse)
3222 return 2;
3223
3224 return checkDeviceBootloader(boot, bootSect);
3225 }
3226
3227 int checkForExtLinux(struct grubConfig * config) {
3228 int fd;
3229 unsigned char bootSect[512];
3230 char * boot;
3231 char executable[] = "/boot/extlinux/extlinux";
3232
3233 printf("entered: checkForExtLinux()\n");
3234
3235 if (parseSysconfigGrub(NULL, &boot))
3236 return 0;
3237
3238 /* assume grub is not installed -- not an error condition */
3239 if (!boot)
3240 return 0;
3241
3242 fd = open(executable, O_RDONLY);
3243 if (fd < 0)
3244 /* this doesn't exist if grub hasn't been installed */
3245 return 0;
3246
3247 if (read(fd, bootSect, 512) != 512) {
3248 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3249 executable, strerror(errno));
3250 return 1;
3251 }
3252 close(fd);
3253
3254 return checkDeviceBootloader(boot, bootSect);
3255 }
3256
3257 int checkForYaboot(struct grubConfig * config) {
3258 /*
3259 * This is a simplistic check that we consider good enough for own puporses
3260 *
3261 * If we were to properly check if yaboot is *installed* we'd need to:
3262 * 1) get the system boot device (LT_BOOT)
3263 * 2) considering it's a raw filesystem, check if the yaboot binary matches
3264 * the content on the boot device
3265 * 3) if not, copy the binary to a temporary file and run "addnote" on it
3266 * 4) check again if binary and boot device contents match
3267 */
3268 if (!access("/etc/yaboot.conf", R_OK))
3269 return 2;
3270
3271 return 1;
3272 }
3273
3274 int checkForElilo(struct grubConfig * config) {
3275 if (!access("/etc/elilo.conf", R_OK))
3276 return 2;
3277
3278 return 1;
3279 }
3280
3281 static char * getRootSpecifier(char * str) {
3282 char * idx, * rootspec = NULL;
3283
3284 if (*str == '(') {
3285 idx = rootspec = strdup(str);
3286 while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3287 *(++idx) = '\0';
3288 }
3289 return rootspec;
3290 }
3291
3292 static char * getInitrdVal(struct grubConfig * config,
3293 const char * prefix, struct singleLine *tmplLine,
3294 const char * newKernelInitrd,
3295 const char ** extraInitrds, int extraInitrdCount)
3296 {
3297 char *initrdVal, *end;
3298 int i;
3299 size_t totalSize;
3300 size_t prefixLen;
3301 char separatorChar;
3302
3303 prefixLen = strlen(prefix);
3304 totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3305
3306 for (i = 0; i < extraInitrdCount; i++) {
3307 totalSize += sizeof(separatorChar);
3308 totalSize += strlen(extraInitrds[i]) - prefixLen;
3309 }
3310
3311 initrdVal = end = malloc(totalSize);
3312
3313 end = stpcpy (end, newKernelInitrd + prefixLen);
3314
3315 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3316 for (i = 0; i < extraInitrdCount; i++) {
3317 const char *extraInitrd;
3318 int j;
3319
3320 extraInitrd = extraInitrds[i] + prefixLen;
3321 /* Don't add entries that are already there */
3322 if (tmplLine != NULL) {
3323 for (j = 2; j < tmplLine->numElements; j++)
3324 if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3325 break;
3326
3327 if (j != tmplLine->numElements)
3328 continue;
3329 }
3330
3331 *end++ = separatorChar;
3332 end = stpcpy(end, extraInitrd);
3333 }
3334
3335 return initrdVal;
3336 }
3337
3338 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3339 const char * prefix,
3340 const char * newKernelPath, const char * newKernelTitle,
3341 const char * newKernelArgs, const char * newKernelInitrd,
3342 const char ** extraInitrds, int extraInitrdCount,
3343 const char * newMBKernel, const char * newMBKernelArgs) {
3344 struct singleEntry * new;
3345 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3346 int needs;
3347 char * chptr;
3348
3349 if (!newKernelPath) return 0;
3350
3351 /* if the newKernelTitle is too long silently munge it into something
3352 * we can live with. truncating is first check, then we'll just mess with
3353 * it until it looks better */
3354 if (config->cfi->maxTitleLength &&
3355 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3356 char * buf = alloca(config->cfi->maxTitleLength + 7);
3357 char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3358 int i = 1;
3359
3360 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3361 while (findEntryByPath(config, buf, NULL, NULL)) {
3362 sprintf(numBuf, "%d", i++);
3363 strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3364 }
3365
3366 newKernelTitle = buf + 6;
3367 }
3368
3369 new = malloc(sizeof(*new));
3370 new->skip = 0;
3371 new->multiboot = 0;
3372 new->next = config->entries;
3373 new->lines = NULL;
3374 config->entries = new;
3375
3376 /* copy/update from the template */
3377 needs = NEED_KERNEL | NEED_TITLE;
3378 if (newKernelInitrd)
3379 needs |= NEED_INITRD;
3380 if (newMBKernel) {
3381 needs |= NEED_MB;
3382 new->multiboot = 1;
3383 }
3384
3385 if (template) {
3386 for (masterLine = template->lines;
3387 masterLine && (tmplLine = lineDup(masterLine));
3388 lineFree(tmplLine), masterLine = masterLine->next)
3389 {
3390 dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3391
3392 /* skip comments */
3393 chptr = tmplLine->indent;
3394 while (*chptr && isspace(*chptr)) chptr++;
3395 if (*chptr == '#') continue;
3396
3397 if (tmplLine->type == LT_KERNEL &&
3398 tmplLine->numElements >= 2) {
3399 if (!template->multiboot && (needs & NEED_MB)) {
3400 /* it's not a multiboot template and this is the kernel
3401 * line. Try to be intelligent about inserting the
3402 * hypervisor at the same time.
3403 */
3404 if (config->cfi->mbHyperFirst) {
3405 /* insert the hypervisor first */
3406 newLine = addLine(new, config->cfi, LT_HYPER,
3407 tmplLine->indent,
3408 newMBKernel + strlen(prefix));
3409 /* set up for adding the kernel line */
3410 free(tmplLine->indent);
3411 tmplLine->indent = strdup(config->secondaryIndent);
3412 needs &= ~NEED_MB;
3413 }
3414 if (needs & NEED_KERNEL) {
3415 /* use addLineTmpl to preserve line elements,
3416 * otherwise we could just call addLine. Unfortunately
3417 * this means making some changes to the template
3418 * such as the indent change above and the type
3419 * change below.
3420 */
3421 struct keywordTypes * mbm_kw =
3422 getKeywordByType(LT_MBMODULE, config->cfi);
3423 if (mbm_kw) {
3424 tmplLine->type = LT_MBMODULE;
3425 free(tmplLine->elements[0].item);
3426 tmplLine->elements[0].item = strdup(mbm_kw->key);
3427 }
3428 newLine = addLineTmpl(new, tmplLine, newLine,
3429 newKernelPath + strlen(prefix), config->cfi);
3430 needs &= ~NEED_KERNEL;
3431 }
3432 if (needs & NEED_MB) { /* !mbHyperFirst */
3433 newLine = addLine(new, config->cfi, LT_HYPER,
3434 config->secondaryIndent,
3435 newMBKernel + strlen(prefix));
3436 needs &= ~NEED_MB;
3437 }
3438 } else if (needs & NEED_KERNEL) {
3439 newLine = addLineTmpl(new, tmplLine, newLine,
3440 newKernelPath + strlen(prefix), config->cfi);
3441 needs &= ~NEED_KERNEL;
3442 }
3443
3444 } else if (tmplLine->type == LT_HYPER &&
3445 tmplLine->numElements >= 2) {
3446 if (needs & NEED_MB) {
3447 newLine = addLineTmpl(new, tmplLine, newLine,
3448 newMBKernel + strlen(prefix), config->cfi);
3449 needs &= ~NEED_MB;
3450 }
3451
3452 } else if (tmplLine->type == LT_MBMODULE &&
3453 tmplLine->numElements >= 2) {
3454 if (new->multiboot) {
3455 if (needs & NEED_KERNEL) {
3456 newLine = addLineTmpl(new, tmplLine, newLine,
3457 newKernelPath +
3458 strlen(prefix), config->cfi);
3459 needs &= ~NEED_KERNEL;
3460 } else if (config->cfi->mbInitRdIsModule &&
3461 (needs & NEED_INITRD)) {
3462 char *initrdVal;
3463 initrdVal = getInitrdVal(config, prefix, tmplLine,
3464 newKernelInitrd, extraInitrds,
3465 extraInitrdCount);
3466 newLine = addLineTmpl(new, tmplLine, newLine,
3467 initrdVal, config->cfi);
3468 free(initrdVal);
3469 needs &= ~NEED_INITRD;
3470 }
3471 } else if (needs & NEED_KERNEL) {
3472 /* template is multi but new is not,
3473 * insert the kernel in the first module slot
3474 */
3475 tmplLine->type = LT_KERNEL;
3476 free(tmplLine->elements[0].item);
3477 tmplLine->elements[0].item =
3478 strdup(getKeywordByType(LT_KERNEL, config->cfi)->key);
3479 newLine = addLineTmpl(new, tmplLine, newLine,
3480 newKernelPath + strlen(prefix), config->cfi);
3481 needs &= ~NEED_KERNEL;
3482 } else if (needs & NEED_INITRD) {
3483 char *initrdVal;
3484 /* template is multi but new is not,
3485 * insert the initrd in the second module slot
3486 */
3487 tmplLine->type = LT_INITRD;
3488 free(tmplLine->elements[0].item);
3489 tmplLine->elements[0].item =
3490 strdup(getKeywordByType(LT_INITRD, config->cfi)->key);
3491 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3492 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3493 free(initrdVal);
3494 needs &= ~NEED_INITRD;
3495 }
3496
3497 } else if (tmplLine->type == LT_INITRD &&
3498 tmplLine->numElements >= 2) {
3499 if (needs & NEED_INITRD &&
3500 new->multiboot && !template->multiboot &&
3501 config->cfi->mbInitRdIsModule) {
3502 /* make sure we don't insert the module initrd
3503 * before the module kernel... if we don't do it here,
3504 * it will be inserted following the template.
3505 */
3506 if (!needs & NEED_KERNEL) {
3507 char *initrdVal;
3508
3509 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3510 newLine = addLine(new, config->cfi, LT_MBMODULE,
3511 config->secondaryIndent,
3512 initrdVal);
3513 free(initrdVal);
3514 needs &= ~NEED_INITRD;
3515 }
3516 } else if (needs & NEED_INITRD) {
3517 char *initrdVal;
3518 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3519 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3520 free(initrdVal);
3521 needs &= ~NEED_INITRD;
3522 }
3523
3524 } else if (tmplLine->type == LT_MENUENTRY &&
3525 (needs & NEED_TITLE)) {
3526 requote(tmplLine, config->cfi);
3527 char *nkt = malloc(strlen(newKernelTitle)+3);
3528 strcpy(nkt, "'");
3529 strcat(nkt, newKernelTitle);
3530 strcat(nkt, "'");
3531 newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3532 free(nkt);
3533 needs &= ~NEED_TITLE;
3534 } else if (tmplLine->type == LT_TITLE &&
3535 (needs & NEED_TITLE)) {
3536 if (tmplLine->numElements >= 2) {
3537 newLine = addLineTmpl(new, tmplLine, newLine,
3538 newKernelTitle, config->cfi);
3539 needs &= ~NEED_TITLE;
3540 } else if (tmplLine->numElements == 1 &&
3541 config->cfi->titleBracketed) {
3542 /* addLineTmpl doesn't handle titleBracketed */
3543 newLine = addLine(new, config->cfi, LT_TITLE,
3544 tmplLine->indent, newKernelTitle);
3545 needs &= ~NEED_TITLE;
3546 }
3547 } else if (tmplLine->type == LT_ECHO) {
3548 requote(tmplLine, config->cfi);
3549 static const char *prefix = "'Loading ";
3550 if (tmplLine->numElements > 1 &&
3551 strstr(tmplLine->elements[1].item, prefix) &&
3552 masterLine->next && masterLine->next->type == LT_KERNEL) {
3553 char *newTitle = malloc(strlen(prefix) +
3554 strlen(newKernelTitle) + 2);
3555
3556 strcpy(newTitle, prefix);
3557 strcat(newTitle, newKernelTitle);
3558 strcat(newTitle, "'");
3559 newLine = addLine(new, config->cfi, LT_ECHO,
3560 tmplLine->indent, newTitle);
3561 free(newTitle);
3562 } else {
3563 /* pass through other lines from the template */
3564 newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3565 config->cfi);
3566 }
3567 } else {
3568 /* pass through other lines from the template */
3569 newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3570 }
3571 }
3572
3573 } else {
3574 /* don't have a template, so start the entry with the
3575 * appropriate starting line
3576 */
3577 switch (config->cfi->entryStart) {
3578 case LT_KERNEL:
3579 if (new->multiboot && config->cfi->mbHyperFirst) {
3580 /* fall through to LT_HYPER */
3581 } else {
3582 newLine = addLine(new, config->cfi, LT_KERNEL,
3583 config->primaryIndent,
3584 newKernelPath + strlen(prefix));
3585 needs &= ~NEED_KERNEL;
3586 break;
3587 }
3588
3589 case LT_HYPER:
3590 newLine = addLine(new, config->cfi, LT_HYPER,
3591 config->primaryIndent,
3592 newMBKernel + strlen(prefix));
3593 needs &= ~NEED_MB;
3594 break;
3595
3596 case LT_MENUENTRY: {
3597 char *nkt = malloc(strlen(newKernelTitle)+3);
3598 strcpy(nkt, "'");
3599 strcat(nkt, newKernelTitle);
3600 strcat(nkt, "'");
3601 newLine = addLine(new, config->cfi, LT_MENUENTRY,
3602 config->primaryIndent, nkt);
3603 free(nkt);
3604 needs &= ~NEED_TITLE;
3605 needs |= NEED_END;
3606 break;
3607 }
3608 case LT_TITLE:
3609 if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
3610 char * templabel;
3611 int x = 0, y = 0;
3612
3613 templabel = strdup(newKernelTitle);
3614 while( templabel[x]){
3615 if( templabel[x] == ' ' ){
3616 y = x;
3617 while( templabel[y] ){
3618 templabel[y] = templabel[y+1];
3619 y++;
3620 }
3621 }
3622 x++;
3623 }
3624 newLine = addLine(new, config->cfi, LT_TITLE,
3625 config->primaryIndent, templabel);
3626 free(templabel);
3627 }else{
3628 newLine = addLine(new, config->cfi, LT_TITLE,
3629 config->primaryIndent, newKernelTitle);
3630 }
3631 needs &= ~NEED_TITLE;
3632 break;
3633
3634 default:
3635 abort();
3636 }
3637 }
3638
3639 /* add the remainder of the lines, i.e. those that either
3640 * weren't present in the template, or in the case of no template,
3641 * all the lines following the entryStart.
3642 */
3643 if (needs & NEED_TITLE) {
3644 newLine = addLine(new, config->cfi, LT_TITLE,
3645 config->secondaryIndent,
3646 newKernelTitle);
3647 needs &= ~NEED_TITLE;
3648 }
3649 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
3650 newLine = addLine(new, config->cfi, LT_HYPER,
3651 config->secondaryIndent,
3652 newMBKernel + strlen(prefix));
3653 needs &= ~NEED_MB;
3654 }
3655 if (needs & NEED_KERNEL) {
3656 newLine = addLine(new, config->cfi,
3657 (new->multiboot && getKeywordByType(LT_MBMODULE,
3658 config->cfi)) ?
3659 LT_MBMODULE : LT_KERNEL,
3660 config->secondaryIndent,
3661 newKernelPath + strlen(prefix));
3662 needs &= ~NEED_KERNEL;
3663 }
3664 if (needs & NEED_MB) {
3665 newLine = addLine(new, config->cfi, LT_HYPER,
3666 config->secondaryIndent,
3667 newMBKernel + strlen(prefix));
3668 needs &= ~NEED_MB;
3669 }
3670 if (needs & NEED_INITRD) {
3671 char *initrdVal;
3672 initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
3673 newLine = addLine(new, config->cfi,
3674 (new->multiboot && getKeywordByType(LT_MBMODULE,
3675 config->cfi)) ?
3676 LT_MBMODULE : LT_INITRD,
3677 config->secondaryIndent,
3678 initrdVal);
3679 free(initrdVal);
3680 needs &= ~NEED_INITRD;
3681 }
3682 if (needs & NEED_END) {
3683 newLine = addLine(new, config->cfi, LT_ENTRY_END,
3684 config->secondaryIndent, NULL);
3685 needs &= ~NEED_END;
3686 }
3687
3688 if (needs) {
3689 printf(_("grubby: needs=%d, aborting\n"), needs);
3690 abort();
3691 }
3692
3693 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
3694 newMBKernelArgs, NULL)) return 1;
3695
3696 return 0;
3697 }
3698
3699 static void traceback(int signum)
3700 {
3701 void *array[40];
3702 size_t size;
3703
3704 signal(SIGSEGV, SIG_DFL);
3705 memset(array, '\0', sizeof (array));
3706 size = backtrace(array, 40);
3707
3708 fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n",
3709 (unsigned long)size);
3710 backtrace_symbols_fd(array, size, STDERR_FILENO);
3711 exit(1);
3712 }
3713
3714 int main(int argc, const char ** argv) {
3715 poptContext optCon;
3716 const char * grubConfig = NULL;
3717 char * outputFile = NULL;
3718 int arg = 0;
3719 int flags = 0;
3720 int badImageOkay = 0;
3721 int configureGrub2 = 0;
3722 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
3723 int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
3724 int configureExtLinux = 0;
3725 int bootloaderProbe = 0;
3726 int extraInitrdCount = 0;
3727 char * updateKernelPath = NULL;
3728 char * newKernelPath = NULL;
3729 char * removeKernelPath = NULL;
3730 char * newKernelArgs = NULL;
3731 char * newKernelInitrd = NULL;
3732 char * newKernelTitle = NULL;
3733 char * newKernelVersion = NULL;
3734 char * newMBKernel = NULL;
3735 char * newMBKernelArgs = NULL;
3736 char * removeMBKernelArgs = NULL;
3737 char * removeMBKernel = NULL;
3738 char * bootPrefix = NULL;
3739 char * defaultKernel = NULL;
3740 char * removeArgs = NULL;
3741 char * kernelInfo = NULL;
3742 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
3743 const char * chptr = NULL;
3744 struct configFileInfo * cfi = NULL;
3745 struct grubConfig * config;
3746 struct singleEntry * template = NULL;
3747 int copyDefault = 0, makeDefault = 0;
3748 int displayDefault = 0;
3749 int displayDefaultIndex = 0;
3750 int displayDefaultTitle = 0;
3751 int defaultIndex = -1;
3752 struct poptOption options[] = {
3753 { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
3754 _("add an entry for the specified kernel"), _("kernel-path") },
3755 { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
3756 _("add an entry for the specified multiboot kernel"), NULL },
3757 { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
3758 _("default arguments for the new kernel or new arguments for "
3759 "kernel being updated"), _("args") },
3760 { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
3761 _("default arguments for the new multiboot kernel or "
3762 "new arguments for multiboot kernel being updated"), NULL },
3763 { "bad-image-okay", 0, 0, &badImageOkay, 0,
3764 _("don't sanity check images in boot entries (for testing only)"),
3765 NULL },
3766 { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
3767 _("filestystem which contains /boot directory (for testing only)"),
3768 _("bootfs") },
3769 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
3770 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
3771 _("check which bootloader is installed on boot sector") },
3772 #endif
3773 { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
3774 _("path to grub config file to update (\"-\" for stdin)"),
3775 _("path") },
3776 { "copy-default", 0, 0, &copyDefault, 0,
3777 _("use the default boot entry as a template for the new entry "
3778 "being added; if the default is not a linux image, or if "
3779 "the kernel referenced by the default image does not exist, "
3780 "the first linux entry whose kernel does exist is used as the "
3781 "template"), NULL },
3782 { "debug", 0, 0, &debug, 0,
3783 _("print debugging information for failures") },
3784 { "default-kernel", 0, 0, &displayDefault, 0,
3785 _("display the path of the default kernel") },
3786 { "default-index", 0, 0, &displayDefaultIndex, 0,
3787 _("display the index of the default kernel") },
3788 { "default-title", 0, 0, &displayDefaultTitle, 0,
3789 _("display the title of the default kernel") },
3790 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
3791 _("configure elilo bootloader") },
3792 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
3793 _("configure extlinux bootloader (from syslinux)") },
3794 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
3795 _("configure grub bootloader") },
3796 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
3797 _("configure grub2 bootloader") },
3798 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
3799 _("display boot information for specified kernel"),
3800 _("kernel-path") },
3801 { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
3802 _("initrd image for the new kernel"), _("initrd-path") },
3803 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
3804 _("auxilliary initrd image for things other than the new kernel"), _("initrd-path") },
3805 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
3806 _("configure lilo bootloader") },
3807 { "make-default", 0, 0, &makeDefault, 0,
3808 _("make the newly added entry the default boot entry"), NULL },
3809 { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
3810 _("path to output updated config file (\"-\" for stdout)"),
3811 _("path") },
3812 { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
3813 _("remove kernel arguments"), NULL },
3814 { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
3815 _("remove multiboot kernel arguments"), NULL },
3816 { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
3817 _("remove all entries for the specified kernel"),
3818 _("kernel-path") },
3819 { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
3820 _("remove all entries for the specified multiboot kernel"), NULL },
3821 { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
3822 _("make the first entry referencing the specified kernel "
3823 "the default"), _("kernel-path") },
3824 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
3825 _("make the given entry index the default entry"),
3826 _("entry-index") },
3827 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
3828 _("configure silo bootloader") },
3829 { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
3830 _("title to use for the new kernel entry"), _("entry-title") },
3831 { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
3832 _("updated information for the specified kernel"),
3833 _("kernel-path") },
3834 { "version", 'v', 0, NULL, 'v',
3835 _("print the version of this program and exit"), NULL },
3836 { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
3837 _("configure yaboot bootloader") },
3838 { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
3839 _("configure zipl bootloader") },
3840 POPT_AUTOHELP
3841 { 0, 0, 0, 0, 0 }
3842 };
3843
3844 useextlinuxmenu=0;
3845
3846 signal(SIGSEGV, traceback);
3847
3848 optCon = poptGetContext("grubby", argc, argv, options, 0);
3849 poptReadDefaultConfig(optCon, 1);
3850
3851 while ((arg = poptGetNextOpt(optCon)) >= 0) {
3852 switch (arg) {
3853 case 'v':
3854 printf("grubby version %s\n", VERSION);
3855 exit(0);
3856 break;
3857 case 'i':
3858 if (extraInitrdCount < MAX_EXTRA_INITRDS) {
3859 extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
3860 } else {
3861 fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
3862 return 1;
3863 }
3864 break;
3865 }
3866 }
3867
3868 if (arg < -1) {
3869 fprintf(stderr, _("grubby: bad argument %s: %s\n"),
3870 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
3871 poptStrerror(arg));
3872 return 1;
3873 }
3874
3875 if ((chptr = poptGetArg(optCon))) {
3876 fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
3877 return 1;
3878 }
3879
3880 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
3881 configureYaboot + configureSilo + configureZipl +
3882 configureExtLinux ) > 1) {
3883 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
3884 return 1;
3885 } else if (bootloaderProbe && grubConfig) {
3886 fprintf(stderr,
3887 _("grubby: cannot specify config file with --bootloader-probe\n"));
3888 return 1;
3889 } else if (configureGrub2) {
3890 cfi = &grub2ConfigType;
3891 } else if (configureLilo) {
3892 cfi = &liloConfigType;
3893 } else if (configureGrub) {
3894 cfi = &grubConfigType;
3895 } else if (configureELilo) {
3896 cfi = &eliloConfigType;
3897 } else if (configureYaboot) {
3898 cfi = &yabootConfigType;
3899 } else if (configureSilo) {
3900 cfi = &siloConfigType;
3901 } else if (configureZipl) {
3902 cfi = &ziplConfigType;
3903 } else if (configureExtLinux) {
3904 cfi = &extlinuxConfigType;
3905 useextlinuxmenu=1;
3906 }
3907
3908 if (!cfi) {
3909 if (grub2FindConfig(&grub2ConfigType))
3910 cfi = &grub2ConfigType;
3911 else
3912 #ifdef __ia64__
3913 cfi = &eliloConfigType;
3914 #elif __powerpc__
3915 cfi = &yabootConfigType;
3916 #elif __sparc__
3917 cfi = &siloConfigType;
3918 #elif __s390__
3919 cfi = &ziplConfigType;
3920 #elif __s390x__
3921 cfi = &ziplConfigtype;
3922 #else
3923 cfi = &grubConfigType;
3924 #endif
3925 }
3926
3927 if (!grubConfig) {
3928 if (cfi->findConfig)
3929 grubConfig = cfi->findConfig(cfi);
3930 if (!grubConfig)
3931 grubConfig = cfi->defaultConfig;
3932 }
3933
3934 if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
3935 newKernelPath || removeKernelPath || makeDefault ||
3936 defaultKernel || displayDefaultIndex || displayDefaultTitle ||
3937 (defaultIndex >= 0))) {
3938 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
3939 "specified option"));
3940 return 1;
3941 }
3942
3943 if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
3944 removeKernelPath)) {
3945 fprintf(stderr, _("grubby: --default-kernel and --info may not "
3946 "be used when adding or removing kernels\n"));
3947 return 1;
3948 }
3949
3950 if (newKernelPath && !newKernelTitle) {
3951 fprintf(stderr, _("grubby: kernel title must be specified\n"));
3952 return 1;
3953 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
3954 (newKernelInitrd && !updateKernelPath)||
3955 makeDefault || extraInitrdCount > 0)) {
3956 fprintf(stderr, _("grubby: kernel path expected\n"));
3957 return 1;
3958 }
3959
3960 if (newKernelPath && updateKernelPath) {
3961 fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
3962 "not be used together"));
3963 return 1;
3964 }
3965
3966 if (makeDefault && defaultKernel) {
3967 fprintf(stderr, _("grubby: --make-default and --default-kernel "
3968 "may not be used together\n"));
3969 return 1;
3970 } else if (defaultKernel && removeKernelPath &&
3971 !strcmp(defaultKernel, removeKernelPath)) {
3972 fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
3973 return 1;
3974 } else if (defaultKernel && newKernelPath &&
3975 !strcmp(defaultKernel, newKernelPath)) {
3976 makeDefault = 1;
3977 defaultKernel = NULL;
3978 }
3979 else if (defaultKernel && (defaultIndex >= 0)) {
3980 fprintf(stderr, _("grubby: --set-default and --set-default-index "
3981 "may not be used together\n"));
3982 return 1;
3983 }
3984
3985 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
3986 fprintf(stderr, _("grubby: output file must be specified if stdin "
3987 "is used\n"));
3988 return 1;
3989 }
3990
3991 if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
3992 && !kernelInfo && !bootloaderProbe && !updateKernelPath
3993 && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
3994 && (defaultIndex == -1)) {
3995 fprintf(stderr, _("grubby: no action specified\n"));
3996 return 1;
3997 }
3998
3999 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4000
4001 if (cfi->needsBootPrefix) {
4002 if (!bootPrefix) {
4003 bootPrefix = findBootPrefix();
4004 if (!bootPrefix) return 1;
4005 } else {
4006 /* this shouldn't end with a / */
4007 if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4008 bootPrefix[strlen(bootPrefix) - 1] = '\0';
4009 }
4010 } else {
4011 bootPrefix = "";
4012 }
4013
4014 if (!cfi->mbAllowExtraInitRds &&
4015 extraInitrdCount > 0) {
4016 fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4017 return 1;
4018 }
4019
4020 if (bootloaderProbe) {
4021 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4022 struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4023
4024 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4025 if (grub2config) {
4026 gconfig = readConfig(grub2config, &grub2ConfigType);
4027 if (!gconfig)
4028 gr2c = 1;
4029 else
4030 gr2c = checkForGrub2(gconfig);
4031 }
4032
4033 const char *grubconfig = grubFindConfig(&grubConfigType);
4034 if (!access(grubconfig, F_OK)) {
4035 gconfig = readConfig(grubconfig, &grubConfigType);
4036 if (!gconfig)
4037 grc = 1;
4038 else
4039 grc = checkForGrub(gconfig);
4040 }
4041
4042 if (!access(liloConfigType.defaultConfig, F_OK)) {
4043 lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4044 if (!lconfig)
4045 lrc = 1;
4046 else
4047 lrc = checkForLilo(lconfig);
4048 }
4049
4050 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4051 econfig = readConfig(eliloConfigType.defaultConfig,
4052 &eliloConfigType);
4053 if (!econfig)
4054 erc = 1;
4055 else
4056 erc = checkForElilo(econfig);
4057 }
4058
4059 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4060 lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4061 if (!lconfig)
4062 extrc = 1;
4063 else
4064 extrc = checkForExtLinux(lconfig);
4065 }
4066
4067
4068 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4069 yconfig = readConfig(yabootConfigType.defaultConfig,
4070 &yabootConfigType);
4071 if (!yconfig)
4072 yrc = 1;
4073 else
4074 yrc = checkForYaboot(yconfig);
4075 }
4076
4077 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4078 erc == 1)
4079 return 1;
4080
4081 if (lrc == 2) printf("lilo\n");
4082 if (gr2c == 2) printf("grub2\n");
4083 if (grc == 2) printf("grub\n");
4084 if (extrc == 2) printf("extlinux\n");
4085 if (yrc == 2) printf("yaboot\n");
4086 if (erc == 2) printf("elilo\n");
4087
4088 return 0;
4089 }
4090
4091 config = readConfig(grubConfig, cfi);
4092 if (!config) return 1;
4093
4094 if (displayDefault) {
4095 struct singleLine * line;
4096 struct singleEntry * entry;
4097 char * rootspec;
4098
4099 if (config->defaultImage == -1) return 0;
4100 entry = findEntryByIndex(config, config->defaultImage);
4101 if (!entry) return 0;
4102 if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4103
4104 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
4105 if (!line) return 0;
4106
4107 rootspec = getRootSpecifier(line->elements[1].item);
4108 printf("%s%s\n", bootPrefix, line->elements[1].item +
4109 ((rootspec != NULL) ? strlen(rootspec) : 0));
4110
4111 return 0;
4112
4113 } else if (displayDefaultTitle) {
4114 struct singleLine * line;
4115 struct singleEntry * entry;
4116
4117 if (config->defaultImage == -1) return 0;
4118 entry = findEntryByIndex(config, config->defaultImage);
4119 if (!entry) return 0;
4120
4121 if (!configureGrub2) {
4122 line = getLineByType(LT_TITLE, entry->lines);
4123 if (!line) return 0;
4124 printf("%s\n", line->elements[1].item);
4125
4126 } else {
4127 char * title;
4128
4129 dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4130 line = getLineByType(LT_MENUENTRY, entry->lines);
4131 if (!line) return 0;
4132 title = grub2ExtractTitle(line);
4133 if (title)
4134 printf("%s\n", title);
4135 }
4136 return 0;
4137
4138 } else if (displayDefaultIndex) {
4139 if (config->defaultImage == -1) return 0;
4140 printf("%i\n", config->defaultImage);
4141
4142 } else if (kernelInfo)
4143 return displayInfo(config, kernelInfo, bootPrefix);
4144
4145 if (copyDefault) {
4146 template = findTemplate(config, bootPrefix, NULL, 0, flags);
4147 if (!template) return 1;
4148 }
4149
4150 markRemovedImage(config, removeKernelPath, bootPrefix);
4151 markRemovedImage(config, removeMBKernel, bootPrefix);
4152 setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4153 bootPrefix, flags, defaultIndex);
4154 setFallbackImage(config, newKernelPath != NULL);
4155 if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4156 removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4157 if (updateKernelPath && newKernelInitrd) {
4158 if (updateInitrd(config, updateKernelPath, bootPrefix,
4159 newKernelInitrd)) return 1;
4160 }
4161 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4162 newKernelTitle, newKernelArgs, newKernelInitrd,
4163 (const char **)extraInitrds, extraInitrdCount,
4164 newMBKernel, newMBKernelArgs)) return 1;
4165
4166
4167 if (numEntries(config) == 0) {
4168 fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4169 "Not writing out new config.\n"));
4170 return 1;
4171 }
4172
4173 if (!outputFile)
4174 outputFile = (char *)grubConfig;
4175
4176 return writeConfig(config, outputFile, bootPrefix);
4177 }