Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (show annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
Original Path: trunk/mkinitrd-magellan/grubby/grubby.c
File MIME type: text/plain
File size: 76002 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd

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