Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1740 - (show annotations) (download)
Sat Feb 18 01:03:37 2012 UTC (12 years, 2 months ago) by niro
File MIME type: text/plain
File size: 102496 byte(s)
tagged 'grubby-8_7'
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 int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1372 int skipRemoved, int flags) {
1373 struct singleLine * line;
1374 char * fullName;
1375 int i;
1376 char * dev;
1377 char * rootspec;
1378 char * rootdev;
1379
1380 if (skipRemoved && entry->skip) {
1381 notSuitablePrintf(entry, "marked to skip\n");
1382 return 0;
1383 }
1384
1385 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1386 if (!line) {
1387 notSuitablePrintf(entry, "no line found\n");
1388 return 0;
1389 }
1390 if (line->numElements < 2) {
1391 notSuitablePrintf(entry, "line has only %d elements\n",
1392 line->numElements);
1393 return 0;
1394 }
1395
1396 if (flags & GRUBBY_BADIMAGE_OKAY) return 1;
1397
1398 fullName = alloca(strlen(bootPrefix) +
1399 strlen(line->elements[1].item) + 1);
1400 rootspec = getRootSpecifier(line->elements[1].item);
1401 sprintf(fullName, "%s%s", bootPrefix,
1402 line->elements[1].item + (rootspec ? strlen(rootspec) : 0));
1403 if (access(fullName, R_OK)) {
1404 notSuitablePrintf(entry, "access to %s failed\n", fullName);
1405 return 0;
1406 }
1407 for (i = 2; i < line->numElements; i++)
1408 if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1409 if (i < line->numElements) {
1410 dev = line->elements[i].item + 5;
1411 } else {
1412 /* look for a lilo style LT_ROOT line */
1413 line = getLineByType(LT_ROOT, entry->lines);
1414
1415 if (line && line->numElements >= 2) {
1416 dev = line->elements[1].item;
1417 } else {
1418 /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1419 * grub+multiboot uses LT_MBMODULE for the args, so check that too.
1420 */
1421 line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
1422
1423 /* failed to find one */
1424 if (!line) {
1425 notSuitablePrintf(entry, "no line found\n");
1426 return 0;
1427 }
1428
1429 for (i = 1; i < line->numElements; i++)
1430 if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1431 if (i < line->numElements)
1432 dev = line->elements[i].item + 5;
1433 else {
1434 notSuitablePrintf(entry, "no root= entry found\n");
1435 /* it failed too... can't find root= */
1436 return 0;
1437 }
1438 }
1439 }
1440
1441 dev = getpathbyspec(dev);
1442 if (!getpathbyspec(dev)) {
1443 notSuitablePrintf(entry, "can't find blkid entry for %s\n", dev);
1444 return 0;
1445 } else
1446 dev = getpathbyspec(dev);
1447
1448 rootdev = findDiskForRoot();
1449 if (!rootdev) {
1450 notSuitablePrintf(entry, "can't find root device\n");
1451 return 0;
1452 }
1453
1454 if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1455 notSuitablePrintf(entry, "uuid missing: rootdev %s, dev %s\n",
1456 getuuidbydev(rootdev), getuuidbydev(dev));
1457 free(rootdev);
1458 return 0;
1459 }
1460
1461 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1462 notSuitablePrintf(entry, "uuid mismatch: rootdev %s, dev %s\n",
1463 getuuidbydev(rootdev), getuuidbydev(dev));
1464 free(rootdev);
1465 return 0;
1466 }
1467
1468 free(rootdev);
1469
1470 return 1;
1471 }
1472
1473 /* returns the first match on or after the one pointed to by index (if index
1474 is not NULL) which is not marked as skip */
1475 struct singleEntry * findEntryByPath(struct grubConfig * config,
1476 const char * kernel, const char * prefix,
1477 int * index) {
1478 struct singleEntry * entry = NULL;
1479 struct singleLine * line;
1480 int i;
1481 char * chptr;
1482 char * rootspec = NULL;
1483 enum lineType_e checkType = LT_KERNEL;
1484
1485 if (isdigit(*kernel)) {
1486 int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
1487
1488 i = 0;
1489 indexVars[i] = strtol(kernel, &chptr, 10);
1490 while (*chptr == ',') {
1491 i++;
1492 kernel = chptr + 1;
1493 indexVars[i] = strtol(kernel, &chptr, 10);
1494 }
1495
1496 if (*chptr) {
1497 /* can't parse it, bail */
1498 return NULL;
1499 }
1500
1501 indexVars[i + 1] = -1;
1502
1503 i = 0;
1504 if (index) {
1505 while (i < *index) i++;
1506 if (indexVars[i] == -1) return NULL;
1507 }
1508
1509 entry = findEntryByIndex(config, indexVars[i]);
1510 if (!entry) return NULL;
1511
1512 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1513 if (!line) return NULL;
1514
1515 if (index) *index = indexVars[i];
1516 return entry;
1517 }
1518
1519 if (!strcmp(kernel, "DEFAULT")) {
1520 if (index && *index > config->defaultImage) {
1521 entry = NULL;
1522 } else {
1523 entry = findEntryByIndex(config, config->defaultImage);
1524 if (entry && entry->skip)
1525 entry = NULL;
1526 else if (index)
1527 *index = config->defaultImage;
1528 }
1529 } else if (!strcmp(kernel, "ALL")) {
1530 if (index)
1531 i = *index;
1532 else
1533 i = 0;
1534
1535 while ((entry = findEntryByIndex(config, i))) {
1536 if (!entry->skip) break;
1537 i++;
1538 }
1539
1540 if (entry && index)
1541 *index = i;
1542 } else {
1543 if (index)
1544 i = *index;
1545 else
1546 i = 0;
1547
1548 if (!strncmp(kernel, "TITLE=", 6)) {
1549 prefix = "";
1550 checkType = LT_TITLE;
1551 kernel += 6;
1552 }
1553
1554 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
1555 if (entry->skip) continue;
1556
1557 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
1558
1559 /* check all the lines matching checkType */
1560 for (line = entry->lines; line; line = line->next) {
1561 line = getLineByType(entry->multiboot && checkType == LT_KERNEL ?
1562 LT_KERNEL|LT_MBMODULE|LT_HYPER :
1563 checkType, line);
1564 if (!line) break; /* not found in this entry */
1565
1566 if (line && line->numElements >= 2) {
1567 rootspec = getRootSpecifier(line->elements[1].item);
1568 if (!strcmp(line->elements[1].item +
1569 ((rootspec != NULL) ? strlen(rootspec) : 0),
1570 kernel + strlen(prefix)))
1571 break;
1572 }
1573 }
1574
1575 /* make sure this entry has a kernel identifier; this skips
1576 * non-Linux boot entries (could find netbsd etc, though, which is
1577 * unfortunate)
1578 */
1579 if (line && getLineByType(LT_KERNEL|LT_HYPER, entry->lines))
1580 break; /* found 'im! */
1581 }
1582
1583 if (index) *index = i;
1584 }
1585
1586 return entry;
1587 }
1588
1589 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
1590 struct singleEntry * entry;
1591
1592 entry = cfg->entries;
1593 while (index && entry) {
1594 entry = entry->next;
1595 index--;
1596 }
1597
1598 return entry;
1599 }
1600
1601 /* Find a good template to use for the new kernel. An entry is
1602 * good if the kernel and mkinitrd exist (even if the entry
1603 * is going to be removed). Try and use the default entry, but
1604 * if that doesn't work just take the first. If we can't find one,
1605 * bail. */
1606 struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
1607 int * indexPtr, int skipRemoved, int flags) {
1608 struct singleEntry * entry, * entry2;
1609 int index;
1610
1611 if (cfg->defaultImage > -1) {
1612 entry = findEntryByIndex(cfg, cfg->defaultImage);
1613 if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
1614 if (indexPtr) *indexPtr = cfg->defaultImage;
1615 return entry;
1616 }
1617 }
1618
1619 index = 0;
1620 while ((entry = findEntryByIndex(cfg, index))) {
1621 if (suitableImage(entry, prefix, skipRemoved, flags)) {
1622 int j;
1623 for (j = 0; j < index; j++) {
1624 entry2 = findEntryByIndex(cfg, j);
1625 if (entry2->skip) index--;
1626 }
1627 if (indexPtr) *indexPtr = index;
1628
1629 return entry;
1630 }
1631
1632 index++;
1633 }
1634
1635 fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
1636
1637 return NULL;
1638 }
1639
1640 char * findBootPrefix(void) {
1641 struct stat sb, sb2;
1642
1643 stat("/", &sb);
1644 #ifdef __ia64__
1645 stat("/boot/efi/EFI/redhat/", &sb2);
1646 #else
1647 stat("/boot", &sb2);
1648 #endif
1649
1650 if (sb.st_dev == sb2.st_dev)
1651 return strdup("");
1652
1653 #ifdef __ia64__
1654 return strdup("/boot/efi/EFI/redhat/");
1655 #else
1656 return strdup("/boot");
1657 #endif
1658 }
1659
1660 void markRemovedImage(struct grubConfig * cfg, const char * image,
1661 const char * prefix) {
1662 struct singleEntry * entry;
1663
1664 if (!image) return;
1665
1666 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
1667 entry->skip = 1;
1668 }
1669
1670 void setDefaultImage(struct grubConfig * config, int hasNew,
1671 const char * defaultKernelPath, int newIsDefault,
1672 const char * prefix, int flags) {
1673 struct singleEntry * entry, * entry2, * newDefault;
1674 int i, j;
1675
1676 if (newIsDefault) {
1677 config->defaultImage = 0;
1678 return;
1679 } else if (defaultKernelPath) {
1680 i = 0;
1681 if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
1682 config->defaultImage = i;
1683 } else {
1684 config->defaultImage = -1;
1685 return;
1686 }
1687 }
1688
1689 /* defaultImage now points to what we'd like to use, but before any order
1690 changes */
1691 if (config->defaultImage == DEFAULT_SAVED)
1692 /* default is set to saved, we don't want to change it */
1693 return;
1694
1695 if (config->defaultImage > -1)
1696 entry = findEntryByIndex(config, config->defaultImage);
1697 else
1698 entry = NULL;
1699
1700 if (entry && !entry->skip) {
1701 /* we can preserve the default */
1702 if (hasNew)
1703 config->defaultImage++;
1704
1705 /* count the number of entries erased before this one */
1706 for (j = 0; j < config->defaultImage; j++) {
1707 entry2 = findEntryByIndex(config, j);
1708 if (entry2->skip) config->defaultImage--;
1709 }
1710 } else if (hasNew) {
1711 config->defaultImage = 0;
1712 } else {
1713 /* Either we just erased the default (or the default line was bad
1714 * to begin with) and didn't put a new one in. We'll use the first
1715 * valid image. */
1716 newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
1717 flags);
1718 if (!newDefault)
1719 config->defaultImage = -1;
1720 }
1721 }
1722
1723 void setFallbackImage(struct grubConfig * config, int hasNew) {
1724 struct singleEntry * entry, * entry2;
1725 int j;
1726
1727 if (config->fallbackImage == -1) return;
1728
1729 entry = findEntryByIndex(config, config->fallbackImage);
1730 if (!entry || entry->skip) {
1731 config->fallbackImage = -1;
1732 return;
1733 }
1734
1735 if (hasNew)
1736 config->fallbackImage++;
1737
1738 /* count the number of entries erased before this one */
1739 for (j = 0; j < config->fallbackImage; j++) {
1740 entry2 = findEntryByIndex(config, j);
1741 if (entry2->skip) config->fallbackImage--;
1742 }
1743 }
1744
1745 void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
1746 struct singleLine * line;
1747 char * root = NULL;
1748 int i;
1749
1750 printf("index=%d\n", index);
1751
1752 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1753 if (!line) {
1754 printf("non linux entry\n");
1755 return;
1756 }
1757
1758 printf("kernel=%s\n", line->elements[1].item);
1759
1760 if (line->numElements >= 3) {
1761 printf("args=\"");
1762 i = 2;
1763 while (i < line->numElements) {
1764 if (!strncmp(line->elements[i].item, "root=", 5)) {
1765 root = line->elements[i].item + 5;
1766 } else {
1767 printf("%s%s", line->elements[i].item,
1768 line->elements[i].indent);
1769 }
1770
1771 i++;
1772 }
1773 printf("\"\n");
1774 } else {
1775 line = getLineByType(LT_KERNELARGS, entry->lines);
1776 if (line) {
1777 char * s;
1778
1779 printf("args=\"");
1780 i = 1;
1781 while (i < line->numElements) {
1782 if (!strncmp(line->elements[i].item, "root=", 5)) {
1783 root = line->elements[i].item + 5;
1784 } else {
1785 s = line->elements[i].item;
1786
1787 printf("%s%s", s, line->elements[i].indent);
1788 }
1789
1790 i++;
1791 }
1792
1793 s = line->elements[i - 1].indent;
1794 printf("\"\n");
1795 }
1796 }
1797
1798 if (!root) {
1799 line = getLineByType(LT_ROOT, entry->lines);
1800 if (line && line->numElements >= 2)
1801 root=line->elements[1].item;
1802 }
1803
1804 if (root) {
1805 char * s = alloca(strlen(root) + 1);
1806
1807 strcpy(s, root);
1808 if (s[strlen(s) - 1] == '"')
1809 s[strlen(s) - 1] = '\0';
1810 /* make sure the root doesn't have a trailing " */
1811 printf("root=%s\n", s);
1812 }
1813
1814 line = getLineByType(LT_INITRD, entry->lines);
1815
1816 if (line && line->numElements >= 2) {
1817 printf("initrd=%s", prefix);
1818 for (i = 1; i < line->numElements; i++)
1819 printf("%s%s", line->elements[i].item, line->elements[i].indent);
1820 printf("\n");
1821 }
1822 }
1823
1824 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
1825 FILE * in;
1826 char buf[1024];
1827 char * chptr;
1828 char * start;
1829 char * param;
1830
1831 in = fopen("/etc/conf.d/grub", "r");
1832 if (!in) return 1;
1833
1834 if (lbaPtr) *lbaPtr = 0;
1835 if (bootPtr) *bootPtr = NULL;
1836
1837 while (fgets(buf, sizeof(buf), in)) {
1838 start = buf;
1839 while (isspace(*start)) start++;
1840 if (*start == '#') continue;
1841
1842 chptr = strchr(start, '=');
1843 if (!chptr) continue;
1844 chptr--;
1845 while (*chptr && isspace(*chptr)) chptr--;
1846 chptr++;
1847 *chptr = '\0';
1848
1849 param = chptr + 1;
1850 while (*param && isspace(*param)) param++;
1851 if (*param == '=') {
1852 param++;
1853 while (*param && isspace(*param)) param++;
1854 }
1855
1856 chptr = param;
1857 while (*chptr && !isspace(*chptr)) chptr++;
1858 *chptr = '\0';
1859
1860 if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
1861 *lbaPtr = 1;
1862 else if (!strcmp(start, "boot") && bootPtr)
1863 *bootPtr = strdup(param);
1864 }
1865
1866 fclose(in);
1867
1868 return 0;
1869 }
1870
1871 void dumpSysconfigGrub(void) {
1872 char * boot;
1873 int lba;
1874
1875 if (!parseSysconfigGrub(&lba, &boot)) {
1876 if (lba) printf("lba\n");
1877 if (boot) printf("boot=%s\n", boot);
1878 }
1879 }
1880
1881 int displayInfo(struct grubConfig * config, char * kernel,
1882 const char * prefix) {
1883 int i = 0;
1884 struct singleEntry * entry;
1885 struct singleLine * line;
1886
1887 entry = findEntryByPath(config, kernel, prefix, &i);
1888 if (!entry) {
1889 fprintf(stderr, _("grubby: kernel not found\n"));
1890 return 1;
1891 }
1892
1893 /* this is a horrible hack to support /etc/conf.d/grub; there must
1894 be a better way */
1895 if (config->cfi == &grubConfigType) {
1896 dumpSysconfigGrub();
1897 } else {
1898 line = getLineByType(LT_BOOT, config->theLines);
1899 if (line && line->numElements >= 1) {
1900 printf("boot=%s\n", line->elements[1].item);
1901 }
1902
1903 line = getLineByType(LT_LBA, config->theLines);
1904 if (line) printf("lba\n");
1905 }
1906
1907 displayEntry(entry, prefix, i);
1908
1909 i++;
1910 while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
1911 displayEntry(entry, prefix, i);
1912 i++;
1913 }
1914
1915 return 0;
1916 }
1917
1918 struct singleLine * addLineTmpl(struct singleEntry * entry,
1919 struct singleLine * tmplLine,
1920 struct singleLine * prevLine,
1921 const char * val,
1922 struct configFileInfo * cfi)
1923 {
1924 struct singleLine * newLine = lineDup(tmplLine);
1925
1926 if (val) {
1927 /* override the inherited value with our own.
1928 * This is a little weak because it only applies to elements[1]
1929 */
1930 if (newLine->numElements > 1)
1931 removeElement(newLine, 1);
1932 insertElement(newLine, val, 1, cfi);
1933
1934 /* but try to keep the rootspec from the template... sigh */
1935 if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD)) {
1936 char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
1937 if (rootspec != NULL) {
1938 free(newLine->elements[1].item);
1939 newLine->elements[1].item =
1940 sdupprintf("%s%s", rootspec, val);
1941 }
1942 }
1943 }
1944
1945 dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
1946 newLine->elements[0].item : "");
1947
1948 if (!entry->lines) {
1949 /* first one on the list */
1950 entry->lines = newLine;
1951 } else if (prevLine) {
1952 /* add after prevLine */
1953 newLine->next = prevLine->next;
1954 prevLine->next = newLine;
1955 }
1956
1957 return newLine;
1958 }
1959
1960 /* val may be NULL */
1961 struct singleLine * addLine(struct singleEntry * entry,
1962 struct configFileInfo * cfi,
1963 enum lineType_e type, char * defaultIndent,
1964 const char * val) {
1965 struct singleLine * line, * prev;
1966 struct keywordTypes * kw;
1967 struct singleLine tmpl;
1968
1969 /* NB: This function shouldn't allocate items on the heap, rather on the
1970 * stack since it calls addLineTmpl which will make copies.
1971 */
1972
1973 if (type == LT_TITLE && cfi->titleBracketed) {
1974 /* we're doing a bracketed title (zipl) */
1975 tmpl.type = type;
1976 tmpl.numElements = 1;
1977 tmpl.elements = alloca(sizeof(*tmpl.elements));
1978 tmpl.elements[0].item = alloca(strlen(val)+3);
1979 sprintf(tmpl.elements[0].item, "[%s]", val);
1980 tmpl.elements[0].indent = "";
1981 val = NULL;
1982 } else if (type == LT_MENUENTRY) {
1983 char *lineend = "--class gnu-linux --class gnu --class os {";
1984 if (!val) {
1985 fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
1986 abort();
1987 }
1988 kw = getKeywordByType(type, cfi);
1989 if (!kw) {
1990 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
1991 abort();
1992 }
1993 tmpl.indent = "";
1994 tmpl.type = type;
1995 tmpl.numElements = 3;
1996 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
1997 tmpl.elements[0].item = kw->key;
1998 tmpl.elements[0].indent = alloca(2);
1999 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2000 tmpl.elements[1].item = (char *)val;
2001 tmpl.elements[1].indent = alloca(2);
2002 sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2003 tmpl.elements[2].item = alloca(strlen(lineend)+1);
2004 strcpy(tmpl.elements[2].item, lineend);
2005 tmpl.elements[2].indent = "";
2006 } else {
2007 kw = getKeywordByType(type, cfi);
2008 if (!kw) {
2009 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2010 abort();
2011 }
2012 tmpl.type = type;
2013 tmpl.numElements = val ? 2 : 1;
2014 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2015 tmpl.elements[0].item = kw->key;
2016 tmpl.elements[0].indent = alloca(2);
2017 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2018 if (val) {
2019 tmpl.elements[1].item = (char *)val;
2020 tmpl.elements[1].indent = "";
2021 }
2022 }
2023
2024 /* The last non-empty line gives us the indention to us and the line
2025 to insert after. Note that comments are considered empty lines, which
2026 may not be ideal? If there are no lines or we are looking at the
2027 first line, we use defaultIndent (the first line is normally indented
2028 differently from the rest) */
2029 for (line = entry->lines, prev = NULL; line; line = line->next) {
2030 if (line->numElements) prev = line;
2031 /* fall back on the last line if prev isn't otherwise set */
2032 if (!line->next && !prev) prev = line;
2033 }
2034
2035 struct singleLine *menuEntry;
2036 menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2037 if (tmpl.type == LT_ENTRY_END) {
2038 if (menuEntry)
2039 tmpl.indent = menuEntry->indent;
2040 else
2041 tmpl.indent = defaultIndent ?: "";
2042 } else if (tmpl.type != LT_MENUENTRY) {
2043 if (menuEntry)
2044 tmpl.indent = "\t";
2045 else if (prev == entry->lines)
2046 tmpl.indent = defaultIndent ?: "";
2047 else
2048 tmpl.indent = prev->indent;
2049 }
2050
2051 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2052 }
2053
2054 void removeLine(struct singleEntry * entry, struct singleLine * line) {
2055 struct singleLine * prev;
2056 int i;
2057
2058 for (i = 0; i < line->numElements; i++) {
2059 free(line->elements[i].item);
2060 free(line->elements[i].indent);
2061 }
2062 free(line->elements);
2063 free(line->indent);
2064
2065 if (line == entry->lines) {
2066 entry->lines = line->next;
2067 } else {
2068 prev = entry->lines;
2069 while (prev->next != line) prev = prev->next;
2070 prev->next = line->next;
2071 }
2072
2073 free(line);
2074 }
2075
2076 static int isquote(char q)
2077 {
2078 if (q == '\'' || q == '\"')
2079 return 1;
2080 return 0;
2081 }
2082
2083 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2084 {
2085 struct singleLine newLine = {
2086 .indent = tmplLine->indent,
2087 .type = tmplLine->type,
2088 .next = tmplLine->next,
2089 };
2090 int firstQuotedItem = -1;
2091 int quoteLen = 0;
2092 int j;
2093 int element = 0;
2094 char *c;
2095
2096 c = malloc(strlen(tmplLine->elements[0].item) + 1);
2097 strcpy(c, tmplLine->elements[0].item);
2098 insertElement(&newLine, c, element++, cfi);
2099 free(c);
2100 c = NULL;
2101
2102 for (j = 1; j < tmplLine->numElements; j++) {
2103 if (firstQuotedItem == -1) {
2104 quoteLen += strlen(tmplLine->elements[j].item);
2105
2106 if (isquote(tmplLine->elements[j].item[0])) {
2107 firstQuotedItem = j;
2108 quoteLen += strlen(tmplLine->elements[j].indent);
2109 } else {
2110 c = malloc(quoteLen + 1);
2111 strcpy(c, tmplLine->elements[j].item);
2112 insertElement(&newLine, c, element++, cfi);
2113 free(c);
2114 quoteLen = 0;
2115 }
2116 } else {
2117 int itemlen = strlen(tmplLine->elements[j].item);
2118 quoteLen += itemlen;
2119 quoteLen += strlen(tmplLine->elements[j].indent);
2120
2121 if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2122 c = malloc(quoteLen + 1);
2123 c[0] = '\0';
2124 for (int i = firstQuotedItem; i < j+1; i++) {
2125 strcat(c, tmplLine->elements[i].item);
2126 strcat(c, tmplLine->elements[i].indent);
2127 }
2128 insertElement(&newLine, c, element++, cfi);
2129 free(c);
2130
2131 firstQuotedItem = -1;
2132 quoteLen = 0;
2133 }
2134 }
2135 }
2136 while (tmplLine->numElements)
2137 removeElement(tmplLine, 0);
2138 if (tmplLine->elements)
2139 free(tmplLine->elements);
2140
2141 tmplLine->numElements = newLine.numElements;
2142 tmplLine->elements = newLine.elements;
2143 }
2144
2145 static void insertElement(struct singleLine * line,
2146 const char * item, int insertHere,
2147 struct configFileInfo * cfi)
2148 {
2149 struct keywordTypes * kw;
2150 char indent[2] = "";
2151
2152 /* sanity check */
2153 if (insertHere > line->numElements) {
2154 dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2155 insertHere, line->numElements);
2156 insertHere = line->numElements;
2157 }
2158
2159 line->elements = realloc(line->elements, (line->numElements + 1) *
2160 sizeof(*line->elements));
2161 memmove(&line->elements[insertHere+1],
2162 &line->elements[insertHere],
2163 (line->numElements - insertHere) *
2164 sizeof(*line->elements));
2165 line->elements[insertHere].item = strdup(item);
2166
2167 kw = getKeywordByType(line->type, cfi);
2168
2169 if (line->numElements == 0) {
2170 indent[0] = '\0';
2171 } else if (insertHere == 0) {
2172 indent[0] = kw->nextChar;
2173 } else if (kw->separatorChar != '\0') {
2174 indent[0] = kw->separatorChar;
2175 } else {
2176 indent[0] = ' ';
2177 }
2178
2179 if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
2180 /* move the end-of-line forward */
2181 line->elements[insertHere].indent =
2182 line->elements[insertHere-1].indent;
2183 line->elements[insertHere-1].indent = strdup(indent);
2184 } else {
2185 line->elements[insertHere].indent = strdup(indent);
2186 }
2187
2188 line->numElements++;
2189
2190 dbgPrintf("insertElement(%s, '%s%s', %d)\n",
2191 line->elements[0].item,
2192 line->elements[insertHere].item,
2193 line->elements[insertHere].indent,
2194 insertHere);
2195 }
2196
2197 static void removeElement(struct singleLine * line, int removeHere) {
2198 int i;
2199
2200 /* sanity check */
2201 if (removeHere >= line->numElements) return;
2202
2203 dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
2204 removeHere, line->elements[removeHere].item);
2205
2206 free(line->elements[removeHere].item);
2207
2208 if (removeHere > 1) {
2209 /* previous argument gets this argument's post-indentation */
2210 free(line->elements[removeHere-1].indent);
2211 line->elements[removeHere-1].indent =
2212 line->elements[removeHere].indent;
2213 } else {
2214 free(line->elements[removeHere].indent);
2215 }
2216
2217 /* now collapse the array, but don't bother to realloc smaller */
2218 for (i = removeHere; i < line->numElements - 1; i++)
2219 line->elements[i] = line->elements[i + 1];
2220
2221 line->numElements--;
2222 }
2223
2224 int argMatch(const char * one, const char * two) {
2225 char * first, * second;
2226 char * chptr;
2227
2228 first = strcpy(alloca(strlen(one) + 1), one);
2229 second = strcpy(alloca(strlen(two) + 1), two);
2230
2231 chptr = strchr(first, '=');
2232 if (chptr) *chptr = '\0';
2233
2234 chptr = strchr(second, '=');
2235 if (chptr) *chptr = '\0';
2236
2237 return strcmp(first, second);
2238 }
2239
2240 int updateActualImage(struct grubConfig * cfg, const char * image,
2241 const char * prefix, const char * addArgs,
2242 const char * removeArgs, int multibootArgs) {
2243 struct singleEntry * entry;
2244 struct singleLine * line, * rootLine;
2245 int index = 0;
2246 int i, k;
2247 const char ** newArgs, ** oldArgs;
2248 const char ** arg;
2249 int useKernelArgs, useRoot;
2250 int firstElement;
2251 int *usedElements;
2252 int doreplace;
2253
2254 if (!image) return 0;
2255
2256 if (!addArgs) {
2257 newArgs = malloc(sizeof(*newArgs));
2258 *newArgs = NULL;
2259 } else {
2260 if (poptParseArgvString(addArgs, NULL, &newArgs)) {
2261 fprintf(stderr,
2262 _("grubby: error separating arguments '%s'\n"), addArgs);
2263 return 1;
2264 }
2265 }
2266
2267 if (!removeArgs) {
2268 oldArgs = malloc(sizeof(*oldArgs));
2269 *oldArgs = NULL;
2270 } else {
2271 if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
2272 fprintf(stderr,
2273 _("grubby: error separating arguments '%s'\n"), removeArgs);
2274 free(newArgs);
2275 return 1;
2276 }
2277 }
2278
2279
2280 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
2281 && (!multibootArgs || cfg->cfi->mbConcatArgs));
2282
2283 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
2284 && !multibootArgs);
2285
2286 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2287
2288 if (multibootArgs && !entry->multiboot)
2289 continue;
2290
2291 /* Determine where to put the args. If this config supports
2292 * LT_KERNELARGS, use that. Otherwise use
2293 * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
2294 */
2295 if (useKernelArgs) {
2296 line = getLineByType(LT_KERNELARGS, entry->lines);
2297 if (!line) {
2298 /* no LT_KERNELARGS, need to add it */
2299 line = addLine(entry, cfg->cfi, LT_KERNELARGS,
2300 cfg->secondaryIndent, NULL);
2301 }
2302 firstElement = 1;
2303
2304 } else if (multibootArgs) {
2305 line = getLineByType(LT_HYPER, entry->lines);
2306 if (!line) {
2307 /* a multiboot entry without LT_HYPER? */
2308 continue;
2309 }
2310 firstElement = 2;
2311
2312 } else {
2313 line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines);
2314 if (!line) {
2315 /* no LT_KERNEL or LT_MBMODULE in this entry? */
2316 continue;
2317 }
2318 firstElement = 2;
2319 }
2320
2321 /* handle the elilo case which does:
2322 * append="hypervisor args -- kernel args"
2323 */
2324 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
2325 /* this is a multiboot entry, make sure there's
2326 * -- on the args line
2327 */
2328 for (i = firstElement; i < line->numElements; i++) {
2329 if (!strcmp(line->elements[i].item, "--"))
2330 break;
2331 }
2332 if (i == line->numElements) {
2333 /* assume all existing args are kernel args,
2334 * prepend -- to make it official
2335 */
2336 insertElement(line, "--", firstElement, cfg->cfi);
2337 i = firstElement;
2338 }
2339 if (!multibootArgs) {
2340 /* kernel args start after the -- */
2341 firstElement = i + 1;
2342 }
2343 } else if (cfg->cfi->mbConcatArgs) {
2344 /* this is a non-multiboot entry, remove hyper args */
2345 for (i = firstElement; i < line->numElements; i++) {
2346 if (!strcmp(line->elements[i].item, "--"))
2347 break;
2348 }
2349 if (i < line->numElements) {
2350 /* remove args up to -- */
2351 while (strcmp(line->elements[firstElement].item, "--"))
2352 removeElement(line, firstElement);
2353 /* remove -- */
2354 removeElement(line, firstElement);
2355 }
2356 }
2357
2358 usedElements = calloc(line->numElements, sizeof(*usedElements));
2359
2360 for (k = 0, arg = newArgs; *arg; arg++, k++) {
2361
2362 doreplace = 1;
2363 for (i = firstElement; i < line->numElements; i++) {
2364 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2365 !strcmp(line->elements[i].item, "--"))
2366 {
2367 /* reached the end of hyper args, insert here */
2368 doreplace = 0;
2369 break;
2370 }
2371 if (usedElements[i])
2372 continue;
2373 if (!argMatch(line->elements[i].item, *arg)) {
2374 usedElements[i]=1;
2375 break;
2376 }
2377 }
2378
2379 if (i < line->numElements && doreplace) {
2380 /* direct replacement */
2381 free(line->elements[i].item);
2382 line->elements[i].item = strdup(*arg);
2383
2384 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
2385 /* root= replacement */
2386 rootLine = getLineByType(LT_ROOT, entry->lines);
2387 if (rootLine) {
2388 free(rootLine->elements[1].item);
2389 rootLine->elements[1].item = strdup(*arg + 5);
2390 } else {
2391 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
2392 cfg->secondaryIndent, *arg + 5);
2393 }
2394 }
2395
2396 else {
2397 /* insert/append */
2398 insertElement(line, *arg, i, cfg->cfi);
2399 usedElements = realloc(usedElements, line->numElements *
2400 sizeof(*usedElements));
2401 memmove(&usedElements[i + 1], &usedElements[i],
2402 line->numElements - i - 1);
2403 usedElements[i] = 1;
2404
2405 /* if we updated a root= here even though there is a
2406 LT_ROOT available we need to remove the LT_ROOT entry
2407 (this will happen if we switch from a device to a label) */
2408 if (useRoot && !strncmp(*arg, "root=", 5)) {
2409 rootLine = getLineByType(LT_ROOT, entry->lines);
2410 if (rootLine)
2411 removeLine(entry, rootLine);
2412 }
2413 }
2414 }
2415
2416 free(usedElements);
2417
2418 for (arg = oldArgs; *arg; arg++) {
2419 for (i = firstElement; i < line->numElements; i++) {
2420 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2421 !strcmp(line->elements[i].item, "--"))
2422 /* reached the end of hyper args, stop here */
2423 break;
2424 if (!argMatch(line->elements[i].item, *arg)) {
2425 removeElement(line, i);
2426 break;
2427 }
2428 }
2429 /* handle removing LT_ROOT line too */
2430 if (useRoot && !strncmp(*arg, "root=", 5)) {
2431 rootLine = getLineByType(LT_ROOT, entry->lines);
2432 if (rootLine)
2433 removeLine(entry, rootLine);
2434 }
2435 }
2436
2437 if (line->numElements == 1) {
2438 /* don't need the line at all (note it has to be a
2439 LT_KERNELARGS for this to happen */
2440 removeLine(entry, line);
2441 }
2442 }
2443
2444 free(newArgs);
2445 free(oldArgs);
2446
2447 return 0;
2448 }
2449
2450 int updateImage(struct grubConfig * cfg, const char * image,
2451 const char * prefix, const char * addArgs,
2452 const char * removeArgs,
2453 const char * addMBArgs, const char * removeMBArgs) {
2454 int rc = 0;
2455
2456 if (!image) return rc;
2457
2458 /* update the main args first... */
2459 if (addArgs || removeArgs)
2460 rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
2461 if (rc) return rc;
2462
2463 /* and now any multiboot args */
2464 if (addMBArgs || removeMBArgs)
2465 rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
2466 return rc;
2467 }
2468
2469 int updateInitrd(struct grubConfig * cfg, const char * image,
2470 const char * prefix, const char * initrd) {
2471 struct singleEntry * entry;
2472 struct singleLine * line, * kernelLine, *endLine = NULL;
2473 int index = 0;
2474
2475 if (!image) return 0;
2476
2477 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2478 kernelLine = getLineByType(LT_KERNEL, entry->lines);
2479 if (!kernelLine) continue;
2480
2481 line = getLineByType(LT_INITRD, entry->lines);
2482 if (line)
2483 removeLine(entry, line);
2484 if (prefix) {
2485 int prefixLen = strlen(prefix);
2486 if (!strncmp(initrd, prefix, prefixLen))
2487 initrd += prefixLen;
2488 }
2489 endLine = getLineByType(LT_ENTRY_END, entry->lines);
2490 if (endLine)
2491 removeLine(entry, endLine);
2492 line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd);
2493 if (!line)
2494 return 1;
2495 if (endLine) {
2496 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
2497 if (!line)
2498 return 1;
2499 }
2500
2501 break;
2502 }
2503
2504 return 0;
2505 }
2506
2507 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
2508 int fd;
2509 unsigned char bootSect[512];
2510 int offset;
2511
2512 fd = open(device, O_RDONLY);
2513 if (fd < 0) {
2514 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
2515 device, strerror(errno));
2516 return 1;
2517 }
2518
2519 if (read(fd, bootSect, 512) != 512) {
2520 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2521 device, strerror(errno));
2522 return 1;
2523 }
2524 close(fd);
2525
2526 /* first three bytes should match, a jmp short should be in there */
2527 if (memcmp(boot, bootSect, 3))
2528 return 0;
2529
2530 if (boot[1] == JMP_SHORT_OPCODE) {
2531 offset = boot[2] + 2;
2532 } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
2533 offset = (boot[3] << 8) + boot[2] + 2;
2534 } else if (boot[0] == JMP_SHORT_OPCODE) {
2535 offset = boot[1] + 2;
2536 /*
2537 * it looks like grub, when copying stage1 into the mbr, patches stage1
2538 * right after the JMP location, replacing other instructions such as
2539 * JMPs for NOOPs. So, relax the check a little bit by skipping those
2540 * different bytes.
2541 */
2542 if ((bootSect[offset + 1] == NOOP_OPCODE)
2543 && (bootSect[offset + 2] == NOOP_OPCODE)) {
2544 offset = offset + 3;
2545 }
2546 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
2547 offset = (boot[2] << 8) + boot[1] + 2;
2548 } else {
2549 return 0;
2550 }
2551
2552 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
2553 return 0;
2554
2555 return 2;
2556 }
2557
2558 int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
2559 int fd;
2560 char buf[65536];
2561 char * end;
2562 char * chptr;
2563 char * chptr2;
2564 int rc;
2565
2566 /* it's on raid; we need to parse /proc/mdstat and check all of the
2567 *raw* devices listed in there */
2568
2569 if (!strncmp(mdDev, "/dev/", 5))
2570 mdDev += 5;
2571
2572 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
2573 fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
2574 strerror(errno));
2575 return 2;
2576 }
2577
2578 rc = read(fd, buf, sizeof(buf) - 1);
2579 if (rc < 0 || rc == (sizeof(buf) - 1)) {
2580 fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
2581 strerror(errno));
2582 close(fd);
2583 return 2;
2584 }
2585 close(fd);
2586 buf[rc] = '\0';
2587
2588 chptr = buf;
2589 while (*chptr) {
2590 end = strchr(chptr, '\n');
2591 if (!end) break;
2592 *end = '\0';
2593
2594 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
2595 chptr[strlen(mdDev)] == ' ') {
2596
2597 /* found the device */
2598 while (*chptr && *chptr != ':') chptr++;
2599 chptr++;
2600 while (*chptr && isspace(*chptr)) chptr++;
2601
2602 /* skip the "active" bit */
2603 while (*chptr && !isspace(*chptr)) chptr++;
2604 while (*chptr && isspace(*chptr)) chptr++;
2605
2606 /* skip the raid level */
2607 while (*chptr && !isspace(*chptr)) chptr++;
2608 while (*chptr && isspace(*chptr)) chptr++;
2609
2610 /* everything else is partition stuff */
2611 while (*chptr) {
2612 chptr2 = chptr;
2613 while (*chptr2 && *chptr2 != '[') chptr2++;
2614 if (!*chptr2) break;
2615
2616 /* yank off the numbers at the end */
2617 chptr2--;
2618 while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
2619 chptr2++;
2620 *chptr2 = '\0';
2621
2622 /* Better, now we need the /dev/ back. We're done with
2623 * everything before this point, so we can just put
2624 * the /dev/ part there. There will always be room. */
2625 memcpy(chptr - 5, "/dev/", 5);
2626 rc = checkDeviceBootloader(chptr - 5, boot);
2627 if (rc != 2) {
2628 return rc;
2629 }
2630
2631 chptr = chptr2 + 1;
2632 /* skip the [11] bit */
2633 while (*chptr && !isspace(*chptr)) chptr++;
2634 /* and move to the next one */
2635 while (*chptr && isspace(*chptr)) chptr++;
2636 }
2637
2638 /* we're good to go */
2639 return 2;
2640 }
2641
2642 chptr = end + 1;
2643 }
2644
2645 fprintf(stderr,
2646 _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
2647 mdDev);
2648 return 0;
2649 }
2650
2651 int checkForLilo(struct grubConfig * config) {
2652 int fd;
2653 unsigned char boot[512];
2654 struct singleLine * line;
2655
2656 for (line = config->theLines; line; line = line->next)
2657 if (line->type == LT_BOOT) break;
2658
2659 if (!line) {
2660 fprintf(stderr,
2661 _("grubby: no boot line found in lilo configuration\n"));
2662 return 1;
2663 }
2664
2665 if (line->numElements != 2) return 1;
2666
2667 fd = open("/boot/boot.b", O_RDONLY);
2668 if (fd < 0) {
2669 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
2670 "/boot/boot.b", strerror(errno));
2671 return 1;
2672 }
2673
2674 if (read(fd, boot, 512) != 512) {
2675 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2676 "/boot/boot.b", strerror(errno));
2677 return 1;
2678 }
2679 close(fd);
2680
2681 if (!strncmp("/dev/md", line->elements[1].item, 7))
2682 return checkLiloOnRaid(line->elements[1].item, boot);
2683
2684 return checkDeviceBootloader(line->elements[1].item, boot);
2685 }
2686
2687 int checkForGrub2(struct grubConfig * config) {
2688 if (!access("/etc/grub.d/", R_OK))
2689 return 2;
2690
2691 return 1;
2692 }
2693
2694 int checkForGrub(struct grubConfig * config) {
2695 int fd;
2696 unsigned char bootSect[512];
2697 char * boot;
2698
2699 if (parseSysconfigGrub(NULL, &boot))
2700 return 0;
2701
2702 /* assume grub is not installed -- not an error condition */
2703 if (!boot)
2704 return 0;
2705
2706 fd = open("/boot/grub/stage1", O_RDONLY);
2707 if (fd < 0)
2708 /* this doesn't exist if grub hasn't been installed */
2709 return 0;
2710
2711 if (read(fd, bootSect, 512) != 512) {
2712 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2713 "/boot/grub/stage1", strerror(errno));
2714 close(fd);
2715 return 1;
2716 }
2717 close(fd);
2718
2719 return checkDeviceBootloader(boot, bootSect);
2720 }
2721
2722 int checkForExtLinux(struct grubConfig * config) {
2723 int fd;
2724 unsigned char bootSect[512];
2725 char * boot;
2726 char executable[] = "/boot/extlinux/extlinux";
2727
2728 printf("entered: checkForExtLinux()\n");
2729
2730 if (parseSysconfigGrub(NULL, &boot))
2731 return 0;
2732
2733 /* assume grub is not installed -- not an error condition */
2734 if (!boot)
2735 return 0;
2736
2737 fd = open(executable, O_RDONLY);
2738 if (fd < 0)
2739 /* this doesn't exist if grub hasn't been installed */
2740 return 0;
2741
2742 if (read(fd, bootSect, 512) != 512) {
2743 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2744 executable, strerror(errno));
2745 return 1;
2746 }
2747 close(fd);
2748
2749 return checkDeviceBootloader(boot, bootSect);
2750 }
2751
2752 static char * getRootSpecifier(char * str) {
2753 char * idx, * rootspec = NULL;
2754
2755 if (*str == '(') {
2756 idx = rootspec = strdup(str);
2757 while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
2758 *(++idx) = '\0';
2759 }
2760 return rootspec;
2761 }
2762
2763 static char * getInitrdVal(struct grubConfig * config,
2764 const char * prefix, struct singleLine *tmplLine,
2765 const char * newKernelInitrd,
2766 char ** extraInitrds, int extraInitrdCount)
2767 {
2768 char *initrdVal, *end;
2769 int i;
2770 size_t totalSize;
2771 size_t prefixLen;
2772 char separatorChar;
2773
2774 prefixLen = strlen(prefix);
2775 totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
2776
2777 for (i = 0; i < extraInitrdCount; i++) {
2778 totalSize += sizeof(separatorChar);
2779 totalSize += strlen(extraInitrds[i]) - prefixLen;
2780 }
2781
2782 initrdVal = end = malloc(totalSize);
2783
2784 end = stpcpy (end, newKernelInitrd + prefixLen);
2785
2786 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
2787 for (i = 0; i < extraInitrdCount; i++) {
2788 const char *extraInitrd;
2789 int j;
2790
2791 extraInitrd = extraInitrds[i] + prefixLen;
2792 /* Don't add entries that are already there */
2793 if (tmplLine != NULL) {
2794 for (j = 2; j < tmplLine->numElements; j++)
2795 if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
2796 break;
2797
2798 if (j != tmplLine->numElements)
2799 continue;
2800 }
2801
2802 *end++ = separatorChar;
2803 end = stpcpy(end, extraInitrd);
2804 }
2805
2806 return initrdVal;
2807 }
2808
2809 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
2810 const char * prefix,
2811 char * newKernelPath, char * newKernelTitle,
2812 char * newKernelArgs, char * newKernelInitrd,
2813 char ** extraInitrds, int extraInitrdCount,
2814 char * newMBKernel, char * newMBKernelArgs) {
2815 struct singleEntry * new;
2816 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
2817 int needs;
2818 char * chptr;
2819
2820 if (!newKernelPath) return 0;
2821
2822 /* if the newKernelTitle is too long silently munge it into something
2823 * we can live with. truncating is first check, then we'll just mess with
2824 * it until it looks better */
2825 if (config->cfi->maxTitleLength &&
2826 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
2827 char * buf = alloca(config->cfi->maxTitleLength + 7);
2828 char * numBuf = alloca(config->cfi->maxTitleLength + 1);
2829 int i = 1;
2830
2831 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
2832 while (findEntryByPath(config, buf, NULL, NULL)) {
2833 sprintf(numBuf, "%d", i++);
2834 strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
2835 }
2836
2837 newKernelTitle = buf + 6;
2838 }
2839
2840 new = malloc(sizeof(*new));
2841 new->skip = 0;
2842 new->multiboot = 0;
2843 new->next = config->entries;
2844 new->lines = NULL;
2845 config->entries = new;
2846
2847 /* copy/update from the template */
2848 needs = NEED_KERNEL | NEED_TITLE;
2849 if (newKernelInitrd)
2850 needs |= NEED_INITRD;
2851 if (newMBKernel) {
2852 needs |= NEED_MB;
2853 new->multiboot = 1;
2854 }
2855
2856 if (template) {
2857 for (masterLine = template->lines;
2858 masterLine && (tmplLine = lineDup(masterLine));
2859 lineFree(tmplLine), masterLine = masterLine->next)
2860 {
2861 dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
2862
2863 /* skip comments */
2864 chptr = tmplLine->indent;
2865 while (*chptr && isspace(*chptr)) chptr++;
2866 if (*chptr == '#') continue;
2867
2868 if (tmplLine->type == LT_KERNEL &&
2869 tmplLine->numElements >= 2) {
2870 if (!template->multiboot && (needs & NEED_MB)) {
2871 /* it's not a multiboot template and this is the kernel
2872 * line. Try to be intelligent about inserting the
2873 * hypervisor at the same time.
2874 */
2875 if (config->cfi->mbHyperFirst) {
2876 /* insert the hypervisor first */
2877 newLine = addLine(new, config->cfi, LT_HYPER,
2878 tmplLine->indent,
2879 newMBKernel + strlen(prefix));
2880 /* set up for adding the kernel line */
2881 free(tmplLine->indent);
2882 tmplLine->indent = strdup(config->secondaryIndent);
2883 needs &= ~NEED_MB;
2884 }
2885 if (needs & NEED_KERNEL) {
2886 /* use addLineTmpl to preserve line elements,
2887 * otherwise we could just call addLine. Unfortunately
2888 * this means making some changes to the template
2889 * such as the indent change above and the type
2890 * change below.
2891 */
2892 struct keywordTypes * mbm_kw =
2893 getKeywordByType(LT_MBMODULE, config->cfi);
2894 if (mbm_kw) {
2895 tmplLine->type = LT_MBMODULE;
2896 free(tmplLine->elements[0].item);
2897 tmplLine->elements[0].item = strdup(mbm_kw->key);
2898 }
2899 newLine = addLineTmpl(new, tmplLine, newLine,
2900 newKernelPath + strlen(prefix), config->cfi);
2901 needs &= ~NEED_KERNEL;
2902 }
2903 if (needs & NEED_MB) { /* !mbHyperFirst */
2904 newLine = addLine(new, config->cfi, LT_HYPER,
2905 config->secondaryIndent,
2906 newMBKernel + strlen(prefix));
2907 needs &= ~NEED_MB;
2908 }
2909 } else if (needs & NEED_KERNEL) {
2910 newLine = addLineTmpl(new, tmplLine, newLine,
2911 newKernelPath + strlen(prefix), config->cfi);
2912 needs &= ~NEED_KERNEL;
2913 }
2914
2915 } else if (tmplLine->type == LT_HYPER &&
2916 tmplLine->numElements >= 2) {
2917 if (needs & NEED_MB) {
2918 newLine = addLineTmpl(new, tmplLine, newLine,
2919 newMBKernel + strlen(prefix), config->cfi);
2920 needs &= ~NEED_MB;
2921 }
2922
2923 } else if (tmplLine->type == LT_MBMODULE &&
2924 tmplLine->numElements >= 2) {
2925 if (new->multiboot) {
2926 if (needs & NEED_KERNEL) {
2927 newLine = addLineTmpl(new, tmplLine, newLine,
2928 newKernelPath +
2929 strlen(prefix), config->cfi);
2930 needs &= ~NEED_KERNEL;
2931 } else if (config->cfi->mbInitRdIsModule &&
2932 (needs & NEED_INITRD)) {
2933 char *initrdVal;
2934 initrdVal = getInitrdVal(config, prefix, tmplLine,
2935 newKernelInitrd, extraInitrds,
2936 extraInitrdCount);
2937 newLine = addLineTmpl(new, tmplLine, newLine,
2938 initrdVal, config->cfi);
2939 free(initrdVal);
2940 needs &= ~NEED_INITRD;
2941 }
2942 } else if (needs & NEED_KERNEL) {
2943 /* template is multi but new is not,
2944 * insert the kernel in the first module slot
2945 */
2946 tmplLine->type = LT_KERNEL;
2947 free(tmplLine->elements[0].item);
2948 tmplLine->elements[0].item =
2949 strdup(getKeywordByType(LT_KERNEL, config->cfi)->key);
2950 newLine = addLineTmpl(new, tmplLine, newLine,
2951 newKernelPath + strlen(prefix), config->cfi);
2952 needs &= ~NEED_KERNEL;
2953 } else if (needs & NEED_INITRD) {
2954 char *initrdVal;
2955 /* template is multi but new is not,
2956 * insert the initrd in the second module slot
2957 */
2958 tmplLine->type = LT_INITRD;
2959 free(tmplLine->elements[0].item);
2960 tmplLine->elements[0].item =
2961 strdup(getKeywordByType(LT_INITRD, config->cfi)->key);
2962 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
2963 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
2964 free(initrdVal);
2965 needs &= ~NEED_INITRD;
2966 }
2967
2968 } else if (tmplLine->type == LT_INITRD &&
2969 tmplLine->numElements >= 2) {
2970 if (needs & NEED_INITRD &&
2971 new->multiboot && !template->multiboot &&
2972 config->cfi->mbInitRdIsModule) {
2973 /* make sure we don't insert the module initrd
2974 * before the module kernel... if we don't do it here,
2975 * it will be inserted following the template.
2976 */
2977 if (!needs & NEED_KERNEL) {
2978 char *initrdVal;
2979
2980 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
2981 newLine = addLine(new, config->cfi, LT_MBMODULE,
2982 config->secondaryIndent,
2983 initrdVal);
2984 free(initrdVal);
2985 needs &= ~NEED_INITRD;
2986 }
2987 } else if (needs & NEED_INITRD) {
2988 char *initrdVal;
2989 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
2990 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
2991 free(initrdVal);
2992 needs &= ~NEED_INITRD;
2993 }
2994
2995 } else if (tmplLine->type == LT_MENUENTRY &&
2996 (needs & NEED_TITLE)) {
2997 requote(tmplLine, config->cfi);
2998 char *nkt = malloc(strlen(newKernelTitle)+3);
2999 strcpy(nkt, "'");
3000 strcat(nkt, newKernelTitle);
3001 strcat(nkt, "'");
3002 newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3003 free(nkt);
3004 needs &= ~NEED_TITLE;
3005 } else if (tmplLine->type == LT_TITLE &&
3006 (needs & NEED_TITLE)) {
3007 if (tmplLine->numElements >= 2) {
3008 newLine = addLineTmpl(new, tmplLine, newLine,
3009 newKernelTitle, config->cfi);
3010 needs &= ~NEED_TITLE;
3011 } else if (tmplLine->numElements == 1 &&
3012 config->cfi->titleBracketed) {
3013 /* addLineTmpl doesn't handle titleBracketed */
3014 newLine = addLine(new, config->cfi, LT_TITLE,
3015 tmplLine->indent, newKernelTitle);
3016 needs &= ~NEED_TITLE;
3017 }
3018 } else if (tmplLine->type == LT_ECHO) {
3019 requote(tmplLine, config->cfi);
3020 static const char *prefix = "'Loading ";
3021 if (tmplLine->numElements > 1 &&
3022 strstr(tmplLine->elements[1].item, prefix) &&
3023 masterLine->next && masterLine->next->type == LT_KERNEL) {
3024 char *newTitle = malloc(strlen(prefix) +
3025 strlen(newKernelTitle) + 2);
3026
3027 strcpy(newTitle, prefix);
3028 strcat(newTitle, newKernelTitle);
3029 strcat(newTitle, "'");
3030 newLine = addLine(new, config->cfi, LT_ECHO,
3031 tmplLine->indent, newTitle);
3032 free(newTitle);
3033 } else {
3034 /* pass through other lines from the template */
3035 newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3036 config->cfi);
3037 }
3038 } else {
3039 /* pass through other lines from the template */
3040 newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3041 }
3042 }
3043
3044 } else {
3045 /* don't have a template, so start the entry with the
3046 * appropriate starting line
3047 */
3048 switch (config->cfi->entryStart) {
3049 case LT_KERNEL:
3050 if (new->multiboot && config->cfi->mbHyperFirst) {
3051 /* fall through to LT_HYPER */
3052 } else {
3053 newLine = addLine(new, config->cfi, LT_KERNEL,
3054 config->primaryIndent,
3055 newKernelPath + strlen(prefix));
3056 needs &= ~NEED_KERNEL;
3057 break;
3058 }
3059
3060 case LT_HYPER:
3061 newLine = addLine(new, config->cfi, LT_HYPER,
3062 config->primaryIndent,
3063 newMBKernel + strlen(prefix));
3064 needs &= ~NEED_MB;
3065 break;
3066
3067 case LT_MENUENTRY: {
3068 char *nkt = malloc(strlen(newKernelTitle)+3);
3069 strcpy(nkt, "'");
3070 strcat(nkt, newKernelTitle);
3071 strcat(nkt, "'");
3072 newLine = addLine(new, config->cfi, LT_MENUENTRY,
3073 config->primaryIndent, nkt);
3074 free(nkt);
3075 needs &= ~NEED_TITLE;
3076 needs |= NEED_END;
3077 break;
3078 }
3079 case LT_TITLE:
3080 if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
3081 char * templabel;
3082 int x = 0, y = 0;
3083
3084 templabel = strdup(newKernelTitle);
3085 while( templabel[x]){
3086 if( templabel[x] == ' ' ){
3087 y = x;
3088 while( templabel[y] ){
3089 templabel[y] = templabel[y+1];
3090 y++;
3091 }
3092 }
3093 x++;
3094 }
3095 newLine = addLine(new, config->cfi, LT_TITLE,
3096 config->primaryIndent, templabel);
3097 free(templabel);
3098 }else{
3099 newLine = addLine(new, config->cfi, LT_TITLE,
3100 config->primaryIndent, newKernelTitle);
3101 }
3102 needs &= ~NEED_TITLE;
3103 break;
3104
3105 default:
3106 abort();
3107 }
3108 }
3109
3110 /* add the remainder of the lines, i.e. those that either
3111 * weren't present in the template, or in the case of no template,
3112 * all the lines following the entryStart.
3113 */
3114 if (needs & NEED_TITLE) {
3115 newLine = addLine(new, config->cfi, LT_TITLE,
3116 config->secondaryIndent,
3117 newKernelTitle);
3118 needs &= ~NEED_TITLE;
3119 }
3120 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
3121 newLine = addLine(new, config->cfi, LT_HYPER,
3122 config->secondaryIndent,
3123 newMBKernel + strlen(prefix));
3124 needs &= ~NEED_MB;
3125 }
3126 if (needs & NEED_KERNEL) {
3127 newLine = addLine(new, config->cfi,
3128 (new->multiboot && getKeywordByType(LT_MBMODULE,
3129 config->cfi)) ?
3130 LT_MBMODULE : LT_KERNEL,
3131 config->secondaryIndent,
3132 newKernelPath + strlen(prefix));
3133 needs &= ~NEED_KERNEL;
3134 }
3135 if (needs & NEED_MB) {
3136 newLine = addLine(new, config->cfi, LT_HYPER,
3137 config->secondaryIndent,
3138 newMBKernel + strlen(prefix));
3139 needs &= ~NEED_MB;
3140 }
3141 if (needs & NEED_INITRD) {
3142 char *initrdVal;
3143 initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
3144 newLine = addLine(new, config->cfi,
3145 (new->multiboot && getKeywordByType(LT_MBMODULE,
3146 config->cfi)) ?
3147 LT_MBMODULE : LT_INITRD,
3148 config->secondaryIndent,
3149 initrdVal);
3150 free(initrdVal);
3151 needs &= ~NEED_INITRD;
3152 }
3153 if (needs & NEED_END) {
3154 newLine = addLine(new, config->cfi, LT_ENTRY_END,
3155 config->secondaryIndent, NULL);
3156 needs &= ~NEED_END;
3157 }
3158
3159 if (needs) {
3160 printf(_("grubby: needs=%d, aborting\n"), needs);
3161 abort();
3162 }
3163
3164 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
3165 newMBKernelArgs, NULL)) return 1;
3166
3167 return 0;
3168 }
3169
3170 static void traceback(int signum)
3171 {
3172 void *array[40];
3173 size_t size;
3174
3175 signal(SIGSEGV, SIG_DFL);
3176 memset(array, '\0', sizeof (array));
3177 size = backtrace(array, 40);
3178
3179 fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n",
3180 (unsigned long)size);
3181 backtrace_symbols_fd(array, size, STDERR_FILENO);
3182 exit(1);
3183 }
3184
3185 int main(int argc, const char ** argv) {
3186 poptContext optCon;
3187 const char * grubConfig = NULL;
3188 char * outputFile = NULL;
3189 int arg = 0;
3190 int flags = 0;
3191 int badImageOkay = 0;
3192 int configureGrub2 = 0;
3193 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
3194 int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
3195 int configureExtLinux = 0;
3196 int bootloaderProbe = 0;
3197 int extraInitrdCount = 0;
3198 char * updateKernelPath = NULL;
3199 char * newKernelPath = NULL;
3200 char * removeKernelPath = NULL;
3201 char * newKernelArgs = NULL;
3202 char * newKernelInitrd = NULL;
3203 char * newKernelTitle = NULL;
3204 char * newKernelVersion = NULL;
3205 char * newMBKernel = NULL;
3206 char * newMBKernelArgs = NULL;
3207 char * removeMBKernelArgs = NULL;
3208 char * removeMBKernel = NULL;
3209 char * bootPrefix = NULL;
3210 char * defaultKernel = NULL;
3211 char * removeArgs = NULL;
3212 char * kernelInfo = NULL;
3213 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
3214 const char * chptr = NULL;
3215 struct configFileInfo * cfi = NULL;
3216 struct grubConfig * config;
3217 struct singleEntry * template = NULL;
3218 int copyDefault = 0, makeDefault = 0;
3219 int displayDefault = 0;
3220 int displayDefaultIndex = 0;
3221 int displayDefaultTitle = 0;
3222 struct poptOption options[] = {
3223 { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
3224 _("add an entry for the specified kernel"), _("kernel-path") },
3225 { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
3226 _("add an entry for the specified multiboot kernel"), NULL },
3227 { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
3228 _("default arguments for the new kernel or new arguments for "
3229 "kernel being updated"), _("args") },
3230 { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
3231 _("default arguments for the new multiboot kernel or "
3232 "new arguments for multiboot kernel being updated"), NULL },
3233 { "bad-image-okay", 0, 0, &badImageOkay, 0,
3234 _("don't sanity check images in boot entries (for testing only)"),
3235 NULL },
3236 { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
3237 _("filestystem which contains /boot directory (for testing only)"),
3238 _("bootfs") },
3239 #if defined(__i386__) || defined(__x86_64__)
3240 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
3241 _("check if lilo is installed on lilo.conf boot sector") },
3242 #endif
3243 { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
3244 _("path to grub config file to update (\"-\" for stdin)"),
3245 _("path") },
3246 { "copy-default", 0, 0, &copyDefault, 0,
3247 _("use the default boot entry as a template for the new entry "
3248 "being added; if the default is not a linux image, or if "
3249 "the kernel referenced by the default image does not exist, "
3250 "the first linux entry whose kernel does exist is used as the "
3251 "template"), NULL },
3252 { "debug", 0, 0, &debug, 0,
3253 _("print debugging information for failures") },
3254 { "default-kernel", 0, 0, &displayDefault, 0,
3255 _("display the path of the default kernel") },
3256 { "default-index", 0, 0, &displayDefaultIndex, 0,
3257 _("display the index of the default kernel") },
3258 { "default-title", 0, 0, &displayDefaultTitle, 0,
3259 _("display the title of the default kernel") },
3260 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
3261 _("configure elilo bootloader") },
3262 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
3263 _("configure extlinux bootloader (from syslinux)") },
3264 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
3265 _("configure grub bootloader") },
3266 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
3267 _("configure grub2 bootloader") },
3268 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
3269 _("display boot information for specified kernel"),
3270 _("kernel-path") },
3271 { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
3272 _("initrd image for the new kernel"), _("initrd-path") },
3273 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
3274 _("auxilliary initrd image for things other than the new kernel"), _("initrd-path") },
3275 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
3276 _("configure lilo bootloader") },
3277 { "make-default", 0, 0, &makeDefault, 0,
3278 _("make the newly added entry the default boot entry"), NULL },
3279 { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
3280 _("path to output updated config file (\"-\" for stdout)"),
3281 _("path") },
3282 { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
3283 _("remove kernel arguments"), NULL },
3284 { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
3285 _("remove multiboot kernel arguments"), NULL },
3286 { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
3287 _("remove all entries for the specified kernel"),
3288 _("kernel-path") },
3289 { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
3290 _("remove all entries for the specified multiboot kernel"), NULL },
3291 { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
3292 _("make the first entry referencing the specified kernel "
3293 "the default"), _("kernel-path") },
3294 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
3295 _("configure silo bootloader") },
3296 { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
3297 _("title to use for the new kernel entry"), _("entry-title") },
3298 { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
3299 _("updated information for the specified kernel"),
3300 _("kernel-path") },
3301 { "version", 'v', 0, NULL, 'v',
3302 _("print the version of this program and exit"), NULL },
3303 { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
3304 _("configure yaboot bootloader") },
3305 { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
3306 _("configure zipl bootloader") },
3307 POPT_AUTOHELP
3308 { 0, 0, 0, 0, 0 }
3309 };
3310
3311 useextlinuxmenu=0;
3312
3313 signal(SIGSEGV, traceback);
3314
3315 optCon = poptGetContext("grubby", argc, argv, options, 0);
3316 poptReadDefaultConfig(optCon, 1);
3317
3318 while ((arg = poptGetNextOpt(optCon)) >= 0) {
3319 switch (arg) {
3320 case 'v':
3321 printf("grubby version %s\n", VERSION);
3322 exit(0);
3323 break;
3324 case 'i':
3325 if (extraInitrdCount < MAX_EXTRA_INITRDS) {
3326 extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
3327 } else {
3328 fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
3329 return 1;
3330 }
3331 break;
3332 }
3333 }
3334
3335 if (arg < -1) {
3336 fprintf(stderr, _("grubby: bad argument %s: %s\n"),
3337 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
3338 poptStrerror(arg));
3339 return 1;
3340 }
3341
3342 if ((chptr = poptGetArg(optCon))) {
3343 fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
3344 return 1;
3345 }
3346
3347 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
3348 configureYaboot + configureSilo + configureZipl +
3349 configureExtLinux ) > 1) {
3350 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
3351 return 1;
3352 } else if (bootloaderProbe && grubConfig) {
3353 fprintf(stderr,
3354 _("grubby: cannot specify config file with --bootloader-probe\n"));
3355 return 1;
3356 } else if (configureGrub2) {
3357 cfi = &grub2ConfigType;
3358 } else if (configureLilo) {
3359 cfi = &liloConfigType;
3360 } else if (configureGrub) {
3361 cfi = &grubConfigType;
3362 } else if (configureELilo) {
3363 cfi = &eliloConfigType;
3364 } else if (configureYaboot) {
3365 cfi = &yabootConfigType;
3366 } else if (configureSilo) {
3367 cfi = &siloConfigType;
3368 } else if (configureZipl) {
3369 cfi = &ziplConfigType;
3370 } else if (configureExtLinux) {
3371 cfi = &extlinuxConfigType;
3372 useextlinuxmenu=1;
3373 }
3374
3375 if (!cfi) {
3376 #ifdef __ia64__
3377 cfi = &eliloConfigType;
3378 #elif __powerpc__
3379 cfi = &yabootConfigType;
3380 #elif __sparc__
3381 cfi = &siloConfigType;
3382 #elif __s390__
3383 cfi = &ziplConfigType;
3384 #elif __s390x__
3385 cfi = &ziplConfigtype;
3386 #else
3387 if (grub2FindConfig(&grub2ConfigType))
3388 cfi = &grub2ConfigType;
3389 else
3390 cfi = &grubConfigType;
3391 #endif
3392 }
3393
3394 if (!grubConfig) {
3395 if (cfi->findConfig)
3396 grubConfig = cfi->findConfig(cfi);
3397 if (!grubConfig)
3398 grubConfig = cfi->defaultConfig;
3399 }
3400
3401 if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
3402 newKernelPath || removeKernelPath || makeDefault ||
3403 defaultKernel || displayDefaultIndex || displayDefaultTitle)) {
3404 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
3405 "specified option"));
3406 return 1;
3407 }
3408
3409 if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
3410 removeKernelPath)) {
3411 fprintf(stderr, _("grubby: --default-kernel and --info may not "
3412 "be used when adding or removing kernels\n"));
3413 return 1;
3414 }
3415
3416 if (newKernelPath && !newKernelTitle) {
3417 fprintf(stderr, _("grubby: kernel title must be specified\n"));
3418 return 1;
3419 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
3420 (newKernelInitrd && !updateKernelPath)||
3421 makeDefault || extraInitrdCount > 0)) {
3422 fprintf(stderr, _("grubby: kernel path expected\n"));
3423 return 1;
3424 }
3425
3426 if (newKernelPath && updateKernelPath) {
3427 fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
3428 "not be used together"));
3429 return 1;
3430 }
3431
3432 if (makeDefault && defaultKernel) {
3433 fprintf(stderr, _("grubby: --make-default and --default-kernel "
3434 "may not be used together\n"));
3435 return 1;
3436 } else if (defaultKernel && removeKernelPath &&
3437 !strcmp(defaultKernel, removeKernelPath)) {
3438 fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
3439 return 1;
3440 } else if (defaultKernel && newKernelPath &&
3441 !strcmp(defaultKernel, newKernelPath)) {
3442 makeDefault = 1;
3443 defaultKernel = NULL;
3444 }
3445
3446 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
3447 fprintf(stderr, _("grubby: output file must be specified if stdin "
3448 "is used\n"));
3449 return 1;
3450 }
3451
3452 if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
3453 && !kernelInfo && !bootloaderProbe && !updateKernelPath
3454 && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle) {
3455 fprintf(stderr, _("grubby: no action specified\n"));
3456 return 1;
3457 }
3458
3459 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
3460
3461 if (cfi->needsBootPrefix) {
3462 if (!bootPrefix) {
3463 bootPrefix = findBootPrefix();
3464 if (!bootPrefix) return 1;
3465 } else {
3466 /* this shouldn't end with a / */
3467 if (bootPrefix[strlen(bootPrefix) - 1] == '/')
3468 bootPrefix[strlen(bootPrefix) - 1] = '\0';
3469 }
3470 } else {
3471 bootPrefix = "";
3472 }
3473
3474 if (!cfi->mbAllowExtraInitRds &&
3475 extraInitrdCount > 0) {
3476 fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
3477 return 1;
3478 }
3479
3480 if (bootloaderProbe) {
3481 int lrc = 0, grc = 0, gr2c = 0, erc = 0;
3482 struct grubConfig * lconfig, * gconfig;
3483
3484 const char *grub2config = grub2FindConfig(&grub2ConfigType);
3485 if (grub2config) {
3486 gconfig = readConfig(grub2config, &grub2ConfigType);
3487 if (!gconfig)
3488 gr2c = 1;
3489 else
3490 gr2c = checkForGrub2(gconfig);
3491 }
3492
3493 const char *grubconfig = grubFindConfig(&grubConfigType);
3494 if (!access(grubconfig, F_OK)) {
3495 gconfig = readConfig(grubconfig, &grubConfigType);
3496 if (!gconfig)
3497 grc = 1;
3498 else
3499 grc = checkForGrub(gconfig);
3500 }
3501
3502 if (!access(liloConfigType.defaultConfig, F_OK)) {
3503 lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
3504 if (!lconfig)
3505 lrc = 1;
3506 else
3507 lrc = checkForLilo(lconfig);
3508 }
3509
3510 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
3511 lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
3512 if (!lconfig)
3513 erc = 1;
3514 else
3515 erc = checkForExtLinux(lconfig);
3516 }
3517
3518 if (lrc == 1 || grc == 1 || gr2c == 1) return 1;
3519
3520 if (lrc == 2) printf("lilo\n");
3521 if (gr2c == 2) printf("grub2\n");
3522 if (grc == 2) printf("grub\n");
3523 if (erc == 2) printf("extlinux\n");
3524
3525 return 0;
3526 }
3527
3528 config = readConfig(grubConfig, cfi);
3529 if (!config) return 1;
3530
3531 if (displayDefault) {
3532 struct singleLine * line;
3533 struct singleEntry * entry;
3534 char * rootspec;
3535
3536 if (config->defaultImage == -1) return 0;
3537 entry = findEntryByIndex(config, config->defaultImage);
3538 if (!entry) return 0;
3539 if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
3540
3541 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
3542 if (!line) return 0;
3543
3544 rootspec = getRootSpecifier(line->elements[1].item);
3545 printf("%s%s\n", bootPrefix, line->elements[1].item +
3546 ((rootspec != NULL) ? strlen(rootspec) : 0));
3547
3548 return 0;
3549
3550 } else if (displayDefaultTitle) {
3551 struct singleLine * line;
3552 struct singleEntry * entry;
3553
3554 if (config->defaultImage == -1) return 0;
3555 entry = findEntryByIndex(config, config->defaultImage);
3556 if (!entry) return 0;
3557
3558 if (!configureGrub2) {
3559 line = getLineByType(LT_TITLE, entry->lines);
3560 if (!line) return 0;
3561 printf("%s\n", line->elements[1].item);
3562
3563 } else {
3564 int i;
3565 size_t len;
3566 char * start;
3567 char * tmp;
3568
3569 dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
3570 line = getLineByType(LT_MENUENTRY, entry->lines);
3571 if (!line) return 0;
3572
3573 for (i = 0; i < line->numElements; i++) {
3574
3575 if (!strcmp(line->elements[i].item, "menuentry"))
3576 continue;
3577
3578 if (*line->elements[i].item == '\'')
3579 start = line->elements[i].item + 1;
3580 else
3581 start = line->elements[i].item;
3582
3583 len = strlen(start);
3584 if (*(start + len - 1) == '\'') {
3585 tmp = strdup(start);
3586 *(tmp + len - 1) = '\0';
3587 printf("%s", tmp);
3588 free(tmp);
3589 break;
3590 } else {
3591 printf("%s ", start);
3592 }
3593 }
3594 printf("\n");
3595 }
3596 return 0;
3597
3598 } else if (displayDefaultIndex) {
3599 if (config->defaultImage == -1) return 0;
3600 printf("%i\n", config->defaultImage);
3601
3602 } else if (kernelInfo)
3603 return displayInfo(config, kernelInfo, bootPrefix);
3604
3605 if (copyDefault) {
3606 template = findTemplate(config, bootPrefix, NULL, 0, flags);
3607 if (!template) return 1;
3608 }
3609
3610 markRemovedImage(config, removeKernelPath, bootPrefix);
3611 markRemovedImage(config, removeMBKernel, bootPrefix);
3612 setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
3613 bootPrefix, flags);
3614 setFallbackImage(config, newKernelPath != NULL);
3615 if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
3616 removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
3617 if (updateKernelPath && newKernelInitrd) {
3618 if (updateInitrd(config, updateKernelPath, bootPrefix,
3619 newKernelInitrd)) return 1;
3620 }
3621 if (addNewKernel(config, template, bootPrefix, newKernelPath,
3622 newKernelTitle, newKernelArgs, newKernelInitrd,
3623 extraInitrds, extraInitrdCount,
3624 newMBKernel, newMBKernelArgs)) return 1;
3625
3626
3627 if (numEntries(config) == 0) {
3628 fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
3629 "Not writing out new config.\n"));
3630 return 1;
3631 }
3632
3633 if (!outputFile)
3634 outputFile = (char *)grubConfig;
3635
3636 return writeConfig(config, outputFile, bootPrefix);
3637 }