Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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