Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1693 - (show annotations) (download)
Fri Feb 17 23:21:08 2012 UTC (12 years, 2 months ago) by niro
File MIME type: text/plain
File size: 88513 byte(s)
Rename entrySeparator to entryStart.
    
Since we're actually using it to detect the start of an entry (and
specifically not the end), name this slightly better.


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