Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1715 - (show annotations) (download)
Sat Feb 18 00:35:46 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 98708 byte(s)
    Look for other possible grub config files
    
    Since debian and Ubuntu don't symlink /boot/grub/menu.lst
    to /etc/grub.conf, make it possible to look them over on
    this original location as well.
    
    Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>


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