Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1694 - (show annotations) (download)
Fri Feb 17 23:23:07 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 88534 byte(s)
Tweak some make options.

Some functions are only used in dbgPrintf() arguments. As such,
errors/warnings on unused functions need to be disabled.

Also, use -std=gnu99.

Also, add a debug target.


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