Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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