Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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