Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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