Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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