Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1745 - (show annotations) (download)
Sat Feb 18 01:05:52 2012 UTC (12 years, 2 months ago) by niro
File MIME type: text/plain
File size: 102856 byte(s)
Include a / when one is missing in paths (#769641)


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