Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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