Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1750 - (show annotations) (download)
Sat Feb 18 01:09:51 2012 UTC (12 years, 2 months ago) by niro
Original Path: trunk/grubby/grubby.c
File MIME type: text/plain
File size: 104863 byte(s)
Fix endswith() to correctly test its input for validity.


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