Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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