Magellan Linux

Contents of /tags/grubby-7_0_18/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1434 - (show annotations) (download)
Tue Jul 19 17:08:39 2011 UTC (12 years, 9 months ago) by niro
File MIME type: text/plain
File size: 91953 byte(s)
tagged 'grubby-7_0_18'
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;
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 (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2018
2019 if (multibootArgs && !entry->multiboot)
2020 continue;
2021
2022 /* Determine where to put the args. If this config supports
2023 * LT_KERNELARGS, use that. Otherwise use
2024 * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
2025 */
2026 if (useKernelArgs) {
2027 line = getLineByType(LT_KERNELARGS, entry->lines);
2028 if (!line) {
2029 /* no LT_KERNELARGS, need to add it */
2030 line = addLine(entry, cfg->cfi, LT_KERNELARGS,
2031 cfg->secondaryIndent, NULL);
2032 }
2033 firstElement = 1;
2034
2035 } else if (multibootArgs) {
2036 line = getLineByType(LT_HYPER, entry->lines);
2037 if (!line) {
2038 /* a multiboot entry without LT_HYPER? */
2039 continue;
2040 }
2041 firstElement = 2;
2042
2043 } else {
2044 line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines);
2045 if (!line) {
2046 /* no LT_KERNEL or LT_MBMODULE in this entry? */
2047 continue;
2048 }
2049 firstElement = 2;
2050 }
2051
2052 /* handle the elilo case which does:
2053 * append="hypervisor args -- kernel args"
2054 */
2055 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
2056 /* this is a multiboot entry, make sure there's
2057 * -- on the args line
2058 */
2059 for (i = firstElement; i < line->numElements; i++) {
2060 if (!strcmp(line->elements[i].item, "--"))
2061 break;
2062 }
2063 if (i == line->numElements) {
2064 /* assume all existing args are kernel args,
2065 * prepend -- to make it official
2066 */
2067 insertElement(line, "--", firstElement, cfg->cfi);
2068 i = firstElement;
2069 }
2070 if (!multibootArgs) {
2071 /* kernel args start after the -- */
2072 firstElement = i + 1;
2073 }
2074 } else if (cfg->cfi->mbConcatArgs) {
2075 /* this is a non-multiboot entry, remove hyper args */
2076 for (i = firstElement; i < line->numElements; i++) {
2077 if (!strcmp(line->elements[i].item, "--"))
2078 break;
2079 }
2080 if (i < line->numElements) {
2081 /* remove args up to -- */
2082 while (strcmp(line->elements[firstElement].item, "--"))
2083 removeElement(line, firstElement);
2084 /* remove -- */
2085 removeElement(line, firstElement);
2086 }
2087 }
2088
2089 usedElements = calloc(line->numElements, sizeof(*usedElements));
2090
2091 for (k = 0, arg = newArgs; *arg; arg++, k++) {
2092
2093 doreplace = 1;
2094 for (i = firstElement; i < line->numElements; i++) {
2095 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2096 !strcmp(line->elements[i].item, "--"))
2097 {
2098 /* reached the end of hyper args, insert here */
2099 doreplace = 0;
2100 break;
2101 }
2102 if (usedElements[i])
2103 continue;
2104 if (!argMatch(line->elements[i].item, *arg)) {
2105 usedElements[i]=1;
2106 break;
2107 }
2108 }
2109
2110 if (i < line->numElements && doreplace) {
2111 /* direct replacement */
2112 free(line->elements[i].item);
2113 line->elements[i].item = strdup(*arg);
2114
2115 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
2116 /* root= replacement */
2117 rootLine = getLineByType(LT_ROOT, entry->lines);
2118 if (rootLine) {
2119 free(rootLine->elements[1].item);
2120 rootLine->elements[1].item = strdup(*arg + 5);
2121 } else {
2122 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
2123 cfg->secondaryIndent, *arg + 5);
2124 }
2125 }
2126
2127 else {
2128 /* insert/append */
2129 insertElement(line, *arg, i, cfg->cfi);
2130 usedElements = realloc(usedElements, line->numElements *
2131 sizeof(*usedElements));
2132 memmove(&usedElements[i + 1], &usedElements[i],
2133 line->numElements - i - 1);
2134 usedElements[i] = 1;
2135
2136 /* if we updated a root= here even though there is a
2137 LT_ROOT available we need to remove the LT_ROOT entry
2138 (this will happen if we switch from a device to a label) */
2139 if (useRoot && !strncmp(*arg, "root=", 5)) {
2140 rootLine = getLineByType(LT_ROOT, entry->lines);
2141 if (rootLine)
2142 removeLine(entry, rootLine);
2143 }
2144 }
2145 }
2146
2147 free(usedElements);
2148
2149 for (arg = oldArgs; *arg; arg++) {
2150 for (i = firstElement; i < line->numElements; i++) {
2151 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2152 !strcmp(line->elements[i].item, "--"))
2153 /* reached the end of hyper args, stop here */
2154 break;
2155 if (!argMatch(line->elements[i].item, *arg)) {
2156 removeElement(line, i);
2157 break;
2158 }
2159 }
2160 /* handle removing LT_ROOT line too */
2161 if (useRoot && !strncmp(*arg, "root=", 5)) {
2162 rootLine = getLineByType(LT_ROOT, entry->lines);
2163 if (rootLine)
2164 removeLine(entry, rootLine);
2165 }
2166 }
2167
2168 if (line->numElements == 1) {
2169 /* don't need the line at all (note it has to be a
2170 LT_KERNELARGS for this to happen */
2171 removeLine(entry, line);
2172 }
2173 }
2174
2175 free(newArgs);
2176 free(oldArgs);
2177
2178 return 0;
2179 }
2180
2181 int updateImage(struct grubConfig * cfg, const char * image,
2182 const char * prefix, const char * addArgs,
2183 const char * removeArgs,
2184 const char * addMBArgs, const char * removeMBArgs) {
2185 int rc = 0;
2186
2187 if (!image) return rc;
2188
2189 /* update the main args first... */
2190 if (addArgs || removeArgs)
2191 rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
2192 if (rc) return rc;
2193
2194 /* and now any multiboot args */
2195 if (addMBArgs || removeMBArgs)
2196 rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
2197 return rc;
2198 }
2199
2200 int updateInitrd(struct grubConfig * cfg, const char * image,
2201 const char * prefix, const char * initrd) {
2202 struct singleEntry * entry;
2203 struct singleLine * line, * kernelLine;
2204 int index = 0;
2205
2206 if (!image) return 0;
2207
2208 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2209 kernelLine = getLineByType(LT_KERNEL, entry->lines);
2210 if (!kernelLine) continue;
2211
2212 line = getLineByType(LT_INITRD, entry->lines);
2213 if (line)
2214 removeLine(entry, line);
2215 if (prefix) {
2216 int prefixLen = strlen(prefix);
2217 if (!strncmp(initrd, prefix, prefixLen))
2218 initrd += prefixLen;
2219 }
2220 line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd);
2221 if (!line) return 1;
2222 break;
2223 }
2224
2225 return 0;
2226 }
2227
2228 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
2229 int fd;
2230 unsigned char bootSect[512];
2231 int offset;
2232
2233 fd = open(device, O_RDONLY);
2234 if (fd < 0) {
2235 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
2236 device, strerror(errno));
2237 return 1;
2238 }
2239
2240 if (read(fd, bootSect, 512) != 512) {
2241 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2242 device, strerror(errno));
2243 return 1;
2244 }
2245 close(fd);
2246
2247 /* first three bytes should match, a jmp short should be in there */
2248 if (memcmp(boot, bootSect, 3))
2249 return 0;
2250
2251 if (boot[1] == 0xeb) {
2252 offset = boot[2] + 2;
2253 } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
2254 offset = (boot[3] << 8) + boot[2] + 2;
2255 } else if (boot[0] == 0xeb) {
2256 offset = boot[1] + 2;
2257 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
2258 offset = (boot[2] << 8) + boot[1] + 2;
2259 } else {
2260 return 0;
2261 }
2262
2263 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
2264 return 0;
2265
2266 return 2;
2267 }
2268
2269 int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
2270 int fd;
2271 char buf[65536];
2272 char * end;
2273 char * chptr;
2274 char * chptr2;
2275 int rc;
2276
2277 /* it's on raid; we need to parse /proc/mdstat and check all of the
2278 *raw* devices listed in there */
2279
2280 if (!strncmp(mdDev, "/dev/", 5))
2281 mdDev += 5;
2282
2283 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
2284 fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
2285 strerror(errno));
2286 return 2;
2287 }
2288
2289 rc = read(fd, buf, sizeof(buf) - 1);
2290 if (rc < 0 || rc == (sizeof(buf) - 1)) {
2291 fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
2292 strerror(errno));
2293 close(fd);
2294 return 2;
2295 }
2296 close(fd);
2297 buf[rc] = '\0';
2298
2299 chptr = buf;
2300 while (*chptr) {
2301 end = strchr(chptr, '\n');
2302 if (!end) break;
2303 *end = '\0';
2304
2305 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
2306 chptr[strlen(mdDev)] == ' ') {
2307
2308 /* found the device */
2309 while (*chptr && *chptr != ':') chptr++;
2310 chptr++;
2311 while (*chptr && isspace(*chptr)) chptr++;
2312
2313 /* skip the "active" bit */
2314 while (*chptr && !isspace(*chptr)) chptr++;
2315 while (*chptr && isspace(*chptr)) chptr++;
2316
2317 /* skip the raid level */
2318 while (*chptr && !isspace(*chptr)) chptr++;
2319 while (*chptr && isspace(*chptr)) chptr++;
2320
2321 /* everything else is partition stuff */
2322 while (*chptr) {
2323 chptr2 = chptr;
2324 while (*chptr2 && *chptr2 != '[') chptr2++;
2325 if (!*chptr2) break;
2326
2327 /* yank off the numbers at the end */
2328 chptr2--;
2329 while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
2330 chptr2++;
2331 *chptr2 = '\0';
2332
2333 /* Better, now we need the /dev/ back. We're done with
2334 * everything before this point, so we can just put
2335 * the /dev/ part there. There will always be room. */
2336 memcpy(chptr - 5, "/dev/", 5);
2337 rc = checkDeviceBootloader(chptr - 5, boot);
2338 if (rc != 2) {
2339 return rc;
2340 }
2341
2342 chptr = chptr2 + 1;
2343 /* skip the [11] bit */
2344 while (*chptr && !isspace(*chptr)) chptr++;
2345 /* and move to the next one */
2346 while (*chptr && isspace(*chptr)) chptr++;
2347 }
2348
2349 /* we're good to go */
2350 return 2;
2351 }
2352
2353 chptr = end + 1;
2354 }
2355
2356 fprintf(stderr,
2357 _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
2358 mdDev);
2359 return 0;
2360 }
2361
2362 int checkForLilo(struct grubConfig * config) {
2363 int fd;
2364 unsigned char boot[512];
2365 struct singleLine * line;
2366
2367 for (line = config->theLines; line; line = line->next)
2368 if (line->type == LT_BOOT) break;
2369
2370 if (!line) {
2371 fprintf(stderr,
2372 _("grubby: no boot line found in lilo configuration\n"));
2373 return 1;
2374 }
2375
2376 if (line->numElements != 2) return 1;
2377
2378 fd = open("/boot/boot.b", O_RDONLY);
2379 if (fd < 0) {
2380 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
2381 "/boot/boot.b", strerror(errno));
2382 return 1;
2383 }
2384
2385 if (read(fd, boot, 512) != 512) {
2386 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2387 "/boot/boot.b", strerror(errno));
2388 return 1;
2389 }
2390 close(fd);
2391
2392 if (!strncmp("/dev/md", line->elements[1].item, 7))
2393 return checkLiloOnRaid(line->elements[1].item, boot);
2394
2395 return checkDeviceBootloader(line->elements[1].item, boot);
2396 }
2397
2398 int checkForGrub(struct grubConfig * config) {
2399 int fd;
2400 unsigned char bootSect[512];
2401 char * boot;
2402
2403 if (parseSysconfigGrub(NULL, &boot))
2404 return 0;
2405
2406 /* assume grub is not installed -- not an error condition */
2407 if (!boot)
2408 return 0;
2409
2410 fd = open("/boot/grub/stage1", O_RDONLY);
2411 if (fd < 0)
2412 /* this doesn't exist if grub hasn't been installed */
2413 return 0;
2414
2415 if (read(fd, bootSect, 512) != 512) {
2416 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2417 "/boot/grub/stage1", strerror(errno));
2418 close(fd);
2419 return 1;
2420 }
2421 close(fd);
2422
2423 return checkDeviceBootloader(boot, bootSect);
2424 }
2425
2426 int checkForExtLinux(struct grubConfig * config) {
2427 int fd;
2428 unsigned char bootSect[512];
2429 char * boot;
2430 char executable[] = "/boot/extlinux/extlinux";
2431
2432 printf("entered: checkForExtLinux()\n");
2433
2434 if (parseSysconfigGrub(NULL, &boot))
2435 return 0;
2436
2437 /* assume grub is not installed -- not an error condition */
2438 if (!boot)
2439 return 0;
2440
2441 fd = open(executable, O_RDONLY);
2442 if (fd < 0)
2443 /* this doesn't exist if grub hasn't been installed */
2444 return 0;
2445
2446 if (read(fd, bootSect, 512) != 512) {
2447 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2448 executable, strerror(errno));
2449 return 1;
2450 }
2451 close(fd);
2452
2453 return checkDeviceBootloader(boot, bootSect);
2454 }
2455
2456 static char * getRootSpecifier(char * str) {
2457 char * idx, * rootspec = NULL;
2458
2459 if (*str == '(') {
2460 idx = rootspec = strdup(str);
2461 while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
2462 *(++idx) = '\0';
2463 }
2464 return rootspec;
2465 }
2466
2467 static char * getInitrdVal(struct grubConfig * config,
2468 const char * prefix, struct singleLine *tmplLine,
2469 const char * newKernelInitrd,
2470 char ** extraInitrds, int extraInitrdCount)
2471 {
2472 char *initrdVal, *end;
2473 int i;
2474 size_t totalSize;
2475 size_t prefixLen;
2476 char separatorChar;
2477
2478 prefixLen = strlen(prefix);
2479 totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
2480
2481 for (i = 0; i < extraInitrdCount; i++) {
2482 totalSize += sizeof(separatorChar);
2483 totalSize += strlen(extraInitrds[i]) - prefixLen;
2484 }
2485
2486 initrdVal = end = malloc(totalSize);
2487
2488 end = stpcpy (end, newKernelInitrd + prefixLen);
2489
2490 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
2491 for (i = 0; i < extraInitrdCount; i++) {
2492 const char *extraInitrd;
2493 int j;
2494
2495 extraInitrd = extraInitrds[i] + prefixLen;
2496 /* Don't add entries that are already there */
2497 if (tmplLine != NULL) {
2498 for (j = 2; j < tmplLine->numElements; j++)
2499 if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
2500 break;
2501
2502 if (j != tmplLine->numElements)
2503 continue;
2504 }
2505
2506 *end++ = separatorChar;
2507 end = stpcpy(end, extraInitrd);
2508 }
2509
2510 return initrdVal;
2511 }
2512
2513 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
2514 const char * prefix,
2515 char * newKernelPath, char * newKernelTitle,
2516 char * newKernelArgs, char * newKernelInitrd,
2517 char ** extraInitrds, int extraInitrdCount,
2518 char * newMBKernel, char * newMBKernelArgs) {
2519 struct singleEntry * new;
2520 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
2521 int needs;
2522 char * chptr;
2523
2524 if (!newKernelPath) return 0;
2525
2526 /* if the newKernelTitle is too long silently munge it into something
2527 * we can live with. truncating is first check, then we'll just mess with
2528 * it until it looks better */
2529 if (config->cfi->maxTitleLength &&
2530 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
2531 char * buf = alloca(config->cfi->maxTitleLength + 7);
2532 char * numBuf = alloca(config->cfi->maxTitleLength + 1);
2533 int i = 1;
2534
2535 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
2536 while (findEntryByPath(config, buf, NULL, NULL)) {
2537 sprintf(numBuf, "%d", i++);
2538 strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
2539 }
2540
2541 newKernelTitle = buf + 6;
2542 }
2543
2544 new = malloc(sizeof(*new));
2545 new->skip = 0;
2546 new->multiboot = 0;
2547 new->next = config->entries;
2548 new->lines = NULL;
2549 config->entries = new;
2550
2551 /* copy/update from the template */
2552 needs = NEED_KERNEL | NEED_TITLE;
2553 if (newKernelInitrd)
2554 needs |= NEED_INITRD;
2555 if (newMBKernel) {
2556 needs |= NEED_MB;
2557 new->multiboot = 1;
2558 }
2559
2560 if (template) {
2561 for (masterLine = template->lines;
2562 masterLine && (tmplLine = lineDup(masterLine));
2563 lineFree(tmplLine), masterLine = masterLine->next)
2564 {
2565 dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
2566
2567 /* skip comments */
2568 chptr = tmplLine->indent;
2569 while (*chptr && isspace(*chptr)) chptr++;
2570 if (*chptr == '#') continue;
2571
2572 if (tmplLine->type == LT_KERNEL &&
2573 tmplLine->numElements >= 2) {
2574 if (!template->multiboot && (needs & NEED_MB)) {
2575 /* it's not a multiboot template and this is the kernel
2576 * line. Try to be intelligent about inserting the
2577 * hypervisor at the same time.
2578 */
2579 if (config->cfi->mbHyperFirst) {
2580 /* insert the hypervisor first */
2581 newLine = addLine(new, config->cfi, LT_HYPER,
2582 tmplLine->indent,
2583 newMBKernel + strlen(prefix));
2584 /* set up for adding the kernel line */
2585 free(tmplLine->indent);
2586 tmplLine->indent = strdup(config->secondaryIndent);
2587 needs &= ~NEED_MB;
2588 }
2589 if (needs & NEED_KERNEL) {
2590 /* use addLineTmpl to preserve line elements,
2591 * otherwise we could just call addLine. Unfortunately
2592 * this means making some changes to the template
2593 * such as the indent change above and the type
2594 * change below.
2595 */
2596 struct keywordTypes * mbm_kw =
2597 getKeywordByType(LT_MBMODULE, config->cfi);
2598 if (mbm_kw) {
2599 tmplLine->type = LT_MBMODULE;
2600 free(tmplLine->elements[0].item);
2601 tmplLine->elements[0].item = strdup(mbm_kw->key);
2602 }
2603 newLine = addLineTmpl(new, tmplLine, newLine,
2604 newKernelPath + strlen(prefix), config->cfi);
2605 needs &= ~NEED_KERNEL;
2606 }
2607 if (needs & NEED_MB) { /* !mbHyperFirst */
2608 newLine = addLine(new, config->cfi, LT_HYPER,
2609 config->secondaryIndent,
2610 newMBKernel + strlen(prefix));
2611 needs &= ~NEED_MB;
2612 }
2613 } else if (needs & NEED_KERNEL) {
2614 newLine = addLineTmpl(new, tmplLine, newLine,
2615 newKernelPath + strlen(prefix), config->cfi);
2616 needs &= ~NEED_KERNEL;
2617 }
2618
2619 } else if (tmplLine->type == LT_HYPER &&
2620 tmplLine->numElements >= 2) {
2621 if (needs & NEED_MB) {
2622 newLine = addLineTmpl(new, tmplLine, newLine,
2623 newMBKernel + strlen(prefix), config->cfi);
2624 needs &= ~NEED_MB;
2625 }
2626
2627 } else if (tmplLine->type == LT_MBMODULE &&
2628 tmplLine->numElements >= 2) {
2629 if (new->multiboot) {
2630 if (needs & NEED_KERNEL) {
2631 newLine = addLineTmpl(new, tmplLine, newLine,
2632 newKernelPath +
2633 strlen(prefix), config->cfi);
2634 needs &= ~NEED_KERNEL;
2635 } else if (config->cfi->mbInitRdIsModule &&
2636 (needs & NEED_INITRD)) {
2637 char *initrdVal;
2638 initrdVal = getInitrdVal(config, prefix, tmplLine,
2639 newKernelInitrd, extraInitrds,
2640 extraInitrdCount);
2641 newLine = addLineTmpl(new, tmplLine, newLine,
2642 initrdVal, config->cfi);
2643 free(initrdVal);
2644 needs &= ~NEED_INITRD;
2645 }
2646 } else if (needs & NEED_KERNEL) {
2647 /* template is multi but new is not,
2648 * insert the kernel in the first module slot
2649 */
2650 tmplLine->type = LT_KERNEL;
2651 free(tmplLine->elements[0].item);
2652 tmplLine->elements[0].item =
2653 strdup(getKeywordByType(LT_KERNEL, config->cfi)->key);
2654 newLine = addLineTmpl(new, tmplLine, newLine,
2655 newKernelPath + strlen(prefix), config->cfi);
2656 needs &= ~NEED_KERNEL;
2657 } else if (needs & NEED_INITRD) {
2658 char *initrdVal;
2659 /* template is multi but new is not,
2660 * insert the initrd in the second module slot
2661 */
2662 tmplLine->type = LT_INITRD;
2663 free(tmplLine->elements[0].item);
2664 tmplLine->elements[0].item =
2665 strdup(getKeywordByType(LT_INITRD, config->cfi)->key);
2666 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
2667 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
2668 free(initrdVal);
2669 needs &= ~NEED_INITRD;
2670 }
2671
2672 } else if (tmplLine->type == LT_INITRD &&
2673 tmplLine->numElements >= 2) {
2674 if (needs & NEED_INITRD &&
2675 new->multiboot && !template->multiboot &&
2676 config->cfi->mbInitRdIsModule) {
2677 /* make sure we don't insert the module initrd
2678 * before the module kernel... if we don't do it here,
2679 * it will be inserted following the template.
2680 */
2681 if (!needs & NEED_KERNEL) {
2682 char *initrdVal;
2683
2684 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
2685 newLine = addLine(new, config->cfi, LT_MBMODULE,
2686 config->secondaryIndent,
2687 initrdVal);
2688 free(initrdVal);
2689 needs &= ~NEED_INITRD;
2690 }
2691 } else if (needs & NEED_INITRD) {
2692 char *initrdVal;
2693 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
2694 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
2695 free(initrdVal);
2696 needs &= ~NEED_INITRD;
2697 }
2698
2699 } else if (tmplLine->type == LT_TITLE &&
2700 (needs & NEED_TITLE)) {
2701 if (tmplLine->numElements >= 2) {
2702 newLine = addLineTmpl(new, tmplLine, newLine,
2703 newKernelTitle, config->cfi);
2704 needs &= ~NEED_TITLE;
2705 } else if (tmplLine->numElements == 1 &&
2706 config->cfi->titleBracketed) {
2707 /* addLineTmpl doesn't handle titleBracketed */
2708 newLine = addLine(new, config->cfi, LT_TITLE,
2709 tmplLine->indent, newKernelTitle);
2710 needs &= ~NEED_TITLE;
2711 }
2712
2713 } else {
2714 /* pass through other lines from the template */
2715 newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
2716 }
2717 }
2718
2719 } else {
2720 /* don't have a template, so start the entry with the
2721 * appropriate starting line
2722 */
2723 switch (config->cfi->entrySeparator) {
2724 case LT_KERNEL:
2725 if (new->multiboot && config->cfi->mbHyperFirst) {
2726 /* fall through to LT_HYPER */
2727 } else {
2728 newLine = addLine(new, config->cfi, LT_KERNEL,
2729 config->primaryIndent,
2730 newKernelPath + strlen(prefix));
2731 needs &= ~NEED_KERNEL;
2732 break;
2733 }
2734
2735 case LT_HYPER:
2736 newLine = addLine(new, config->cfi, LT_HYPER,
2737 config->primaryIndent,
2738 newMBKernel + strlen(prefix));
2739 needs &= ~NEED_MB;
2740 break;
2741
2742 case LT_TITLE:
2743 if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
2744 char * templabel;
2745 int x = 0, y = 0;
2746
2747 templabel = strdup(newKernelTitle);
2748 while( templabel[x]){
2749 if( templabel[x] == ' ' ){
2750 y = x;
2751 while( templabel[y] ){
2752 templabel[y] = templabel[y+1];
2753 y++;
2754 }
2755 }
2756 x++;
2757 }
2758 newLine = addLine(new, config->cfi, LT_TITLE,
2759 config->primaryIndent, templabel);
2760 free(templabel);
2761 }else{
2762 newLine = addLine(new, config->cfi, LT_TITLE,
2763 config->primaryIndent, newKernelTitle);
2764 }
2765 needs &= ~NEED_TITLE;
2766 break;
2767
2768 default:
2769 abort();
2770 }
2771 }
2772
2773 /* add the remainder of the lines, i.e. those that either
2774 * weren't present in the template, or in the case of no template,
2775 * all the lines following the entrySeparator.
2776 */
2777 if (needs & NEED_TITLE) {
2778 newLine = addLine(new, config->cfi, LT_TITLE,
2779 config->secondaryIndent,
2780 newKernelTitle);
2781 needs &= ~NEED_TITLE;
2782 }
2783 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
2784 newLine = addLine(new, config->cfi, LT_HYPER,
2785 config->secondaryIndent,
2786 newMBKernel + strlen(prefix));
2787 needs &= ~NEED_MB;
2788 }
2789 if (needs & NEED_KERNEL) {
2790 newLine = addLine(new, config->cfi,
2791 (new->multiboot && getKeywordByType(LT_MBMODULE,
2792 config->cfi)) ?
2793 LT_MBMODULE : LT_KERNEL,
2794 config->secondaryIndent,
2795 newKernelPath + strlen(prefix));
2796 needs &= ~NEED_KERNEL;
2797 }
2798 if (needs & NEED_MB) {
2799 newLine = addLine(new, config->cfi, LT_HYPER,
2800 config->secondaryIndent,
2801 newMBKernel + strlen(prefix));
2802 needs &= ~NEED_MB;
2803 }
2804 if (needs & NEED_INITRD) {
2805 char *initrdVal;
2806 initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
2807 newLine = addLine(new, config->cfi,
2808 (new->multiboot && getKeywordByType(LT_MBMODULE,
2809 config->cfi)) ?
2810 LT_MBMODULE : LT_INITRD,
2811 config->secondaryIndent,
2812 initrdVal);
2813 free(initrdVal);
2814 needs &= ~NEED_INITRD;
2815 }
2816
2817 if (needs) {
2818 printf(_("grubby: needs=%d, aborting\n"), needs);
2819 abort();
2820 }
2821
2822 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
2823 newMBKernelArgs, NULL)) return 1;
2824
2825 return 0;
2826 }
2827
2828 static void traceback(int signum)
2829 {
2830 void *array[40];
2831 size_t size;
2832
2833 signal(SIGSEGV, SIG_DFL);
2834 memset(array, '\0', sizeof (array));
2835 size = backtrace(array, 40);
2836
2837 fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n",
2838 (unsigned long)size);
2839 backtrace_symbols_fd(array, size, STDERR_FILENO);
2840 exit(1);
2841 }
2842
2843 int main(int argc, const char ** argv) {
2844 poptContext optCon;
2845 char * grubConfig = NULL;
2846 char * outputFile = NULL;
2847 int arg = 0;
2848 int flags = 0;
2849 int badImageOkay = 0;
2850 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
2851 int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
2852 int configureExtLinux = 0;
2853 int bootloaderProbe = 0;
2854 int extraInitrdCount = 0;
2855 char * updateKernelPath = NULL;
2856 char * newKernelPath = NULL;
2857 char * removeKernelPath = NULL;
2858 char * newKernelArgs = NULL;
2859 char * newKernelInitrd = NULL;
2860 char * newKernelTitle = NULL;
2861 char * newKernelVersion = NULL;
2862 char * newMBKernel = NULL;
2863 char * newMBKernelArgs = NULL;
2864 char * removeMBKernelArgs = NULL;
2865 char * removeMBKernel = NULL;
2866 char * bootPrefix = NULL;
2867 char * defaultKernel = NULL;
2868 char * removeArgs = NULL;
2869 char * kernelInfo = NULL;
2870 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
2871 const char * chptr = NULL;
2872 struct configFileInfo * cfi = NULL;
2873 struct grubConfig * config;
2874 struct singleEntry * template = NULL;
2875 int copyDefault = 0, makeDefault = 0;
2876 int displayDefault = 0;
2877 struct poptOption options[] = {
2878 { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
2879 _("add an entry for the specified kernel"), _("kernel-path") },
2880 { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
2881 _("add an entry for the specified multiboot kernel"), NULL },
2882 { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
2883 _("default arguments for the new kernel or new arguments for "
2884 "kernel being updated"), _("args") },
2885 { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
2886 _("default arguments for the new multiboot kernel or "
2887 "new arguments for multiboot kernel being updated"), NULL },
2888 { "bad-image-okay", 0, 0, &badImageOkay, 0,
2889 _("don't sanity check images in boot entries (for testing only)"),
2890 NULL },
2891 { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
2892 _("filestystem which contains /boot directory (for testing only)"),
2893 _("bootfs") },
2894 #if defined(__i386__) || defined(__x86_64__)
2895 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
2896 _("check if lilo is installed on lilo.conf boot sector") },
2897 #endif
2898 { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
2899 _("path to grub config file to update (\"-\" for stdin)"),
2900 _("path") },
2901 { "copy-default", 0, 0, &copyDefault, 0,
2902 _("use the default boot entry as a template for the new entry "
2903 "being added; if the default is not a linux image, or if "
2904 "the kernel referenced by the default image does not exist, "
2905 "the first linux entry whose kernel does exist is used as the "
2906 "template"), NULL },
2907 { "default-kernel", 0, 0, &displayDefault, 0,
2908 _("display the path of the default kernel") },
2909 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
2910 _("configure elilo bootloader") },
2911 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
2912 _("configure extlinux bootloader (from syslinux)") },
2913 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
2914 _("configure grub bootloader") },
2915 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
2916 _("display boot information for specified kernel"),
2917 _("kernel-path") },
2918 { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
2919 _("initrd image for the new kernel"), _("initrd-path") },
2920 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
2921 _("auxilliary initrd image for things other than the new kernel"), _("initrd-path") },
2922 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
2923 _("configure lilo bootloader") },
2924 { "make-default", 0, 0, &makeDefault, 0,
2925 _("make the newly added entry the default boot entry"), NULL },
2926 { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
2927 _("path to output updated config file (\"-\" for stdout)"),
2928 _("path") },
2929 { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
2930 _("remove kernel arguments"), NULL },
2931 { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
2932 _("remove multiboot kernel arguments"), NULL },
2933 { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
2934 _("remove all entries for the specified kernel"),
2935 _("kernel-path") },
2936 { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
2937 _("remove all entries for the specified multiboot kernel"), NULL },
2938 { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
2939 _("make the first entry referencing the specified kernel "
2940 "the default"), _("kernel-path") },
2941 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
2942 _("configure silo bootloader") },
2943 { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
2944 _("title to use for the new kernel entry"), _("entry-title") },
2945 { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
2946 _("updated information for the specified kernel"),
2947 _("kernel-path") },
2948 { "version", 'v', 0, NULL, 'v',
2949 _("print the version of this program and exit"), NULL },
2950 { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
2951 _("configure yaboot bootloader") },
2952 { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
2953 _("configure zipl bootloader") },
2954 POPT_AUTOHELP
2955 { 0, 0, 0, 0, 0 }
2956 };
2957
2958 useextlinuxmenu=0;
2959
2960 signal(SIGSEGV, traceback);
2961
2962 optCon = poptGetContext("grubby", argc, argv, options, 0);
2963 poptReadDefaultConfig(optCon, 1);
2964
2965 while ((arg = poptGetNextOpt(optCon)) >= 0) {
2966 switch (arg) {
2967 case 'v':
2968 printf("grubby version %s\n", VERSION);
2969 exit(0);
2970 break;
2971 case 'i':
2972 if (extraInitrdCount < MAX_EXTRA_INITRDS) {
2973 extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
2974 } else {
2975 fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
2976 return 1;
2977 }
2978 break;
2979 }
2980 }
2981
2982 if (arg < -1) {
2983 fprintf(stderr, _("grubby: bad argument %s: %s\n"),
2984 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
2985 poptStrerror(arg));
2986 return 1;
2987 }
2988
2989 if ((chptr = poptGetArg(optCon))) {
2990 fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
2991 return 1;
2992 }
2993
2994 if ((configureLilo + configureGrub + configureELilo +
2995 configureYaboot + configureSilo + configureZipl +
2996 configureExtLinux ) > 1) {
2997 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
2998 return 1;
2999 } else if (bootloaderProbe && grubConfig) {
3000 fprintf(stderr,
3001 _("grubby: cannot specify config file with --bootloader-probe\n"));
3002 return 1;
3003 } else if (configureLilo) {
3004 cfi = &liloConfigType;
3005 } else if (configureGrub) {
3006 cfi = &grubConfigType;
3007 } else if (configureELilo) {
3008 cfi = &eliloConfigType;
3009 } else if (configureYaboot) {
3010 cfi = &yabootConfigType;
3011 } else if (configureSilo) {
3012 cfi = &siloConfigType;
3013 } else if (configureZipl) {
3014 cfi = &ziplConfigType;
3015 } else if (configureExtLinux) {
3016 cfi = &extlinuxConfigType;
3017 useextlinuxmenu=1;
3018 }
3019
3020 if (!cfi) {
3021 #ifdef __ia64__
3022 cfi = &eliloConfigType;
3023 #elif __powerpc__
3024 cfi = &yabootConfigType;
3025 #elif __sparc__
3026 cfi = &siloConfigType;
3027 #elif __s390__
3028 cfi = &ziplConfigType;
3029 #elif __s390x__
3030 cfi = &ziplConfigtype;
3031 #else
3032 cfi = &grubConfigType;
3033 #endif
3034 }
3035
3036 if (!grubConfig)
3037 grubConfig = cfi->defaultConfig;
3038
3039 if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
3040 newKernelPath || removeKernelPath || makeDefault ||
3041 defaultKernel)) {
3042 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
3043 "specified option"));
3044 return 1;
3045 }
3046
3047 if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
3048 removeKernelPath)) {
3049 fprintf(stderr, _("grubby: --default-kernel and --info may not "
3050 "be used when adding or removing kernels\n"));
3051 return 1;
3052 }
3053
3054 if (newKernelPath && !newKernelTitle) {
3055 fprintf(stderr, _("grubby: kernel title must be specified\n"));
3056 return 1;
3057 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
3058 (newKernelInitrd && !updateKernelPath)||
3059 makeDefault || extraInitrdCount > 0)) {
3060 fprintf(stderr, _("grubby: kernel path expected\n"));
3061 return 1;
3062 }
3063
3064 if (newKernelPath && updateKernelPath) {
3065 fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
3066 "not be used together"));
3067 return 1;
3068 }
3069
3070 if (makeDefault && defaultKernel) {
3071 fprintf(stderr, _("grubby: --make-default and --default-kernel "
3072 "may not be used together\n"));
3073 return 1;
3074 } else if (defaultKernel && removeKernelPath &&
3075 !strcmp(defaultKernel, removeKernelPath)) {
3076 fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
3077 return 1;
3078 } else if (defaultKernel && newKernelPath &&
3079 !strcmp(defaultKernel, newKernelPath)) {
3080 makeDefault = 1;
3081 defaultKernel = NULL;
3082 }
3083
3084 if (!strcmp(grubConfig, "-") && !outputFile) {
3085 fprintf(stderr, _("grubby: output file must be specified if stdin "
3086 "is used\n"));
3087 return 1;
3088 }
3089
3090 if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
3091 && !kernelInfo && !bootloaderProbe && !updateKernelPath
3092 && !removeMBKernel) {
3093 fprintf(stderr, _("grubby: no action specified\n"));
3094 return 1;
3095 }
3096
3097 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
3098
3099 if (cfi->needsBootPrefix) {
3100 if (!bootPrefix) {
3101 bootPrefix = findBootPrefix();
3102 if (!bootPrefix) return 1;
3103 } else {
3104 /* this shouldn't end with a / */
3105 if (bootPrefix[strlen(bootPrefix) - 1] == '/')
3106 bootPrefix[strlen(bootPrefix) - 1] = '\0';
3107 }
3108 } else {
3109 bootPrefix = "";
3110 }
3111
3112 if (!cfi->mbAllowExtraInitRds &&
3113 extraInitrdCount > 0) {
3114 fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
3115 return 1;
3116 }
3117
3118 if (bootloaderProbe) {
3119 int lrc = 0, grc = 0, erc = 0;
3120 struct grubConfig * lconfig, * gconfig;
3121
3122 if (!access(grubConfigType.defaultConfig, F_OK)) {
3123 gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType);
3124 if (!gconfig)
3125 grc = 1;
3126 else
3127 grc = checkForGrub(gconfig);
3128 }
3129
3130 if (!access(liloConfigType.defaultConfig, F_OK)) {
3131 lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
3132 if (!lconfig)
3133 lrc = 1;
3134 else
3135 lrc = checkForLilo(lconfig);
3136 }
3137
3138 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
3139 lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
3140 if (!lconfig)
3141 erc = 1;
3142 else
3143 erc = checkForExtLinux(lconfig);
3144 }
3145
3146 if (lrc == 1 || grc == 1) return 1;
3147
3148 if (lrc == 2) printf("lilo\n");
3149 if (grc == 2) printf("grub\n");
3150 if (erc == 2) printf("extlinux\n");
3151
3152 return 0;
3153 }
3154
3155 config = readConfig(grubConfig, cfi);
3156 if (!config) return 1;
3157
3158 if (displayDefault) {
3159 struct singleLine * line;
3160 struct singleEntry * entry;
3161 char * rootspec;
3162
3163 if (config->defaultImage == -1) return 0;
3164 entry = findEntryByIndex(config, config->defaultImage);
3165 if (!entry) return 0;
3166 if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
3167
3168 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
3169 if (!line) return 0;
3170
3171 rootspec = getRootSpecifier(line->elements[1].item);
3172 printf("%s%s\n", bootPrefix, line->elements[1].item +
3173 ((rootspec != NULL) ? strlen(rootspec) : 0));
3174
3175 return 0;
3176 } else if (kernelInfo)
3177 return displayInfo(config, kernelInfo, bootPrefix);
3178
3179 if (copyDefault) {
3180 template = findTemplate(config, bootPrefix, NULL, 0, flags);
3181 if (!template) return 1;
3182 }
3183
3184 markRemovedImage(config, removeKernelPath, bootPrefix);
3185 markRemovedImage(config, removeMBKernel, bootPrefix);
3186 setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
3187 bootPrefix, flags);
3188 setFallbackImage(config, newKernelPath != NULL);
3189 if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
3190 removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
3191 if (updateKernelPath && newKernelInitrd) {
3192 if (updateInitrd(config, updateKernelPath, bootPrefix,
3193 newKernelInitrd)) return 1;
3194 }
3195 if (addNewKernel(config, template, bootPrefix, newKernelPath,
3196 newKernelTitle, newKernelArgs, newKernelInitrd,
3197 extraInitrds, extraInitrdCount,
3198 newMBKernel, newMBKernelArgs)) return 1;
3199
3200
3201 if (numEntries(config) == 0) {
3202 fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
3203 "Not writing out new config.\n"));
3204 return 1;
3205 }
3206
3207 if (!outputFile)
3208 outputFile = grubConfig;
3209
3210 return writeConfig(config, outputFile, bootPrefix);
3211 }