Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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