Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1933 - (show annotations) (download)
Mon Oct 1 12:07:35 2012 UTC (11 years, 6 months ago) by niro
File MIME type: text/plain
File size: 116707 byte(s)
tagged 'grubby-8_16'
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 free(dir);
1318 do {
1319 buf = alloca(len + 1);
1320 rc = readlink(basename(outName), buf, len);
1321 if (rc == len) len += 256;
1322 } while (rc == len);
1323
1324 if (rc < 0) {
1325 fprintf(stderr, _("grubby: error readlink link %s: %s\n"),
1326 outName, strerror(errno));
1327 return 1;
1328 }
1329
1330 outName = buf;
1331 outName[rc] = '\0';
1332 }
1333
1334 tmpOutName = alloca(strlen(outName) + 2);
1335 sprintf(tmpOutName, "%s-", outName);
1336 out = fopen(tmpOutName, "w");
1337 if (!out) {
1338 fprintf(stderr, _("grubby: error creating %s: %s\n"), tmpOutName,
1339 strerror(errno));
1340 return 1;
1341 }
1342
1343 if (!stat(outName, &sb)) {
1344 if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1345 fprintf(stderr, _("grubby: error setting perms on %s: %s\n"),
1346 tmpOutName, strerror(errno));
1347 fclose(out);
1348 unlink(tmpOutName);
1349 return 1;
1350 }
1351 }
1352 }
1353
1354 line = cfg->theLines;
1355 struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1356 while (line) {
1357 if (line->type == LT_SET_VARIABLE && defaultKw &&
1358 line->numElements == 3 &&
1359 !strcmp(line->elements[1].item, defaultKw->key)) {
1360 writeDefault(out, line->indent, line->elements[0].indent, cfg);
1361 needs &= ~MAIN_DEFAULT;
1362 } else if (line->type == LT_DEFAULT) {
1363 writeDefault(out, line->indent, line->elements[0].indent, cfg);
1364 needs &= ~MAIN_DEFAULT;
1365 } else if (line->type == LT_FALLBACK) {
1366 if (cfg->fallbackImage > -1)
1367 fprintf(out, "%s%s%s%d\n", line->indent,
1368 line->elements[0].item, line->elements[0].indent,
1369 cfg->fallbackImage);
1370 } else {
1371 if (lineWrite(out, line, cfg->cfi) == -1) {
1372 fprintf(stderr, _("grubby: error writing %s: %s\n"),
1373 tmpOutName, strerror(errno));
1374 fclose(out);
1375 unlink(tmpOutName);
1376 return 1;
1377 }
1378 }
1379
1380 line = line->next;
1381 }
1382
1383 if (needs & MAIN_DEFAULT) {
1384 writeDefault(out, cfg->primaryIndent, "=", cfg);
1385 needs &= ~MAIN_DEFAULT;
1386 }
1387
1388 i = 0;
1389 while ((entry = findEntryByIndex(cfg, i++))) {
1390 if (entry->skip) continue;
1391
1392 line = entry->lines;
1393 while (line) {
1394 if (lineWrite(out, line, cfg->cfi) == -1) {
1395 fprintf(stderr, _("grubby: error writing %s: %s\n"),
1396 tmpOutName, strerror(errno));
1397 fclose(out);
1398 unlink(tmpOutName);
1399 return 1;
1400 }
1401 line = line->next;
1402 }
1403 }
1404
1405 if (tmpOutName) {
1406 if (rename(tmpOutName, outName)) {
1407 fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),
1408 tmpOutName, outName, strerror(errno));
1409 unlink(outName);
1410 return 1;
1411 }
1412 }
1413
1414 return 0;
1415 }
1416
1417 static int numEntries(struct grubConfig *cfg) {
1418 int i = 0;
1419 struct singleEntry * entry;
1420
1421 entry = cfg->entries;
1422 while (entry) {
1423 if (!entry->skip)
1424 i++;
1425 entry = entry->next;
1426 }
1427 return i;
1428 }
1429
1430 static char *findDiskForRoot()
1431 {
1432 int fd;
1433 char buf[65536];
1434 char *devname;
1435 char *chptr;
1436 int rc;
1437
1438 if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1439 fprintf(stderr, "grubby: failed to open %s: %s\n",
1440 _PATH_MOUNTED, strerror(errno));
1441 return NULL;
1442 }
1443
1444 rc = read(fd, buf, sizeof(buf) - 1);
1445 if (rc <= 0) {
1446 fprintf(stderr, "grubby: failed to read %s: %s\n",
1447 _PATH_MOUNTED, strerror(errno));
1448 close(fd);
1449 return NULL;
1450 }
1451 close(fd);
1452 buf[rc] = '\0';
1453 chptr = buf;
1454
1455 char *foundanswer = NULL;
1456
1457 while (chptr && chptr != buf+rc) {
1458 devname = chptr;
1459
1460 /*
1461 * The first column of a mtab entry is the device, but if the entry is a
1462 * special device it won't start with /, so move on to the next line.
1463 */
1464 if (*devname != '/') {
1465 chptr = strchr(chptr, '\n');
1466 if (chptr)
1467 chptr++;
1468 continue;
1469 }
1470
1471 /* Seek to the next space */
1472 chptr = strchr(chptr, ' ');
1473 if (!chptr) {
1474 fprintf(stderr, "grubby: error parsing %s: %s\n",
1475 _PATH_MOUNTED, strerror(errno));
1476 return NULL;
1477 }
1478
1479 /*
1480 * The second column of a mtab entry is the mount point, we are looking
1481 * for '/' obviously.
1482 */
1483 if (*(++chptr) == '/' && *(++chptr) == ' ') {
1484 /* remember the last / entry in mtab */
1485 foundanswer = devname;
1486 }
1487
1488 /* Next line */
1489 chptr = strchr(chptr, '\n');
1490 if (chptr)
1491 chptr++;
1492 }
1493
1494 /* Return the last / entry found */
1495 if (foundanswer) {
1496 chptr = strchr(foundanswer, ' ');
1497 *chptr = '\0';
1498 return strdup(foundanswer);
1499 }
1500
1501 return NULL;
1502 }
1503
1504 void printEntry(struct singleEntry * entry) {
1505 int i;
1506 struct singleLine * line;
1507
1508 for (line = entry->lines; line; line = line->next) {
1509 fprintf(stderr, "DBG: %s", line->indent);
1510 for (i = 0; i < line->numElements; i++) {
1511 /* Need to handle this, because we strip the quotes from
1512 * menuentry when read it. */
1513 if (line->type == LT_MENUENTRY && i == 1) {
1514 if(!isquote(*line->elements[i].item))
1515 fprintf(stderr, "\'%s\'", line->elements[i].item);
1516 else
1517 fprintf(stderr, "%s", line->elements[i].item);
1518 fprintf(stderr, "%s", line->elements[i].indent);
1519
1520 continue;
1521 }
1522
1523 fprintf(stderr, "%s%s",
1524 line->elements[i].item, line->elements[i].indent);
1525 }
1526 fprintf(stderr, "\n");
1527 }
1528 }
1529
1530 void notSuitablePrintf(struct singleEntry * entry, const char *fmt, ...)
1531 {
1532 va_list argp;
1533
1534 if (!debug)
1535 return;
1536
1537 va_start(argp, fmt);
1538 fprintf(stderr, "DBG: Image entry failed: ");
1539 vfprintf(stderr, fmt, argp);
1540 printEntry(entry);
1541 va_end(argp);
1542 }
1543
1544 #define beginswith(s, c) ((s) && (s)[0] == (c))
1545
1546 static int endswith(const char *s, char c)
1547 {
1548 int slen;
1549
1550 if (!s || !s[0])
1551 return 0;
1552 slen = strlen(s) - 1;
1553
1554 return s[slen] == c;
1555 }
1556
1557 int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1558 int skipRemoved, int flags) {
1559 struct singleLine * line;
1560 char * fullName;
1561 int i;
1562 char * dev;
1563 char * rootspec;
1564 char * rootdev;
1565
1566 if (skipRemoved && entry->skip) {
1567 notSuitablePrintf(entry, "marked to skip\n");
1568 return 0;
1569 }
1570
1571 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1572 if (!line) {
1573 notSuitablePrintf(entry, "no line found\n");
1574 return 0;
1575 }
1576 if (line->numElements < 2) {
1577 notSuitablePrintf(entry, "line has only %d elements\n",
1578 line->numElements);
1579 return 0;
1580 }
1581
1582 if (flags & GRUBBY_BADIMAGE_OKAY) return 1;
1583
1584 fullName = alloca(strlen(bootPrefix) +
1585 strlen(line->elements[1].item) + 1);
1586 rootspec = getRootSpecifier(line->elements[1].item);
1587 int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1588 int hasslash = endswith(bootPrefix, '/') ||
1589 beginswith(line->elements[1].item + rootspec_offset, '/');
1590 sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1591 line->elements[1].item + rootspec_offset);
1592 if (access(fullName, R_OK)) {
1593 notSuitablePrintf(entry, "access to %s failed\n", fullName);
1594 return 0;
1595 }
1596 for (i = 2; i < line->numElements; i++)
1597 if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1598 if (i < line->numElements) {
1599 dev = line->elements[i].item + 5;
1600 } else {
1601 /* look for a lilo style LT_ROOT line */
1602 line = getLineByType(LT_ROOT, entry->lines);
1603
1604 if (line && line->numElements >= 2) {
1605 dev = line->elements[1].item;
1606 } else {
1607 /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1608 * grub+multiboot uses LT_MBMODULE for the args, so check that too.
1609 */
1610 line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
1611
1612 /* failed to find one */
1613 if (!line) {
1614 notSuitablePrintf(entry, "no line found\n");
1615 return 0;
1616 }
1617
1618 for (i = 1; i < line->numElements; i++)
1619 if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1620 if (i < line->numElements)
1621 dev = line->elements[i].item + 5;
1622 else {
1623 notSuitablePrintf(entry, "no root= entry found\n");
1624 /* it failed too... can't find root= */
1625 return 0;
1626 }
1627 }
1628 }
1629
1630 dev = getpathbyspec(dev);
1631 if (!getpathbyspec(dev)) {
1632 notSuitablePrintf(entry, "can't find blkid entry for %s\n", dev);
1633 return 0;
1634 } else
1635 dev = getpathbyspec(dev);
1636
1637 rootdev = findDiskForRoot();
1638 if (!rootdev) {
1639 notSuitablePrintf(entry, "can't find root device\n");
1640 return 0;
1641 }
1642
1643 if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1644 notSuitablePrintf(entry, "uuid missing: rootdev %s, dev %s\n",
1645 getuuidbydev(rootdev), getuuidbydev(dev));
1646 free(rootdev);
1647 return 0;
1648 }
1649
1650 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1651 notSuitablePrintf(entry, "uuid mismatch: rootdev %s, dev %s\n",
1652 getuuidbydev(rootdev), getuuidbydev(dev));
1653 free(rootdev);
1654 return 0;
1655 }
1656
1657 free(rootdev);
1658
1659 return 1;
1660 }
1661
1662 /* returns the first match on or after the one pointed to by index (if index
1663 is not NULL) which is not marked as skip */
1664 struct singleEntry * findEntryByPath(struct grubConfig * config,
1665 const char * kernel, const char * prefix,
1666 int * index) {
1667 struct singleEntry * entry = NULL;
1668 struct singleLine * line;
1669 int i;
1670 char * chptr;
1671 char * rootspec = NULL;
1672 enum lineType_e checkType = LT_KERNEL;
1673
1674 if (isdigit(*kernel)) {
1675 int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
1676
1677 i = 0;
1678 indexVars[i] = strtol(kernel, &chptr, 10);
1679 while (*chptr == ',') {
1680 i++;
1681 kernel = chptr + 1;
1682 indexVars[i] = strtol(kernel, &chptr, 10);
1683 }
1684
1685 if (*chptr) {
1686 /* can't parse it, bail */
1687 return NULL;
1688 }
1689
1690 indexVars[i + 1] = -1;
1691
1692 i = 0;
1693 if (index) {
1694 while (i < *index) i++;
1695 if (indexVars[i] == -1) return NULL;
1696 }
1697
1698 entry = findEntryByIndex(config, indexVars[i]);
1699 if (!entry) return NULL;
1700
1701 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1702 if (!line) return NULL;
1703
1704 if (index) *index = indexVars[i];
1705 return entry;
1706 }
1707
1708 if (!strcmp(kernel, "DEFAULT")) {
1709 if (index && *index > config->defaultImage) {
1710 entry = NULL;
1711 } else {
1712 entry = findEntryByIndex(config, config->defaultImage);
1713 if (entry && entry->skip)
1714 entry = NULL;
1715 else if (index)
1716 *index = config->defaultImage;
1717 }
1718 } else if (!strcmp(kernel, "ALL")) {
1719 if (index)
1720 i = *index;
1721 else
1722 i = 0;
1723
1724 while ((entry = findEntryByIndex(config, i))) {
1725 if (!entry->skip) break;
1726 i++;
1727 }
1728
1729 if (entry && index)
1730 *index = i;
1731 } else {
1732 if (index)
1733 i = *index;
1734 else
1735 i = 0;
1736
1737 if (!strncmp(kernel, "TITLE=", 6)) {
1738 prefix = "";
1739 checkType = LT_TITLE|LT_MENUENTRY;
1740 kernel += 6;
1741 }
1742
1743 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
1744 if (entry->skip) continue;
1745
1746 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
1747
1748 /* check all the lines matching checkType */
1749 for (line = entry->lines; line; line = line->next) {
1750 line = getLineByType(entry->multiboot && checkType == LT_KERNEL ?
1751 LT_KERNEL|LT_MBMODULE|LT_HYPER :
1752 checkType, line);
1753 if (!line) break; /* not found in this entry */
1754
1755 if (line && line->type != LT_MENUENTRY &&
1756 line->numElements >= 2) {
1757 rootspec = getRootSpecifier(line->elements[1].item);
1758 if (!strcmp(line->elements[1].item +
1759 ((rootspec != NULL) ? strlen(rootspec) : 0),
1760 kernel + strlen(prefix)))
1761 break;
1762 }
1763 if(line->type == LT_MENUENTRY &&
1764 !strcmp(line->elements[1].item, kernel))
1765 break;
1766 }
1767
1768 /* make sure this entry has a kernel identifier; this skips
1769 * non-Linux boot entries (could find netbsd etc, though, which is
1770 * unfortunate)
1771 */
1772 if (line && getLineByType(LT_KERNEL|LT_HYPER, entry->lines))
1773 break; /* found 'im! */
1774 }
1775
1776 if (index) *index = i;
1777 }
1778
1779 return entry;
1780 }
1781
1782 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
1783 struct singleEntry * entry;
1784
1785 entry = cfg->entries;
1786 while (index && entry) {
1787 entry = entry->next;
1788 index--;
1789 }
1790
1791 return entry;
1792 }
1793
1794 /* Find a good template to use for the new kernel. An entry is
1795 * good if the kernel and mkinitrd exist (even if the entry
1796 * is going to be removed). Try and use the default entry, but
1797 * if that doesn't work just take the first. If we can't find one,
1798 * bail. */
1799 struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
1800 int * indexPtr, int skipRemoved, int flags) {
1801 struct singleEntry * entry, * entry2;
1802 int index;
1803
1804 if (cfg->defaultImage > -1) {
1805 entry = findEntryByIndex(cfg, cfg->defaultImage);
1806 if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
1807 if (indexPtr) *indexPtr = cfg->defaultImage;
1808 return entry;
1809 }
1810 }
1811
1812 index = 0;
1813 while ((entry = findEntryByIndex(cfg, index))) {
1814 if (suitableImage(entry, prefix, skipRemoved, flags)) {
1815 int j;
1816 for (j = 0; j < index; j++) {
1817 entry2 = findEntryByIndex(cfg, j);
1818 if (entry2->skip) index--;
1819 }
1820 if (indexPtr) *indexPtr = index;
1821
1822 return entry;
1823 }
1824
1825 index++;
1826 }
1827
1828 fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
1829
1830 return NULL;
1831 }
1832
1833 char * findBootPrefix(void) {
1834 struct stat sb, sb2;
1835
1836 stat("/", &sb);
1837 #ifdef __ia64__
1838 stat("/boot/efi/EFI/redhat/", &sb2);
1839 #else
1840 stat("/boot", &sb2);
1841 #endif
1842
1843 if (sb.st_dev == sb2.st_dev)
1844 return strdup("");
1845
1846 #ifdef __ia64__
1847 return strdup("/boot/efi/EFI/redhat/");
1848 #else
1849 return strdup("/boot");
1850 #endif
1851 }
1852
1853 void markRemovedImage(struct grubConfig * cfg, const char * image,
1854 const char * prefix) {
1855 struct singleEntry * entry;
1856
1857 if (!image)
1858 return;
1859
1860 /* check and see if we're removing the default image */
1861 if (isdigit(*image)) {
1862 entry = findEntryByPath(cfg, image, prefix, NULL);
1863 if(entry)
1864 entry->skip = 1;
1865 return;
1866 }
1867
1868 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
1869 entry->skip = 1;
1870 }
1871
1872 void setDefaultImage(struct grubConfig * config, int hasNew,
1873 const char * defaultKernelPath, int newIsDefault,
1874 const char * prefix, int flags, int index) {
1875 struct singleEntry * entry, * entry2, * newDefault;
1876 int i, j;
1877
1878 if (newIsDefault) {
1879 config->defaultImage = 0;
1880 return;
1881 } else if ((index >= 0) && config->cfi->defaultIsIndex) {
1882 if (findEntryByIndex(config, index))
1883 config->defaultImage = index;
1884 else
1885 config->defaultImage = -1;
1886 return;
1887 } else if (defaultKernelPath) {
1888 i = 0;
1889 if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
1890 config->defaultImage = i;
1891 } else {
1892 config->defaultImage = -1;
1893 return;
1894 }
1895 }
1896
1897 /* defaultImage now points to what we'd like to use, but before any order
1898 changes */
1899 if ((config->defaultImage == DEFAULT_SAVED) ||
1900 (config->defaultImage == DEFAULT_SAVED_GRUB2))
1901 /* default is set to saved, we don't want to change it */
1902 return;
1903
1904 if (config->defaultImage > -1)
1905 entry = findEntryByIndex(config, config->defaultImage);
1906 else
1907 entry = NULL;
1908
1909 if (entry && !entry->skip) {
1910 /* we can preserve the default */
1911 if (hasNew)
1912 config->defaultImage++;
1913
1914 /* count the number of entries erased before this one */
1915 for (j = 0; j < config->defaultImage; j++) {
1916 entry2 = findEntryByIndex(config, j);
1917 if (entry2->skip) config->defaultImage--;
1918 }
1919 } else if (hasNew) {
1920 config->defaultImage = 0;
1921 } else {
1922 /* Either we just erased the default (or the default line was bad
1923 * to begin with) and didn't put a new one in. We'll use the first
1924 * valid image. */
1925 newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
1926 flags);
1927 if (!newDefault)
1928 config->defaultImage = -1;
1929 }
1930 }
1931
1932 void setFallbackImage(struct grubConfig * config, int hasNew) {
1933 struct singleEntry * entry, * entry2;
1934 int j;
1935
1936 if (config->fallbackImage == -1) return;
1937
1938 entry = findEntryByIndex(config, config->fallbackImage);
1939 if (!entry || entry->skip) {
1940 config->fallbackImage = -1;
1941 return;
1942 }
1943
1944 if (hasNew)
1945 config->fallbackImage++;
1946
1947 /* count the number of entries erased before this one */
1948 for (j = 0; j < config->fallbackImage; j++) {
1949 entry2 = findEntryByIndex(config, j);
1950 if (entry2->skip) config->fallbackImage--;
1951 }
1952 }
1953
1954 void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
1955 struct singleLine * line;
1956 char * root = NULL;
1957 int i;
1958
1959 printf("index=%d\n", index);
1960
1961 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1962 if (!line) {
1963 printf("non linux entry\n");
1964 return;
1965 }
1966
1967 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
1968 printf("kernel=%s\n", line->elements[1].item);
1969 else
1970 printf("kernel=%s%s\n", prefix, line->elements[1].item);
1971
1972 if (line->numElements >= 3) {
1973 printf("args=\"");
1974 i = 2;
1975 while (i < line->numElements) {
1976 if (!strncmp(line->elements[i].item, "root=", 5)) {
1977 root = line->elements[i].item + 5;
1978 } else {
1979 printf("%s%s", line->elements[i].item,
1980 line->elements[i].indent);
1981 }
1982
1983 i++;
1984 }
1985 printf("\"\n");
1986 } else {
1987 line = getLineByType(LT_KERNELARGS, entry->lines);
1988 if (line) {
1989 char * s;
1990
1991 printf("args=\"");
1992 i = 1;
1993 while (i < line->numElements) {
1994 if (!strncmp(line->elements[i].item, "root=", 5)) {
1995 root = line->elements[i].item + 5;
1996 } else {
1997 s = line->elements[i].item;
1998
1999 printf("%s%s", s, line->elements[i].indent);
2000 }
2001
2002 i++;
2003 }
2004
2005 s = line->elements[i - 1].indent;
2006 printf("\"\n");
2007 }
2008 }
2009
2010 if (!root) {
2011 line = getLineByType(LT_ROOT, entry->lines);
2012 if (line && line->numElements >= 2)
2013 root=line->elements[1].item;
2014 }
2015
2016 if (root) {
2017 char * s = alloca(strlen(root) + 1);
2018
2019 strcpy(s, root);
2020 if (s[strlen(s) - 1] == '"')
2021 s[strlen(s) - 1] = '\0';
2022 /* make sure the root doesn't have a trailing " */
2023 printf("root=%s\n", s);
2024 }
2025
2026 line = getLineByType(LT_INITRD, entry->lines);
2027
2028 if (line && line->numElements >= 2) {
2029 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2030 printf("initrd=");
2031 else
2032 printf("initrd=%s", prefix);
2033
2034 for (i = 1; i < line->numElements; i++)
2035 printf("%s%s", line->elements[i].item, line->elements[i].indent);
2036 printf("\n");
2037 }
2038
2039 line = getLineByType(LT_TITLE, entry->lines);
2040 if (line) {
2041 printf("title=%s\n", line->elements[1].item);
2042 } else {
2043 char * title;
2044 line = getLineByType(LT_MENUENTRY, entry->lines);
2045 title = grub2ExtractTitle(line);
2046 if (title)
2047 printf("title=%s\n", title);
2048 }
2049 }
2050
2051 int isSuseSystem(void) {
2052 const char * path;
2053 const static char default_path[] = "/etc/SuSE-release";
2054
2055 if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2056 path = default_path;
2057
2058 if (!access(path, R_OK))
2059 return 1;
2060 return 0;
2061 }
2062
2063 int isSuseGrubConf(const char * path) {
2064 FILE * grubConf;
2065 char * line = NULL;
2066 size_t len = 0, res = 0;
2067
2068 grubConf = fopen(path, "r");
2069 if (!grubConf) {
2070 dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2071 return 0;
2072 }
2073
2074 while ((res = getline(&line, &len, grubConf)) != -1) {
2075 if (!strncmp(line, "setup", 5)) {
2076 fclose(grubConf);
2077 free(line);
2078 return 1;
2079 }
2080 }
2081
2082 dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2083 path);
2084
2085 fclose(grubConf);
2086 free(line);
2087 return 0;
2088 }
2089
2090 int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2091 FILE * grubConf;
2092 char * line = NULL;
2093 size_t res = 0, len = 0;
2094
2095 if (!path) return 1;
2096 if (!lbaPtr) return 1;
2097
2098 grubConf = fopen(path, "r");
2099 if (!grubConf) return 1;
2100
2101 while ((res = getline(&line, &len, grubConf)) != -1) {
2102 if (line[res - 1] == '\n')
2103 line[res - 1] = '\0';
2104 else if (len > res)
2105 line[res] = '\0';
2106 else {
2107 line = realloc(line, res + 1);
2108 line[res] = '\0';
2109 }
2110
2111 if (!strncmp(line, "setup", 5)) {
2112 if (strstr(line, "--force-lba")) {
2113 *lbaPtr = 1;
2114 } else {
2115 *lbaPtr = 0;
2116 }
2117 dbgPrintf("lba: %i\n", *lbaPtr);
2118 break;
2119 }
2120 }
2121
2122 free(line);
2123 fclose(grubConf);
2124 return 0;
2125 }
2126
2127 int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2128 FILE * grubConf;
2129 char * line = NULL;
2130 size_t res = 0, len = 0;
2131 char * lastParamPtr = NULL;
2132 char * secLastParamPtr = NULL;
2133 char installDeviceNumber = '\0';
2134 char * bounds = NULL;
2135
2136 if (!path) return 1;
2137 if (!devicePtr) return 1;
2138
2139 grubConf = fopen(path, "r");
2140 if (!grubConf) return 1;
2141
2142 while ((res = getline(&line, &len, grubConf)) != -1) {
2143 if (strncmp(line, "setup", 5))
2144 continue;
2145
2146 if (line[res - 1] == '\n')
2147 line[res - 1] = '\0';
2148 else if (len > res)
2149 line[res] = '\0';
2150 else {
2151 line = realloc(line, res + 1);
2152 line[res] = '\0';
2153 }
2154
2155 lastParamPtr = bounds = line + res;
2156
2157 /* Last parameter in grub may be an optional IMAGE_DEVICE */
2158 while (!isspace(*lastParamPtr))
2159 lastParamPtr--;
2160 lastParamPtr++;
2161
2162 secLastParamPtr = lastParamPtr - 2;
2163 dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2164
2165 if (lastParamPtr + 3 > bounds) {
2166 dbgPrintf("lastParamPtr going over boundary");
2167 fclose(grubConf);
2168 free(line);
2169 return 1;
2170 }
2171 if (!strncmp(lastParamPtr, "(hd", 3))
2172 lastParamPtr += 3;
2173 dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2174
2175 /*
2176 * Second last parameter will decide wether last parameter is
2177 * an IMAGE_DEVICE or INSTALL_DEVICE
2178 */
2179 while (!isspace(*secLastParamPtr))
2180 secLastParamPtr--;
2181 secLastParamPtr++;
2182
2183 if (secLastParamPtr + 3 > bounds) {
2184 dbgPrintf("secLastParamPtr going over boundary");
2185 fclose(grubConf);
2186 free(line);
2187 return 1;
2188 }
2189 dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2190 if (!strncmp(secLastParamPtr, "(hd", 3)) {
2191 secLastParamPtr += 3;
2192 dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2193 installDeviceNumber = *secLastParamPtr;
2194 } else {
2195 installDeviceNumber = *lastParamPtr;
2196 }
2197
2198 *devicePtr = malloc(6);
2199 snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2200 dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2201 fclose(grubConf);
2202 free(line);
2203 return 0;
2204 }
2205
2206 free(line);
2207 fclose(grubConf);
2208 return 1;
2209 }
2210
2211 int grubGetBootFromDeviceMap(const char * device,
2212 char ** bootPtr) {
2213 FILE * deviceMap;
2214 char * line = NULL;
2215 size_t res = 0, len = 0;
2216 char * devicePtr;
2217 char * bounds = NULL;
2218 const char * path;
2219 const static char default_path[] = "/boot/grub/device.map";
2220
2221 if (!device) return 1;
2222 if (!bootPtr) return 1;
2223
2224 if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2225 path = default_path;
2226
2227 dbgPrintf("opening grub device.map file from: %s\n", path);
2228 deviceMap = fopen(path, "r");
2229 if (!deviceMap)
2230 return 1;
2231
2232 while ((res = getline(&line, &len, deviceMap)) != -1) {
2233 if (!strncmp(line, "#", 1))
2234 continue;
2235
2236 if (line[res - 1] == '\n')
2237 line[res - 1] = '\0';
2238 else if (len > res)
2239 line[res] = '\0';
2240 else {
2241 line = realloc(line, res + 1);
2242 line[res] = '\0';
2243 }
2244
2245 devicePtr = line;
2246 bounds = line + res;
2247
2248 while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2249 devicePtr++;
2250 dbgPrintf("device: %s\n", devicePtr);
2251
2252 if (!strncmp(devicePtr, device, strlen(device))) {
2253 devicePtr += strlen(device);
2254 while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2255 devicePtr++;
2256
2257 *bootPtr = strdup(devicePtr);
2258 break;
2259 }
2260 }
2261
2262 free(line);
2263 fclose(deviceMap);
2264 return 0;
2265 }
2266
2267 int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2268 char * grubDevice;
2269
2270 if (suseGrubConfGetInstallDevice(path, &grubDevice))
2271 dbgPrintf("error looking for grub installation device\n");
2272 else
2273 dbgPrintf("grubby installation device: %s\n", grubDevice);
2274
2275 if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2276 dbgPrintf("error looking for grub boot device\n");
2277 else
2278 dbgPrintf("grubby boot device: %s\n", *bootPtr);
2279
2280 free(grubDevice);
2281 return 0;
2282 }
2283
2284 int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2285 /*
2286 * This SuSE grub configuration file at this location is not your average
2287 * grub configuration file, but instead the grub commands used to setup
2288 * grub on that system.
2289 */
2290 const char * path;
2291 const static char default_path[] = "/etc/grub.conf";
2292
2293 if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2294 path = default_path;
2295
2296 if (!isSuseGrubConf(path)) return 1;
2297
2298 if (lbaPtr) {
2299 *lbaPtr = 0;
2300 if (suseGrubConfGetLba(path, lbaPtr))
2301 return 1;
2302 }
2303
2304 if (bootPtr) {
2305 *bootPtr = NULL;
2306 suseGrubConfGetBoot(path, bootPtr);
2307 }
2308
2309 return 0;
2310 }
2311
2312 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
2313 FILE * in;
2314 char buf[1024];
2315 char * chptr;
2316 char * start;
2317 char * param;
2318
2319 in = fopen("/etc/conf.d/grub", "r");
2320 if (!in) return 1;
2321
2322 if (lbaPtr) *lbaPtr = 0;
2323 if (bootPtr) *bootPtr = NULL;
2324
2325 while (fgets(buf, sizeof(buf), in)) {
2326 start = buf;
2327 while (isspace(*start)) start++;
2328 if (*start == '#') continue;
2329
2330 chptr = strchr(start, '=');
2331 if (!chptr) continue;
2332 chptr--;
2333 while (*chptr && isspace(*chptr)) chptr--;
2334 chptr++;
2335 *chptr = '\0';
2336
2337 param = chptr + 1;
2338 while (*param && isspace(*param)) param++;
2339 if (*param == '=') {
2340 param++;
2341 while (*param && isspace(*param)) param++;
2342 }
2343
2344 chptr = param;
2345 while (*chptr && !isspace(*chptr)) chptr++;
2346 *chptr = '\0';
2347
2348 if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
2349 *lbaPtr = 1;
2350 else if (!strcmp(start, "boot") && bootPtr)
2351 *bootPtr = strdup(param);
2352 }
2353
2354 fclose(in);
2355
2356 return 0;
2357 }
2358
2359 void dumpSysconfigGrub(void) {
2360 char * boot = NULL;
2361 int lba;
2362
2363 if (isSuseSystem()) {
2364 if (parseSuseGrubConf(&lba, &boot)) {
2365 free(boot);
2366 return;
2367 }
2368 } else {
2369 if (parseSysconfigGrub(&lba, &boot)) {
2370 free(boot);
2371 return;
2372 }
2373 }
2374
2375 if (lba) printf("lba\n");
2376 if (boot) {
2377 printf("boot=%s\n", boot);
2378 free(boot);
2379 }
2380 }
2381
2382 int displayInfo(struct grubConfig * config, char * kernel,
2383 const char * prefix) {
2384 int i = 0;
2385 struct singleEntry * entry;
2386 struct singleLine * line;
2387
2388 entry = findEntryByPath(config, kernel, prefix, &i);
2389 if (!entry) {
2390 fprintf(stderr, _("grubby: kernel not found\n"));
2391 return 1;
2392 }
2393
2394 /* this is a horrible hack to support /etc/conf.d/grub; there must
2395 be a better way */
2396 if (config->cfi == &grubConfigType) {
2397 dumpSysconfigGrub();
2398 } else {
2399 line = getLineByType(LT_BOOT, config->theLines);
2400 if (line && line->numElements >= 1) {
2401 printf("boot=%s\n", line->elements[1].item);
2402 }
2403
2404 line = getLineByType(LT_LBA, config->theLines);
2405 if (line) printf("lba\n");
2406 }
2407
2408 displayEntry(entry, prefix, i);
2409
2410 i++;
2411 while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
2412 displayEntry(entry, prefix, i);
2413 i++;
2414 }
2415
2416 return 0;
2417 }
2418
2419 struct singleLine * addLineTmpl(struct singleEntry * entry,
2420 struct singleLine * tmplLine,
2421 struct singleLine * prevLine,
2422 const char * val,
2423 struct configFileInfo * cfi)
2424 {
2425 struct singleLine * newLine = lineDup(tmplLine);
2426
2427 if (val) {
2428 /* override the inherited value with our own.
2429 * This is a little weak because it only applies to elements[1]
2430 */
2431 if (newLine->numElements > 1)
2432 removeElement(newLine, 1);
2433 insertElement(newLine, val, 1, cfi);
2434
2435 /* but try to keep the rootspec from the template... sigh */
2436 if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD)) {
2437 char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2438 if (rootspec != NULL) {
2439 free(newLine->elements[1].item);
2440 newLine->elements[1].item =
2441 sdupprintf("%s%s", rootspec, val);
2442 }
2443 }
2444 }
2445
2446 dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2447 newLine->elements[0].item : "");
2448
2449 if (!entry->lines) {
2450 /* first one on the list */
2451 entry->lines = newLine;
2452 } else if (prevLine) {
2453 /* add after prevLine */
2454 newLine->next = prevLine->next;
2455 prevLine->next = newLine;
2456 }
2457
2458 return newLine;
2459 }
2460
2461 /* val may be NULL */
2462 struct singleLine * addLine(struct singleEntry * entry,
2463 struct configFileInfo * cfi,
2464 enum lineType_e type, char * defaultIndent,
2465 const char * val) {
2466 struct singleLine * line, * prev;
2467 struct keywordTypes * kw;
2468 struct singleLine tmpl;
2469
2470 /* NB: This function shouldn't allocate items on the heap, rather on the
2471 * stack since it calls addLineTmpl which will make copies.
2472 */
2473
2474 if (type == LT_TITLE && cfi->titleBracketed) {
2475 /* we're doing a bracketed title (zipl) */
2476 tmpl.type = type;
2477 tmpl.numElements = 1;
2478 tmpl.elements = alloca(sizeof(*tmpl.elements));
2479 tmpl.elements[0].item = alloca(strlen(val)+3);
2480 sprintf(tmpl.elements[0].item, "[%s]", val);
2481 tmpl.elements[0].indent = "";
2482 val = NULL;
2483 } else if (type == LT_MENUENTRY) {
2484 char *lineend = "--class gnu-linux --class gnu --class os {";
2485 if (!val) {
2486 fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2487 abort();
2488 }
2489 kw = getKeywordByType(type, cfi);
2490 if (!kw) {
2491 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2492 abort();
2493 }
2494 tmpl.indent = "";
2495 tmpl.type = type;
2496 tmpl.numElements = 3;
2497 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2498 tmpl.elements[0].item = kw->key;
2499 tmpl.elements[0].indent = alloca(2);
2500 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2501 tmpl.elements[1].item = (char *)val;
2502 tmpl.elements[1].indent = alloca(2);
2503 sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2504 tmpl.elements[2].item = alloca(strlen(lineend)+1);
2505 strcpy(tmpl.elements[2].item, lineend);
2506 tmpl.elements[2].indent = "";
2507 } else {
2508 kw = getKeywordByType(type, cfi);
2509 if (!kw) {
2510 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2511 abort();
2512 }
2513 tmpl.type = type;
2514 tmpl.numElements = val ? 2 : 1;
2515 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2516 tmpl.elements[0].item = kw->key;
2517 tmpl.elements[0].indent = alloca(2);
2518 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2519 if (val) {
2520 tmpl.elements[1].item = (char *)val;
2521 tmpl.elements[1].indent = "";
2522 }
2523 }
2524
2525 /* The last non-empty line gives us the indention to us and the line
2526 to insert after. Note that comments are considered empty lines, which
2527 may not be ideal? If there are no lines or we are looking at the
2528 first line, we use defaultIndent (the first line is normally indented
2529 differently from the rest) */
2530 for (line = entry->lines, prev = NULL; line; line = line->next) {
2531 if (line->numElements) prev = line;
2532 /* fall back on the last line if prev isn't otherwise set */
2533 if (!line->next && !prev) prev = line;
2534 }
2535
2536 struct singleLine *menuEntry;
2537 menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2538 if (tmpl.type == LT_ENTRY_END) {
2539 if (menuEntry)
2540 tmpl.indent = menuEntry->indent;
2541 else
2542 tmpl.indent = defaultIndent ?: "";
2543 } else if (tmpl.type != LT_MENUENTRY) {
2544 if (menuEntry)
2545 tmpl.indent = "\t";
2546 else if (prev == entry->lines)
2547 tmpl.indent = defaultIndent ?: "";
2548 else
2549 tmpl.indent = prev->indent;
2550 }
2551
2552 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2553 }
2554
2555 void removeLine(struct singleEntry * entry, struct singleLine * line) {
2556 struct singleLine * prev;
2557 int i;
2558
2559 for (i = 0; i < line->numElements; i++) {
2560 free(line->elements[i].item);
2561 free(line->elements[i].indent);
2562 }
2563 free(line->elements);
2564 free(line->indent);
2565
2566 if (line == entry->lines) {
2567 entry->lines = line->next;
2568 } else {
2569 prev = entry->lines;
2570 while (prev->next != line) prev = prev->next;
2571 prev->next = line->next;
2572 }
2573
2574 free(line);
2575 }
2576
2577 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2578 {
2579 struct singleLine newLine = {
2580 .indent = tmplLine->indent,
2581 .type = tmplLine->type,
2582 .next = tmplLine->next,
2583 };
2584 int firstQuotedItem = -1;
2585 int quoteLen = 0;
2586 int j;
2587 int element = 0;
2588 char *c;
2589
2590 c = malloc(strlen(tmplLine->elements[0].item) + 1);
2591 strcpy(c, tmplLine->elements[0].item);
2592 insertElement(&newLine, c, element++, cfi);
2593 free(c);
2594 c = NULL;
2595
2596 for (j = 1; j < tmplLine->numElements; j++) {
2597 if (firstQuotedItem == -1) {
2598 quoteLen += strlen(tmplLine->elements[j].item);
2599
2600 if (isquote(tmplLine->elements[j].item[0])) {
2601 firstQuotedItem = j;
2602 quoteLen += strlen(tmplLine->elements[j].indent);
2603 } else {
2604 c = malloc(quoteLen + 1);
2605 strcpy(c, tmplLine->elements[j].item);
2606 insertElement(&newLine, c, element++, cfi);
2607 free(c);
2608 quoteLen = 0;
2609 }
2610 } else {
2611 int itemlen = strlen(tmplLine->elements[j].item);
2612 quoteLen += itemlen;
2613 quoteLen += strlen(tmplLine->elements[j].indent);
2614
2615 if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2616 c = malloc(quoteLen + 1);
2617 c[0] = '\0';
2618 for (int i = firstQuotedItem; i < j+1; i++) {
2619 strcat(c, tmplLine->elements[i].item);
2620 strcat(c, tmplLine->elements[i].indent);
2621 }
2622 insertElement(&newLine, c, element++, cfi);
2623 free(c);
2624
2625 firstQuotedItem = -1;
2626 quoteLen = 0;
2627 }
2628 }
2629 }
2630 while (tmplLine->numElements)
2631 removeElement(tmplLine, 0);
2632 if (tmplLine->elements)
2633 free(tmplLine->elements);
2634
2635 tmplLine->numElements = newLine.numElements;
2636 tmplLine->elements = newLine.elements;
2637 }
2638
2639 static void insertElement(struct singleLine * line,
2640 const char * item, int insertHere,
2641 struct configFileInfo * cfi)
2642 {
2643 struct keywordTypes * kw;
2644 char indent[2] = "";
2645
2646 /* sanity check */
2647 if (insertHere > line->numElements) {
2648 dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2649 insertHere, line->numElements);
2650 insertHere = line->numElements;
2651 }
2652
2653 line->elements = realloc(line->elements, (line->numElements + 1) *
2654 sizeof(*line->elements));
2655 memmove(&line->elements[insertHere+1],
2656 &line->elements[insertHere],
2657 (line->numElements - insertHere) *
2658 sizeof(*line->elements));
2659 line->elements[insertHere].item = strdup(item);
2660
2661 kw = getKeywordByType(line->type, cfi);
2662
2663 if (line->numElements == 0) {
2664 indent[0] = '\0';
2665 } else if (insertHere == 0) {
2666 indent[0] = kw->nextChar;
2667 } else if (kw->separatorChar != '\0') {
2668 indent[0] = kw->separatorChar;
2669 } else {
2670 indent[0] = ' ';
2671 }
2672
2673 if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
2674 /* move the end-of-line forward */
2675 line->elements[insertHere].indent =
2676 line->elements[insertHere-1].indent;
2677 line->elements[insertHere-1].indent = strdup(indent);
2678 } else {
2679 line->elements[insertHere].indent = strdup(indent);
2680 }
2681
2682 line->numElements++;
2683
2684 dbgPrintf("insertElement(%s, '%s%s', %d)\n",
2685 line->elements[0].item,
2686 line->elements[insertHere].item,
2687 line->elements[insertHere].indent,
2688 insertHere);
2689 }
2690
2691 static void removeElement(struct singleLine * line, int removeHere) {
2692 int i;
2693
2694 /* sanity check */
2695 if (removeHere >= line->numElements) return;
2696
2697 dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
2698 removeHere, line->elements[removeHere].item);
2699
2700 free(line->elements[removeHere].item);
2701
2702 if (removeHere > 1) {
2703 /* previous argument gets this argument's post-indentation */
2704 free(line->elements[removeHere-1].indent);
2705 line->elements[removeHere-1].indent =
2706 line->elements[removeHere].indent;
2707 } else {
2708 free(line->elements[removeHere].indent);
2709 }
2710
2711 /* now collapse the array, but don't bother to realloc smaller */
2712 for (i = removeHere; i < line->numElements - 1; i++)
2713 line->elements[i] = line->elements[i + 1];
2714
2715 line->numElements--;
2716 }
2717
2718 int argMatch(const char * one, const char * two) {
2719 char * first, * second;
2720 char * chptr;
2721
2722 first = strcpy(alloca(strlen(one) + 1), one);
2723 second = strcpy(alloca(strlen(two) + 1), two);
2724
2725 chptr = strchr(first, '=');
2726 if (chptr) *chptr = '\0';
2727
2728 chptr = strchr(second, '=');
2729 if (chptr) *chptr = '\0';
2730
2731 return strcmp(first, second);
2732 }
2733
2734 int updateActualImage(struct grubConfig * cfg, const char * image,
2735 const char * prefix, const char * addArgs,
2736 const char * removeArgs, int multibootArgs) {
2737 struct singleEntry * entry;
2738 struct singleLine * line, * rootLine;
2739 int index = 0;
2740 int i, k;
2741 const char ** newArgs, ** oldArgs;
2742 const char ** arg;
2743 int useKernelArgs, useRoot;
2744 int firstElement;
2745 int *usedElements;
2746 int doreplace;
2747
2748 if (!image) return 0;
2749
2750 if (!addArgs) {
2751 newArgs = malloc(sizeof(*newArgs));
2752 *newArgs = NULL;
2753 } else {
2754 if (poptParseArgvString(addArgs, NULL, &newArgs)) {
2755 fprintf(stderr,
2756 _("grubby: error separating arguments '%s'\n"), addArgs);
2757 return 1;
2758 }
2759 }
2760
2761 if (!removeArgs) {
2762 oldArgs = malloc(sizeof(*oldArgs));
2763 *oldArgs = NULL;
2764 } else {
2765 if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
2766 fprintf(stderr,
2767 _("grubby: error separating arguments '%s'\n"), removeArgs);
2768 free(newArgs);
2769 return 1;
2770 }
2771 }
2772
2773
2774 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
2775 && (!multibootArgs || cfg->cfi->mbConcatArgs));
2776
2777 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
2778 && !multibootArgs);
2779
2780 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2781
2782 if (multibootArgs && !entry->multiboot)
2783 continue;
2784
2785 /* Determine where to put the args. If this config supports
2786 * LT_KERNELARGS, use that. Otherwise use
2787 * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
2788 */
2789 if (useKernelArgs) {
2790 line = getLineByType(LT_KERNELARGS, entry->lines);
2791 if (!line) {
2792 /* no LT_KERNELARGS, need to add it */
2793 line = addLine(entry, cfg->cfi, LT_KERNELARGS,
2794 cfg->secondaryIndent, NULL);
2795 }
2796 firstElement = 1;
2797
2798 } else if (multibootArgs) {
2799 line = getLineByType(LT_HYPER, entry->lines);
2800 if (!line) {
2801 /* a multiboot entry without LT_HYPER? */
2802 continue;
2803 }
2804 firstElement = 2;
2805
2806 } else {
2807 line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines);
2808 if (!line) {
2809 /* no LT_KERNEL or LT_MBMODULE in this entry? */
2810 continue;
2811 }
2812 firstElement = 2;
2813 }
2814
2815 /* handle the elilo case which does:
2816 * append="hypervisor args -- kernel args"
2817 */
2818 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
2819 /* this is a multiboot entry, make sure there's
2820 * -- on the args line
2821 */
2822 for (i = firstElement; i < line->numElements; i++) {
2823 if (!strcmp(line->elements[i].item, "--"))
2824 break;
2825 }
2826 if (i == line->numElements) {
2827 /* assume all existing args are kernel args,
2828 * prepend -- to make it official
2829 */
2830 insertElement(line, "--", firstElement, cfg->cfi);
2831 i = firstElement;
2832 }
2833 if (!multibootArgs) {
2834 /* kernel args start after the -- */
2835 firstElement = i + 1;
2836 }
2837 } else if (cfg->cfi->mbConcatArgs) {
2838 /* this is a non-multiboot entry, remove hyper args */
2839 for (i = firstElement; i < line->numElements; i++) {
2840 if (!strcmp(line->elements[i].item, "--"))
2841 break;
2842 }
2843 if (i < line->numElements) {
2844 /* remove args up to -- */
2845 while (strcmp(line->elements[firstElement].item, "--"))
2846 removeElement(line, firstElement);
2847 /* remove -- */
2848 removeElement(line, firstElement);
2849 }
2850 }
2851
2852 usedElements = calloc(line->numElements, sizeof(*usedElements));
2853
2854 for (k = 0, arg = newArgs; *arg; arg++, k++) {
2855
2856 doreplace = 1;
2857 for (i = firstElement; i < line->numElements; i++) {
2858 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2859 !strcmp(line->elements[i].item, "--"))
2860 {
2861 /* reached the end of hyper args, insert here */
2862 doreplace = 0;
2863 break;
2864 }
2865 if (usedElements[i])
2866 continue;
2867 if (!argMatch(line->elements[i].item, *arg)) {
2868 usedElements[i]=1;
2869 break;
2870 }
2871 }
2872
2873 if (i < line->numElements && doreplace) {
2874 /* direct replacement */
2875 free(line->elements[i].item);
2876 line->elements[i].item = strdup(*arg);
2877
2878 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
2879 /* root= replacement */
2880 rootLine = getLineByType(LT_ROOT, entry->lines);
2881 if (rootLine) {
2882 free(rootLine->elements[1].item);
2883 rootLine->elements[1].item = strdup(*arg + 5);
2884 } else {
2885 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
2886 cfg->secondaryIndent, *arg + 5);
2887 }
2888 }
2889
2890 else {
2891 /* insert/append */
2892 insertElement(line, *arg, i, cfg->cfi);
2893 usedElements = realloc(usedElements, line->numElements *
2894 sizeof(*usedElements));
2895 memmove(&usedElements[i + 1], &usedElements[i],
2896 line->numElements - i - 1);
2897 usedElements[i] = 1;
2898
2899 /* if we updated a root= here even though there is a
2900 LT_ROOT available we need to remove the LT_ROOT entry
2901 (this will happen if we switch from a device to a label) */
2902 if (useRoot && !strncmp(*arg, "root=", 5)) {
2903 rootLine = getLineByType(LT_ROOT, entry->lines);
2904 if (rootLine)
2905 removeLine(entry, rootLine);
2906 }
2907 }
2908 }
2909
2910 free(usedElements);
2911
2912 for (arg = oldArgs; *arg; arg++) {
2913 for (i = firstElement; i < line->numElements; i++) {
2914 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2915 !strcmp(line->elements[i].item, "--"))
2916 /* reached the end of hyper args, stop here */
2917 break;
2918 if (!argMatch(line->elements[i].item, *arg)) {
2919 removeElement(line, i);
2920 break;
2921 }
2922 }
2923 /* handle removing LT_ROOT line too */
2924 if (useRoot && !strncmp(*arg, "root=", 5)) {
2925 rootLine = getLineByType(LT_ROOT, entry->lines);
2926 if (rootLine)
2927 removeLine(entry, rootLine);
2928 }
2929 }
2930
2931 if (line->numElements == 1) {
2932 /* don't need the line at all (note it has to be a
2933 LT_KERNELARGS for this to happen */
2934 removeLine(entry, line);
2935 }
2936 }
2937
2938 free(newArgs);
2939 free(oldArgs);
2940
2941 return 0;
2942 }
2943
2944 int updateImage(struct grubConfig * cfg, const char * image,
2945 const char * prefix, const char * addArgs,
2946 const char * removeArgs,
2947 const char * addMBArgs, const char * removeMBArgs) {
2948 int rc = 0;
2949
2950 if (!image) return rc;
2951
2952 /* update the main args first... */
2953 if (addArgs || removeArgs)
2954 rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
2955 if (rc) return rc;
2956
2957 /* and now any multiboot args */
2958 if (addMBArgs || removeMBArgs)
2959 rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
2960 return rc;
2961 }
2962
2963 int updateInitrd(struct grubConfig * cfg, const char * image,
2964 const char * prefix, const char * initrd) {
2965 struct singleEntry * entry;
2966 struct singleLine * line, * kernelLine, *endLine = NULL;
2967 int index = 0;
2968
2969 if (!image) return 0;
2970
2971 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2972 kernelLine = getLineByType(LT_KERNEL, entry->lines);
2973 if (!kernelLine) continue;
2974
2975 line = getLineByType(LT_INITRD, entry->lines);
2976 if (line)
2977 removeLine(entry, line);
2978 if (prefix) {
2979 int prefixLen = strlen(prefix);
2980 if (!strncmp(initrd, prefix, prefixLen))
2981 initrd += prefixLen;
2982 }
2983 endLine = getLineByType(LT_ENTRY_END, entry->lines);
2984 if (endLine)
2985 removeLine(entry, endLine);
2986 line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd);
2987 if (!line)
2988 return 1;
2989 if (endLine) {
2990 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
2991 if (!line)
2992 return 1;
2993 }
2994
2995 break;
2996 }
2997
2998 return 0;
2999 }
3000
3001 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3002 int fd;
3003 unsigned char bootSect[512];
3004 int offset;
3005
3006 fd = open(device, O_RDONLY);
3007 if (fd < 0) {
3008 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3009 device, strerror(errno));
3010 return 1;
3011 }
3012
3013 if (read(fd, bootSect, 512) != 512) {
3014 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3015 device, strerror(errno));
3016 return 1;
3017 }
3018 close(fd);
3019
3020 /* first three bytes should match, a jmp short should be in there */
3021 if (memcmp(boot, bootSect, 3))
3022 return 0;
3023
3024 if (boot[1] == JMP_SHORT_OPCODE) {
3025 offset = boot[2] + 2;
3026 } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3027 offset = (boot[3] << 8) + boot[2] + 2;
3028 } else if (boot[0] == JMP_SHORT_OPCODE) {
3029 offset = boot[1] + 2;
3030 /*
3031 * it looks like grub, when copying stage1 into the mbr, patches stage1
3032 * right after the JMP location, replacing other instructions such as
3033 * JMPs for NOOPs. So, relax the check a little bit by skipping those
3034 * different bytes.
3035 */
3036 if ((bootSect[offset + 1] == NOOP_OPCODE)
3037 && (bootSect[offset + 2] == NOOP_OPCODE)) {
3038 offset = offset + 3;
3039 }
3040 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3041 offset = (boot[2] << 8) + boot[1] + 2;
3042 } else {
3043 return 0;
3044 }
3045
3046 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3047 return 0;
3048
3049 return 2;
3050 }
3051
3052 int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3053 int fd;
3054 char buf[65536];
3055 char * end;
3056 char * chptr;
3057 char * chptr2;
3058 int rc;
3059
3060 /* it's on raid; we need to parse /proc/mdstat and check all of the
3061 *raw* devices listed in there */
3062
3063 if (!strncmp(mdDev, "/dev/", 5))
3064 mdDev += 5;
3065
3066 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3067 fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3068 strerror(errno));
3069 return 2;
3070 }
3071
3072 rc = read(fd, buf, sizeof(buf) - 1);
3073 if (rc < 0 || rc == (sizeof(buf) - 1)) {
3074 fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3075 strerror(errno));
3076 close(fd);
3077 return 2;
3078 }
3079 close(fd);
3080 buf[rc] = '\0';
3081
3082 chptr = buf;
3083 while (*chptr) {
3084 end = strchr(chptr, '\n');
3085 if (!end) break;
3086 *end = '\0';
3087
3088 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3089 chptr[strlen(mdDev)] == ' ') {
3090
3091 /* found the device */
3092 while (*chptr && *chptr != ':') chptr++;
3093 chptr++;
3094 while (*chptr && isspace(*chptr)) chptr++;
3095
3096 /* skip the "active" bit */
3097 while (*chptr && !isspace(*chptr)) chptr++;
3098 while (*chptr && isspace(*chptr)) chptr++;
3099
3100 /* skip the raid level */
3101 while (*chptr && !isspace(*chptr)) chptr++;
3102 while (*chptr && isspace(*chptr)) chptr++;
3103
3104 /* everything else is partition stuff */
3105 while (*chptr) {
3106 chptr2 = chptr;
3107 while (*chptr2 && *chptr2 != '[') chptr2++;
3108 if (!*chptr2) break;
3109
3110 /* yank off the numbers at the end */
3111 chptr2--;
3112 while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3113 chptr2++;
3114 *chptr2 = '\0';
3115
3116 /* Better, now we need the /dev/ back. We're done with
3117 * everything before this point, so we can just put
3118 * the /dev/ part there. There will always be room. */
3119 memcpy(chptr - 5, "/dev/", 5);
3120 rc = checkDeviceBootloader(chptr - 5, boot);
3121 if (rc != 2) {
3122 return rc;
3123 }
3124
3125 chptr = chptr2 + 1;
3126 /* skip the [11] bit */
3127 while (*chptr && !isspace(*chptr)) chptr++;
3128 /* and move to the next one */
3129 while (*chptr && isspace(*chptr)) chptr++;
3130 }
3131
3132 /* we're good to go */
3133 return 2;
3134 }
3135
3136 chptr = end + 1;
3137 }
3138
3139 fprintf(stderr,
3140 _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3141 mdDev);
3142 return 0;
3143 }
3144
3145 int checkForLilo(struct grubConfig * config) {
3146 int fd;
3147 unsigned char boot[512];
3148 struct singleLine * line;
3149
3150 for (line = config->theLines; line; line = line->next)
3151 if (line->type == LT_BOOT) break;
3152
3153 if (!line) {
3154 fprintf(stderr,
3155 _("grubby: no boot line found in lilo configuration\n"));
3156 return 1;
3157 }
3158
3159 if (line->numElements != 2) return 1;
3160
3161 fd = open("/boot/boot.b", O_RDONLY);
3162 if (fd < 0) {
3163 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3164 "/boot/boot.b", strerror(errno));
3165 return 1;
3166 }
3167
3168 if (read(fd, boot, 512) != 512) {
3169 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3170 "/boot/boot.b", strerror(errno));
3171 return 1;
3172 }
3173 close(fd);
3174
3175 if (!strncmp("/dev/md", line->elements[1].item, 7))
3176 return checkLiloOnRaid(line->elements[1].item, boot);
3177
3178 return checkDeviceBootloader(line->elements[1].item, boot);
3179 }
3180
3181 int checkForGrub2(struct grubConfig * config) {
3182 if (!access("/etc/grub.d/", R_OK))
3183 return 2;
3184
3185 return 1;
3186 }
3187
3188 int checkForGrub(struct grubConfig * config) {
3189 int fd;
3190 unsigned char bootSect[512];
3191 char * boot;
3192 int onSuse = isSuseSystem();
3193
3194
3195 if (onSuse) {
3196 if (parseSuseGrubConf(NULL, &boot))
3197 return 0;
3198 } else {
3199 if (parseSysconfigGrub(NULL, &boot))
3200 return 0;
3201 }
3202
3203 /* assume grub is not installed -- not an error condition */
3204 if (!boot)
3205 return 0;
3206
3207 fd = open("/boot/grub/stage1", O_RDONLY);
3208 if (fd < 0)
3209 /* this doesn't exist if grub hasn't been installed */
3210 return 0;
3211
3212 if (read(fd, bootSect, 512) != 512) {
3213 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3214 "/boot/grub/stage1", strerror(errno));
3215 close(fd);
3216 return 1;
3217 }
3218 close(fd);
3219
3220 /* The more elaborate checks do not work on SuSE. The checks done
3221 * seem to be reasonble (at least for now), so just return success
3222 */
3223 if (onSuse)
3224 return 2;
3225
3226 return checkDeviceBootloader(boot, bootSect);
3227 }
3228
3229 int checkForExtLinux(struct grubConfig * config) {
3230 int fd;
3231 unsigned char bootSect[512];
3232 char * boot;
3233 char executable[] = "/boot/extlinux/extlinux";
3234
3235 printf("entered: checkForExtLinux()\n");
3236
3237 if (parseSysconfigGrub(NULL, &boot))
3238 return 0;
3239
3240 /* assume grub is not installed -- not an error condition */
3241 if (!boot)
3242 return 0;
3243
3244 fd = open(executable, O_RDONLY);
3245 if (fd < 0)
3246 /* this doesn't exist if grub hasn't been installed */
3247 return 0;
3248
3249 if (read(fd, bootSect, 512) != 512) {
3250 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3251 executable, strerror(errno));
3252 return 1;
3253 }
3254 close(fd);
3255
3256 return checkDeviceBootloader(boot, bootSect);
3257 }
3258
3259 int checkForYaboot(struct grubConfig * config) {
3260 /*
3261 * This is a simplistic check that we consider good enough for own puporses
3262 *
3263 * If we were to properly check if yaboot is *installed* we'd need to:
3264 * 1) get the system boot device (LT_BOOT)
3265 * 2) considering it's a raw filesystem, check if the yaboot binary matches
3266 * the content on the boot device
3267 * 3) if not, copy the binary to a temporary file and run "addnote" on it
3268 * 4) check again if binary and boot device contents match
3269 */
3270 if (!access("/etc/yaboot.conf", R_OK))
3271 return 2;
3272
3273 return 1;
3274 }
3275
3276 int checkForElilo(struct grubConfig * config) {
3277 if (!access("/etc/elilo.conf", R_OK))
3278 return 2;
3279
3280 return 1;
3281 }
3282
3283 static char * getRootSpecifier(char * str) {
3284 char * idx, * rootspec = NULL;
3285
3286 if (*str == '(') {
3287 idx = rootspec = strdup(str);
3288 while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3289 *(++idx) = '\0';
3290 }
3291 return rootspec;
3292 }
3293
3294 static char * getInitrdVal(struct grubConfig * config,
3295 const char * prefix, struct singleLine *tmplLine,
3296 const char * newKernelInitrd,
3297 const char ** extraInitrds, int extraInitrdCount)
3298 {
3299 char *initrdVal, *end;
3300 int i;
3301 size_t totalSize;
3302 size_t prefixLen;
3303 char separatorChar;
3304
3305 prefixLen = strlen(prefix);
3306 totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3307
3308 for (i = 0; i < extraInitrdCount; i++) {
3309 totalSize += sizeof(separatorChar);
3310 totalSize += strlen(extraInitrds[i]) - prefixLen;
3311 }
3312
3313 initrdVal = end = malloc(totalSize);
3314
3315 end = stpcpy (end, newKernelInitrd + prefixLen);
3316
3317 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3318 for (i = 0; i < extraInitrdCount; i++) {
3319 const char *extraInitrd;
3320 int j;
3321
3322 extraInitrd = extraInitrds[i] + prefixLen;
3323 /* Don't add entries that are already there */
3324 if (tmplLine != NULL) {
3325 for (j = 2; j < tmplLine->numElements; j++)
3326 if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3327 break;
3328
3329 if (j != tmplLine->numElements)
3330 continue;
3331 }
3332
3333 *end++ = separatorChar;
3334 end = stpcpy(end, extraInitrd);
3335 }
3336
3337 return initrdVal;
3338 }
3339
3340 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3341 const char * prefix,
3342 const char * newKernelPath, const char * newKernelTitle,
3343 const char * newKernelArgs, const char * newKernelInitrd,
3344 const char ** extraInitrds, int extraInitrdCount,
3345 const char * newMBKernel, const char * newMBKernelArgs) {
3346 struct singleEntry * new;
3347 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3348 int needs;
3349 char * chptr;
3350
3351 if (!newKernelPath) return 0;
3352
3353 /* if the newKernelTitle is too long silently munge it into something
3354 * we can live with. truncating is first check, then we'll just mess with
3355 * it until it looks better */
3356 if (config->cfi->maxTitleLength &&
3357 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3358 char * buf = alloca(config->cfi->maxTitleLength + 7);
3359 char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3360 int i = 1;
3361
3362 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3363 while (findEntryByPath(config, buf, NULL, NULL)) {
3364 sprintf(numBuf, "%d", i++);
3365 strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3366 }
3367
3368 newKernelTitle = buf + 6;
3369 }
3370
3371 new = malloc(sizeof(*new));
3372 new->skip = 0;
3373 new->multiboot = 0;
3374 new->next = config->entries;
3375 new->lines = NULL;
3376 config->entries = new;
3377
3378 /* copy/update from the template */
3379 needs = NEED_KERNEL | NEED_TITLE;
3380 if (newKernelInitrd)
3381 needs |= NEED_INITRD;
3382 if (newMBKernel) {
3383 needs |= NEED_MB;
3384 new->multiboot = 1;
3385 }
3386
3387 if (template) {
3388 for (masterLine = template->lines;
3389 masterLine && (tmplLine = lineDup(masterLine));
3390 lineFree(tmplLine), masterLine = masterLine->next)
3391 {
3392 dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3393
3394 /* skip comments */
3395 chptr = tmplLine->indent;
3396 while (*chptr && isspace(*chptr)) chptr++;
3397 if (*chptr == '#') continue;
3398
3399 if (tmplLine->type == LT_KERNEL &&
3400 tmplLine->numElements >= 2) {
3401 if (!template->multiboot && (needs & NEED_MB)) {
3402 /* it's not a multiboot template and this is the kernel
3403 * line. Try to be intelligent about inserting the
3404 * hypervisor at the same time.
3405 */
3406 if (config->cfi->mbHyperFirst) {
3407 /* insert the hypervisor first */
3408 newLine = addLine(new, config->cfi, LT_HYPER,
3409 tmplLine->indent,
3410 newMBKernel + strlen(prefix));
3411 /* set up for adding the kernel line */
3412 free(tmplLine->indent);
3413 tmplLine->indent = strdup(config->secondaryIndent);
3414 needs &= ~NEED_MB;
3415 }
3416 if (needs & NEED_KERNEL) {
3417 /* use addLineTmpl to preserve line elements,
3418 * otherwise we could just call addLine. Unfortunately
3419 * this means making some changes to the template
3420 * such as the indent change above and the type
3421 * change below.
3422 */
3423 struct keywordTypes * mbm_kw =
3424 getKeywordByType(LT_MBMODULE, config->cfi);
3425 if (mbm_kw) {
3426 tmplLine->type = LT_MBMODULE;
3427 free(tmplLine->elements[0].item);
3428 tmplLine->elements[0].item = strdup(mbm_kw->key);
3429 }
3430 newLine = addLineTmpl(new, tmplLine, newLine,
3431 newKernelPath + strlen(prefix), config->cfi);
3432 needs &= ~NEED_KERNEL;
3433 }
3434 if (needs & NEED_MB) { /* !mbHyperFirst */
3435 newLine = addLine(new, config->cfi, LT_HYPER,
3436 config->secondaryIndent,
3437 newMBKernel + strlen(prefix));
3438 needs &= ~NEED_MB;
3439 }
3440 } else if (needs & NEED_KERNEL) {
3441 newLine = addLineTmpl(new, tmplLine, newLine,
3442 newKernelPath + strlen(prefix), config->cfi);
3443 needs &= ~NEED_KERNEL;
3444 }
3445
3446 } else if (tmplLine->type == LT_HYPER &&
3447 tmplLine->numElements >= 2) {
3448 if (needs & NEED_MB) {
3449 newLine = addLineTmpl(new, tmplLine, newLine,
3450 newMBKernel + strlen(prefix), config->cfi);
3451 needs &= ~NEED_MB;
3452 }
3453
3454 } else if (tmplLine->type == LT_MBMODULE &&
3455 tmplLine->numElements >= 2) {
3456 if (new->multiboot) {
3457 if (needs & NEED_KERNEL) {
3458 newLine = addLineTmpl(new, tmplLine, newLine,
3459 newKernelPath +
3460 strlen(prefix), config->cfi);
3461 needs &= ~NEED_KERNEL;
3462 } else if (config->cfi->mbInitRdIsModule &&
3463 (needs & NEED_INITRD)) {
3464 char *initrdVal;
3465 initrdVal = getInitrdVal(config, prefix, tmplLine,
3466 newKernelInitrd, extraInitrds,
3467 extraInitrdCount);
3468 newLine = addLineTmpl(new, tmplLine, newLine,
3469 initrdVal, config->cfi);
3470 free(initrdVal);
3471 needs &= ~NEED_INITRD;
3472 }
3473 } else if (needs & NEED_KERNEL) {
3474 /* template is multi but new is not,
3475 * insert the kernel in the first module slot
3476 */
3477 tmplLine->type = LT_KERNEL;
3478 free(tmplLine->elements[0].item);
3479 tmplLine->elements[0].item =
3480 strdup(getKeywordByType(LT_KERNEL, config->cfi)->key);
3481 newLine = addLineTmpl(new, tmplLine, newLine,
3482 newKernelPath + strlen(prefix), config->cfi);
3483 needs &= ~NEED_KERNEL;
3484 } else if (needs & NEED_INITRD) {
3485 char *initrdVal;
3486 /* template is multi but new is not,
3487 * insert the initrd in the second module slot
3488 */
3489 tmplLine->type = LT_INITRD;
3490 free(tmplLine->elements[0].item);
3491 tmplLine->elements[0].item =
3492 strdup(getKeywordByType(LT_INITRD, config->cfi)->key);
3493 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3494 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3495 free(initrdVal);
3496 needs &= ~NEED_INITRD;
3497 }
3498
3499 } else if (tmplLine->type == LT_INITRD &&
3500 tmplLine->numElements >= 2) {
3501 if (needs & NEED_INITRD &&
3502 new->multiboot && !template->multiboot &&
3503 config->cfi->mbInitRdIsModule) {
3504 /* make sure we don't insert the module initrd
3505 * before the module kernel... if we don't do it here,
3506 * it will be inserted following the template.
3507 */
3508 if (!needs & NEED_KERNEL) {
3509 char *initrdVal;
3510
3511 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3512 newLine = addLine(new, config->cfi, LT_MBMODULE,
3513 config->secondaryIndent,
3514 initrdVal);
3515 free(initrdVal);
3516 needs &= ~NEED_INITRD;
3517 }
3518 } else if (needs & NEED_INITRD) {
3519 char *initrdVal;
3520 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3521 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3522 free(initrdVal);
3523 needs &= ~NEED_INITRD;
3524 }
3525
3526 } else if (tmplLine->type == LT_MENUENTRY &&
3527 (needs & NEED_TITLE)) {
3528 requote(tmplLine, config->cfi);
3529 char *nkt = malloc(strlen(newKernelTitle)+3);
3530 strcpy(nkt, "'");
3531 strcat(nkt, newKernelTitle);
3532 strcat(nkt, "'");
3533 newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3534 free(nkt);
3535 needs &= ~NEED_TITLE;
3536 } else if (tmplLine->type == LT_TITLE &&
3537 (needs & NEED_TITLE)) {
3538 if (tmplLine->numElements >= 2) {
3539 newLine = addLineTmpl(new, tmplLine, newLine,
3540 newKernelTitle, config->cfi);
3541 needs &= ~NEED_TITLE;
3542 } else if (tmplLine->numElements == 1 &&
3543 config->cfi->titleBracketed) {
3544 /* addLineTmpl doesn't handle titleBracketed */
3545 newLine = addLine(new, config->cfi, LT_TITLE,
3546 tmplLine->indent, newKernelTitle);
3547 needs &= ~NEED_TITLE;
3548 }
3549 } else if (tmplLine->type == LT_ECHO) {
3550 requote(tmplLine, config->cfi);
3551 static const char *prefix = "'Loading ";
3552 if (tmplLine->numElements > 1 &&
3553 strstr(tmplLine->elements[1].item, prefix) &&
3554 masterLine->next && masterLine->next->type == LT_KERNEL) {
3555 char *newTitle = malloc(strlen(prefix) +
3556 strlen(newKernelTitle) + 2);
3557
3558 strcpy(newTitle, prefix);
3559 strcat(newTitle, newKernelTitle);
3560 strcat(newTitle, "'");
3561 newLine = addLine(new, config->cfi, LT_ECHO,
3562 tmplLine->indent, newTitle);
3563 free(newTitle);
3564 } else {
3565 /* pass through other lines from the template */
3566 newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3567 config->cfi);
3568 }
3569 } else {
3570 /* pass through other lines from the template */
3571 newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3572 }
3573 }
3574
3575 } else {
3576 /* don't have a template, so start the entry with the
3577 * appropriate starting line
3578 */
3579 switch (config->cfi->entryStart) {
3580 case LT_KERNEL:
3581 if (new->multiboot && config->cfi->mbHyperFirst) {
3582 /* fall through to LT_HYPER */
3583 } else {
3584 newLine = addLine(new, config->cfi, LT_KERNEL,
3585 config->primaryIndent,
3586 newKernelPath + strlen(prefix));
3587 needs &= ~NEED_KERNEL;
3588 break;
3589 }
3590
3591 case LT_HYPER:
3592 newLine = addLine(new, config->cfi, LT_HYPER,
3593 config->primaryIndent,
3594 newMBKernel + strlen(prefix));
3595 needs &= ~NEED_MB;
3596 break;
3597
3598 case LT_MENUENTRY: {
3599 char *nkt = malloc(strlen(newKernelTitle)+3);
3600 strcpy(nkt, "'");
3601 strcat(nkt, newKernelTitle);
3602 strcat(nkt, "'");
3603 newLine = addLine(new, config->cfi, LT_MENUENTRY,
3604 config->primaryIndent, nkt);
3605 free(nkt);
3606 needs &= ~NEED_TITLE;
3607 needs |= NEED_END;
3608 break;
3609 }
3610 case LT_TITLE:
3611 if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
3612 char * templabel;
3613 int x = 0, y = 0;
3614
3615 templabel = strdup(newKernelTitle);
3616 while( templabel[x]){
3617 if( templabel[x] == ' ' ){
3618 y = x;
3619 while( templabel[y] ){
3620 templabel[y] = templabel[y+1];
3621 y++;
3622 }
3623 }
3624 x++;
3625 }
3626 newLine = addLine(new, config->cfi, LT_TITLE,
3627 config->primaryIndent, templabel);
3628 free(templabel);
3629 }else{
3630 newLine = addLine(new, config->cfi, LT_TITLE,
3631 config->primaryIndent, newKernelTitle);
3632 }
3633 needs &= ~NEED_TITLE;
3634 break;
3635
3636 default:
3637 abort();
3638 }
3639 }
3640
3641 /* add the remainder of the lines, i.e. those that either
3642 * weren't present in the template, or in the case of no template,
3643 * all the lines following the entryStart.
3644 */
3645 if (needs & NEED_TITLE) {
3646 newLine = addLine(new, config->cfi, LT_TITLE,
3647 config->secondaryIndent,
3648 newKernelTitle);
3649 needs &= ~NEED_TITLE;
3650 }
3651 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
3652 newLine = addLine(new, config->cfi, LT_HYPER,
3653 config->secondaryIndent,
3654 newMBKernel + strlen(prefix));
3655 needs &= ~NEED_MB;
3656 }
3657 if (needs & NEED_KERNEL) {
3658 newLine = addLine(new, config->cfi,
3659 (new->multiboot && getKeywordByType(LT_MBMODULE,
3660 config->cfi)) ?
3661 LT_MBMODULE : LT_KERNEL,
3662 config->secondaryIndent,
3663 newKernelPath + strlen(prefix));
3664 needs &= ~NEED_KERNEL;
3665 }
3666 if (needs & NEED_MB) {
3667 newLine = addLine(new, config->cfi, LT_HYPER,
3668 config->secondaryIndent,
3669 newMBKernel + strlen(prefix));
3670 needs &= ~NEED_MB;
3671 }
3672 if (needs & NEED_INITRD) {
3673 char *initrdVal;
3674 initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
3675 newLine = addLine(new, config->cfi,
3676 (new->multiboot && getKeywordByType(LT_MBMODULE,
3677 config->cfi)) ?
3678 LT_MBMODULE : LT_INITRD,
3679 config->secondaryIndent,
3680 initrdVal);
3681 free(initrdVal);
3682 needs &= ~NEED_INITRD;
3683 }
3684 if (needs & NEED_END) {
3685 newLine = addLine(new, config->cfi, LT_ENTRY_END,
3686 config->secondaryIndent, NULL);
3687 needs &= ~NEED_END;
3688 }
3689
3690 if (needs) {
3691 printf(_("grubby: needs=%d, aborting\n"), needs);
3692 abort();
3693 }
3694
3695 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
3696 newMBKernelArgs, NULL)) return 1;
3697
3698 return 0;
3699 }
3700
3701 static void traceback(int signum)
3702 {
3703 void *array[40];
3704 size_t size;
3705
3706 signal(SIGSEGV, SIG_DFL);
3707 memset(array, '\0', sizeof (array));
3708 size = backtrace(array, 40);
3709
3710 fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n",
3711 (unsigned long)size);
3712 backtrace_symbols_fd(array, size, STDERR_FILENO);
3713 exit(1);
3714 }
3715
3716 int main(int argc, const char ** argv) {
3717 poptContext optCon;
3718 const char * grubConfig = NULL;
3719 char * outputFile = NULL;
3720 int arg = 0;
3721 int flags = 0;
3722 int badImageOkay = 0;
3723 int configureGrub2 = 0;
3724 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
3725 int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
3726 int configureExtLinux = 0;
3727 int bootloaderProbe = 0;
3728 int extraInitrdCount = 0;
3729 char * updateKernelPath = NULL;
3730 char * newKernelPath = NULL;
3731 char * removeKernelPath = NULL;
3732 char * newKernelArgs = NULL;
3733 char * newKernelInitrd = NULL;
3734 char * newKernelTitle = NULL;
3735 char * newKernelVersion = NULL;
3736 char * newMBKernel = NULL;
3737 char * newMBKernelArgs = NULL;
3738 char * removeMBKernelArgs = NULL;
3739 char * removeMBKernel = NULL;
3740 char * bootPrefix = NULL;
3741 char * defaultKernel = NULL;
3742 char * removeArgs = NULL;
3743 char * kernelInfo = NULL;
3744 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
3745 const char * chptr = NULL;
3746 struct configFileInfo * cfi = NULL;
3747 struct grubConfig * config;
3748 struct singleEntry * template = NULL;
3749 int copyDefault = 0, makeDefault = 0;
3750 int displayDefault = 0;
3751 int displayDefaultIndex = 0;
3752 int displayDefaultTitle = 0;
3753 int defaultIndex = -1;
3754 struct poptOption options[] = {
3755 { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
3756 _("add an entry for the specified kernel"), _("kernel-path") },
3757 { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
3758 _("add an entry for the specified multiboot kernel"), NULL },
3759 { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
3760 _("default arguments for the new kernel or new arguments for "
3761 "kernel being updated"), _("args") },
3762 { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
3763 _("default arguments for the new multiboot kernel or "
3764 "new arguments for multiboot kernel being updated"), NULL },
3765 { "bad-image-okay", 0, 0, &badImageOkay, 0,
3766 _("don't sanity check images in boot entries (for testing only)"),
3767 NULL },
3768 { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
3769 _("filestystem which contains /boot directory (for testing only)"),
3770 _("bootfs") },
3771 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
3772 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
3773 _("check which bootloader is installed on boot sector") },
3774 #endif
3775 { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
3776 _("path to grub config file to update (\"-\" for stdin)"),
3777 _("path") },
3778 { "copy-default", 0, 0, &copyDefault, 0,
3779 _("use the default boot entry as a template for the new entry "
3780 "being added; if the default is not a linux image, or if "
3781 "the kernel referenced by the default image does not exist, "
3782 "the first linux entry whose kernel does exist is used as the "
3783 "template"), NULL },
3784 { "debug", 0, 0, &debug, 0,
3785 _("print debugging information for failures") },
3786 { "default-kernel", 0, 0, &displayDefault, 0,
3787 _("display the path of the default kernel") },
3788 { "default-index", 0, 0, &displayDefaultIndex, 0,
3789 _("display the index of the default kernel") },
3790 { "default-title", 0, 0, &displayDefaultTitle, 0,
3791 _("display the title of the default kernel") },
3792 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
3793 _("configure elilo bootloader") },
3794 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
3795 _("configure extlinux bootloader (from syslinux)") },
3796 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
3797 _("configure grub bootloader") },
3798 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
3799 _("configure grub2 bootloader") },
3800 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
3801 _("display boot information for specified kernel"),
3802 _("kernel-path") },
3803 { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
3804 _("initrd image for the new kernel"), _("initrd-path") },
3805 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
3806 _("auxilliary initrd image for things other than the new kernel"), _("initrd-path") },
3807 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
3808 _("configure lilo bootloader") },
3809 { "make-default", 0, 0, &makeDefault, 0,
3810 _("make the newly added entry the default boot entry"), NULL },
3811 { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
3812 _("path to output updated config file (\"-\" for stdout)"),
3813 _("path") },
3814 { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
3815 _("remove kernel arguments"), NULL },
3816 { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
3817 _("remove multiboot kernel arguments"), NULL },
3818 { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
3819 _("remove all entries for the specified kernel"),
3820 _("kernel-path") },
3821 { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
3822 _("remove all entries for the specified multiboot kernel"), NULL },
3823 { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
3824 _("make the first entry referencing the specified kernel "
3825 "the default"), _("kernel-path") },
3826 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
3827 _("make the given entry index the default entry"),
3828 _("entry-index") },
3829 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
3830 _("configure silo bootloader") },
3831 { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
3832 _("title to use for the new kernel entry"), _("entry-title") },
3833 { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
3834 _("updated information for the specified kernel"),
3835 _("kernel-path") },
3836 { "version", 'v', 0, NULL, 'v',
3837 _("print the version of this program and exit"), NULL },
3838 { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
3839 _("configure yaboot bootloader") },
3840 { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
3841 _("configure zipl bootloader") },
3842 POPT_AUTOHELP
3843 { 0, 0, 0, 0, 0 }
3844 };
3845
3846 useextlinuxmenu=0;
3847
3848 signal(SIGSEGV, traceback);
3849
3850 optCon = poptGetContext("grubby", argc, argv, options, 0);
3851 poptReadDefaultConfig(optCon, 1);
3852
3853 while ((arg = poptGetNextOpt(optCon)) >= 0) {
3854 switch (arg) {
3855 case 'v':
3856 printf("grubby version %s\n", VERSION);
3857 exit(0);
3858 break;
3859 case 'i':
3860 if (extraInitrdCount < MAX_EXTRA_INITRDS) {
3861 extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
3862 } else {
3863 fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
3864 return 1;
3865 }
3866 break;
3867 }
3868 }
3869
3870 if (arg < -1) {
3871 fprintf(stderr, _("grubby: bad argument %s: %s\n"),
3872 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
3873 poptStrerror(arg));
3874 return 1;
3875 }
3876
3877 if ((chptr = poptGetArg(optCon))) {
3878 fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
3879 return 1;
3880 }
3881
3882 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
3883 configureYaboot + configureSilo + configureZipl +
3884 configureExtLinux ) > 1) {
3885 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
3886 return 1;
3887 } else if (bootloaderProbe && grubConfig) {
3888 fprintf(stderr,
3889 _("grubby: cannot specify config file with --bootloader-probe\n"));
3890 return 1;
3891 } else if (configureGrub2) {
3892 cfi = &grub2ConfigType;
3893 } else if (configureLilo) {
3894 cfi = &liloConfigType;
3895 } else if (configureGrub) {
3896 cfi = &grubConfigType;
3897 } else if (configureELilo) {
3898 cfi = &eliloConfigType;
3899 } else if (configureYaboot) {
3900 cfi = &yabootConfigType;
3901 } else if (configureSilo) {
3902 cfi = &siloConfigType;
3903 } else if (configureZipl) {
3904 cfi = &ziplConfigType;
3905 } else if (configureExtLinux) {
3906 cfi = &extlinuxConfigType;
3907 useextlinuxmenu=1;
3908 }
3909
3910 if (!cfi) {
3911 if (grub2FindConfig(&grub2ConfigType))
3912 cfi = &grub2ConfigType;
3913 else
3914 #ifdef __ia64__
3915 cfi = &eliloConfigType;
3916 #elif __powerpc__
3917 cfi = &yabootConfigType;
3918 #elif __sparc__
3919 cfi = &siloConfigType;
3920 #elif __s390__
3921 cfi = &ziplConfigType;
3922 #elif __s390x__
3923 cfi = &ziplConfigtype;
3924 #else
3925 cfi = &grubConfigType;
3926 #endif
3927 }
3928
3929 if (!grubConfig) {
3930 if (cfi->findConfig)
3931 grubConfig = cfi->findConfig(cfi);
3932 if (!grubConfig)
3933 grubConfig = cfi->defaultConfig;
3934 }
3935
3936 if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
3937 newKernelPath || removeKernelPath || makeDefault ||
3938 defaultKernel || displayDefaultIndex || displayDefaultTitle ||
3939 (defaultIndex >= 0))) {
3940 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
3941 "specified option"));
3942 return 1;
3943 }
3944
3945 if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
3946 removeKernelPath)) {
3947 fprintf(stderr, _("grubby: --default-kernel and --info may not "
3948 "be used when adding or removing kernels\n"));
3949 return 1;
3950 }
3951
3952 if (newKernelPath && !newKernelTitle) {
3953 fprintf(stderr, _("grubby: kernel title must be specified\n"));
3954 return 1;
3955 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
3956 (newKernelInitrd && !updateKernelPath)||
3957 makeDefault || extraInitrdCount > 0)) {
3958 fprintf(stderr, _("grubby: kernel path expected\n"));
3959 return 1;
3960 }
3961
3962 if (newKernelPath && updateKernelPath) {
3963 fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
3964 "not be used together"));
3965 return 1;
3966 }
3967
3968 if (makeDefault && defaultKernel) {
3969 fprintf(stderr, _("grubby: --make-default and --default-kernel "
3970 "may not be used together\n"));
3971 return 1;
3972 } else if (defaultKernel && removeKernelPath &&
3973 !strcmp(defaultKernel, removeKernelPath)) {
3974 fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
3975 return 1;
3976 } else if (defaultKernel && newKernelPath &&
3977 !strcmp(defaultKernel, newKernelPath)) {
3978 makeDefault = 1;
3979 defaultKernel = NULL;
3980 }
3981 else if (defaultKernel && (defaultIndex >= 0)) {
3982 fprintf(stderr, _("grubby: --set-default and --set-default-index "
3983 "may not be used together\n"));
3984 return 1;
3985 }
3986
3987 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
3988 fprintf(stderr, _("grubby: output file must be specified if stdin "
3989 "is used\n"));
3990 return 1;
3991 }
3992
3993 if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
3994 && !kernelInfo && !bootloaderProbe && !updateKernelPath
3995 && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
3996 && (defaultIndex == -1)) {
3997 fprintf(stderr, _("grubby: no action specified\n"));
3998 return 1;
3999 }
4000
4001 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4002
4003 if (cfi->needsBootPrefix) {
4004 if (!bootPrefix) {
4005 bootPrefix = findBootPrefix();
4006 if (!bootPrefix) return 1;
4007 } else {
4008 /* this shouldn't end with a / */
4009 if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4010 bootPrefix[strlen(bootPrefix) - 1] = '\0';
4011 }
4012 } else {
4013 bootPrefix = "";
4014 }
4015
4016 if (!cfi->mbAllowExtraInitRds &&
4017 extraInitrdCount > 0) {
4018 fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4019 return 1;
4020 }
4021
4022 if (bootloaderProbe) {
4023 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4024 struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4025
4026 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4027 if (grub2config) {
4028 gconfig = readConfig(grub2config, &grub2ConfigType);
4029 if (!gconfig)
4030 gr2c = 1;
4031 else
4032 gr2c = checkForGrub2(gconfig);
4033 }
4034
4035 const char *grubconfig = grubFindConfig(&grubConfigType);
4036 if (!access(grubconfig, F_OK)) {
4037 gconfig = readConfig(grubconfig, &grubConfigType);
4038 if (!gconfig)
4039 grc = 1;
4040 else
4041 grc = checkForGrub(gconfig);
4042 }
4043
4044 if (!access(liloConfigType.defaultConfig, F_OK)) {
4045 lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4046 if (!lconfig)
4047 lrc = 1;
4048 else
4049 lrc = checkForLilo(lconfig);
4050 }
4051
4052 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4053 econfig = readConfig(eliloConfigType.defaultConfig,
4054 &eliloConfigType);
4055 if (!econfig)
4056 erc = 1;
4057 else
4058 erc = checkForElilo(econfig);
4059 }
4060
4061 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4062 lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4063 if (!lconfig)
4064 extrc = 1;
4065 else
4066 extrc = checkForExtLinux(lconfig);
4067 }
4068
4069
4070 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4071 yconfig = readConfig(yabootConfigType.defaultConfig,
4072 &yabootConfigType);
4073 if (!yconfig)
4074 yrc = 1;
4075 else
4076 yrc = checkForYaboot(yconfig);
4077 }
4078
4079 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4080 erc == 1)
4081 return 1;
4082
4083 if (lrc == 2) printf("lilo\n");
4084 if (gr2c == 2) printf("grub2\n");
4085 if (grc == 2) printf("grub\n");
4086 if (extrc == 2) printf("extlinux\n");
4087 if (yrc == 2) printf("yaboot\n");
4088 if (erc == 2) printf("elilo\n");
4089
4090 return 0;
4091 }
4092
4093 config = readConfig(grubConfig, cfi);
4094 if (!config) return 1;
4095
4096 if (displayDefault) {
4097 struct singleLine * line;
4098 struct singleEntry * entry;
4099 char * rootspec;
4100
4101 if (config->defaultImage == -1) return 0;
4102 entry = findEntryByIndex(config, config->defaultImage);
4103 if (!entry) return 0;
4104 if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4105
4106 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
4107 if (!line) return 0;
4108
4109 rootspec = getRootSpecifier(line->elements[1].item);
4110 printf("%s%s\n", bootPrefix, line->elements[1].item +
4111 ((rootspec != NULL) ? strlen(rootspec) : 0));
4112
4113 return 0;
4114
4115 } else if (displayDefaultTitle) {
4116 struct singleLine * line;
4117 struct singleEntry * entry;
4118
4119 if (config->defaultImage == -1) return 0;
4120 entry = findEntryByIndex(config, config->defaultImage);
4121 if (!entry) return 0;
4122
4123 if (!configureGrub2) {
4124 line = getLineByType(LT_TITLE, entry->lines);
4125 if (!line) return 0;
4126 printf("%s\n", line->elements[1].item);
4127
4128 } else {
4129 char * title;
4130
4131 dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4132 line = getLineByType(LT_MENUENTRY, entry->lines);
4133 if (!line) return 0;
4134 title = grub2ExtractTitle(line);
4135 if (title)
4136 printf("%s\n", title);
4137 }
4138 return 0;
4139
4140 } else if (displayDefaultIndex) {
4141 if (config->defaultImage == -1) return 0;
4142 printf("%i\n", config->defaultImage);
4143
4144 } else if (kernelInfo)
4145 return displayInfo(config, kernelInfo, bootPrefix);
4146
4147 if (copyDefault) {
4148 template = findTemplate(config, bootPrefix, NULL, 0, flags);
4149 if (!template) return 1;
4150 }
4151
4152 markRemovedImage(config, removeKernelPath, bootPrefix);
4153 markRemovedImage(config, removeMBKernel, bootPrefix);
4154 setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4155 bootPrefix, flags, defaultIndex);
4156 setFallbackImage(config, newKernelPath != NULL);
4157 if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4158 removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4159 if (updateKernelPath && newKernelInitrd) {
4160 if (updateInitrd(config, updateKernelPath, bootPrefix,
4161 newKernelInitrd)) return 1;
4162 }
4163 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4164 newKernelTitle, newKernelArgs, newKernelInitrd,
4165 (const char **)extraInitrds, extraInitrdCount,
4166 newMBKernel, newMBKernelArgs)) return 1;
4167
4168
4169 if (numEntries(config) == 0) {
4170 fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4171 "Not writing out new config.\n"));
4172 return 1;
4173 }
4174
4175 if (!outputFile)
4176 outputFile = (char *)grubConfig;
4177
4178 return writeConfig(config, outputFile, bootPrefix);
4179 }