Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1840 - (show annotations) (download)
Mon Jul 2 12:48:49 2012 UTC (11 years, 9 months ago) by niro
File MIME type: text/plain
File size: 107827 byte(s)
Preserve "trailing indentation" when splitting line elements (#742720)
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 line->elements[i + 1].indent = line->elements[i].indent;
905 line->elements[i].indent = strdup(indent);
906 *p++ = '\0';
907 i++;
908 line->elements[i].item = strdup(p);
909 }
910 }
911 }
912 }
913 }
914
915 return 0;
916 }
917
918 static struct grubConfig * readConfig(const char * inName,
919 struct configFileInfo * cfi) {
920 int in;
921 char * incoming = NULL, * head;
922 int rc;
923 int sawEntry = 0;
924 int movedLine = 0;
925 struct grubConfig * cfg;
926 struct singleLine * last = NULL, * line, * defaultLine = NULL;
927 char * end;
928 struct singleEntry * entry = NULL;
929 int i, len;
930 char * buf;
931
932 if (!strcmp(inName, "-")) {
933 in = 0;
934 } else {
935 if ((in = open(inName, O_RDONLY)) < 0) {
936 fprintf(stderr, _("error opening %s for read: %s\n"),
937 inName, strerror(errno));
938 return NULL;
939 }
940 }
941
942 rc = readFile(in, &incoming);
943 close(in);
944 if (rc) return NULL;
945
946 head = incoming;
947 cfg = malloc(sizeof(*cfg));
948 cfg->primaryIndent = strdup("");
949 cfg->secondaryIndent = strdup("\t");
950 cfg->flags = GRUB_CONFIG_NO_DEFAULT;
951 cfg->cfi = cfi;
952 cfg->theLines = NULL;
953 cfg->entries = NULL;
954 cfg->fallbackImage = 0;
955
956 /* copy everything we have */
957 while (*head) {
958 line = malloc(sizeof(*line));
959 lineInit(line);
960
961 if (getNextLine(&head, line, cfi)) {
962 free(line);
963 /* XXX memory leak of everything in cfg */
964 return NULL;
965 }
966
967 if (!sawEntry && line->numElements) {
968 free(cfg->primaryIndent);
969 cfg->primaryIndent = strdup(line->indent);
970 } else if (line->numElements) {
971 free(cfg->secondaryIndent);
972 cfg->secondaryIndent = strdup(line->indent);
973 }
974
975 if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
976 sawEntry = 1;
977 if (!entry) {
978 cfg->entries = malloc(sizeof(*entry));
979 entry = cfg->entries;
980 } else {
981 entry->next = malloc(sizeof(*entry));
982 entry = entry->next;
983 }
984
985 entry->skip = 0;
986 entry->multiboot = 0;
987 entry->lines = NULL;
988 entry->next = NULL;
989 }
990
991 if (line->type == LT_SET_VARIABLE) {
992 int i;
993 dbgPrintf("found 'set' command (%d elements): ", line->numElements);
994 dbgPrintf("%s", line->indent);
995 for (i = 0; i < line->numElements; i++)
996 dbgPrintf("\"%s\"%s", line->elements[i].item, line->elements[i].indent);
997 dbgPrintf("\n");
998 struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi);
999 if (kwType && line->numElements == 3 &&
1000 !strcmp(line->elements[1].item, kwType->key)) {
1001 dbgPrintf("Line sets default config\n");
1002 cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1003 defaultLine = line;
1004 }
1005 } else if (line->type == LT_DEFAULT && line->numElements == 2) {
1006 cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1007 defaultLine = line;
1008
1009 } else if (line->type == LT_KERNEL) {
1010 /* if by some freak chance this is multiboot and the "module"
1011 * lines came earlier in the template, make sure to use LT_HYPER
1012 * instead of LT_KERNEL now
1013 */
1014 if (entry->multiboot)
1015 line->type = LT_HYPER;
1016
1017 } else if (line->type == LT_MBMODULE) {
1018 /* go back and fix the LT_KERNEL line to indicate LT_HYPER
1019 * instead, now that we know this is a multiboot entry.
1020 * This only applies to grub, but that's the only place we
1021 * should find LT_MBMODULE lines anyway.
1022 */
1023 struct singleLine * l;
1024 for (l = entry->lines; l; l = l->next) {
1025 if (l->type == LT_HYPER)
1026 break;
1027 else if (l->type == LT_KERNEL) {
1028 l->type = LT_HYPER;
1029 break;
1030 }
1031 }
1032 entry->multiboot = 1;
1033
1034 } else if (line->type == LT_HYPER) {
1035 entry->multiboot = 1;
1036
1037 } else if (line->type == LT_FALLBACK && line->numElements == 2) {
1038 cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);
1039 if (*end) cfg->fallbackImage = -1;
1040
1041 } else if (line->type == LT_TITLE && line->numElements > 1) {
1042 /* make the title a single argument (undoing our parsing) */
1043 len = 0;
1044 for (i = 1; i < line->numElements; i++) {
1045 len += strlen(line->elements[i].item);
1046 len += strlen(line->elements[i].indent);
1047 }
1048 buf = malloc(len + 1);
1049 *buf = '\0';
1050
1051 for (i = 1; i < line->numElements; i++) {
1052 strcat(buf, line->elements[i].item);
1053 free(line->elements[i].item);
1054
1055 if ((i + 1) != line->numElements) {
1056 strcat(buf, line->elements[i].indent);
1057 free(line->elements[i].indent);
1058 }
1059 }
1060
1061 line->elements[1].indent =
1062 line->elements[line->numElements - 1].indent;
1063 line->elements[1].item = buf;
1064 line->numElements = 2;
1065 } else if (line->type == LT_MENUENTRY && line->numElements > 3) {
1066 /* let --remove-kernel="TITLE=what" work */
1067 len = 0;
1068 char *extras;
1069 char *title;
1070
1071 for (i = 1; i < line->numElements; i++) {
1072 len += strlen(line->elements[i].item);
1073 len += strlen(line->elements[i].indent);
1074 }
1075 buf = malloc(len + 1);
1076 *buf = '\0';
1077
1078 /* allocate mem for extra flags. */
1079 extras = malloc(len + 1);
1080 *extras = '\0';
1081
1082 /* get title. */
1083 for (i = 0; i < line->numElements; i++) {
1084 if (!strcmp(line->elements[i].item, "menuentry"))
1085 continue;
1086 if (isquote(*line->elements[i].item))
1087 title = line->elements[i].item + 1;
1088 else
1089 title = line->elements[i].item;
1090
1091 len = strlen(title);
1092 if (isquote(title[len-1])) {
1093 strncat(buf, title,len-1);
1094 break;
1095 } else {
1096 strcat(buf, title);
1097 strcat(buf, line->elements[i].indent);
1098 }
1099 }
1100
1101 /* get extras */
1102 int count = 0;
1103 for (i = 0; i < line->numElements; i++) {
1104 if (count >= 2) {
1105 strcat(extras, line->elements[i].item);
1106 strcat(extras, line->elements[i].indent);
1107 }
1108
1109 if (!strcmp(line->elements[i].item, "menuentry"))
1110 continue;
1111
1112 /* count ' or ", there should be two in menuentry line. */
1113 if (isquote(*line->elements[i].item))
1114 count++;
1115
1116 len = strlen(line->elements[i].item);
1117
1118 if (isquote(line->elements[i].item[len -1]))
1119 count++;
1120
1121 /* ok, we get the final ' or ", others are extras. */
1122 }
1123 line->elements[1].indent =
1124 line->elements[line->numElements - 2].indent;
1125 line->elements[1].item = buf;
1126 line->elements[2].indent =
1127 line->elements[line->numElements - 2].indent;
1128 line->elements[2].item = extras;
1129 line->numElements = 3;
1130 } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {
1131 /* Strip off any " which may be present; they'll be put back
1132 on write. This is one of the few (the only?) places that grubby
1133 canonicalizes the output */
1134
1135 if (line->numElements >= 2) {
1136 int last, len;
1137
1138 if (isquote(*line->elements[1].item))
1139 memmove(line->elements[1].item, line->elements[1].item + 1,
1140 strlen(line->elements[1].item + 1) + 1);
1141
1142 last = line->numElements - 1;
1143 len = strlen(line->elements[last].item) - 1;
1144 if (isquote(line->elements[last].item[len]))
1145 line->elements[last].item[len] = '\0';
1146 }
1147 }
1148
1149 /* If we find a generic config option which should live at the
1150 top of the file, move it there. Old versions of grubby were
1151 probably responsible for putting new images in the wrong
1152 place in front of it anyway. */
1153 if (sawEntry && line->type == LT_GENERIC) {
1154 struct singleLine **l = &cfg->theLines;
1155 struct singleLine **last_nonws = &cfg->theLines;
1156 while (*l) {
1157 if ((*l)->type != LT_WHITESPACE)
1158 last_nonws = &((*l)->next);
1159 l = &((*l)->next);
1160 }
1161 line->next = *last_nonws;
1162 *last_nonws = line;
1163 movedLine = 1;
1164 continue; /* without setting 'last' */
1165 }
1166
1167 /* If a second line of whitespace happens after a generic option
1168 which was moved, drop it. */
1169 if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {
1170 lineFree(line);
1171 free(line);
1172 movedLine = 0;
1173 continue;
1174 }
1175 movedLine = 0;
1176
1177 if (sawEntry) {
1178 if (!entry->lines)
1179 entry->lines = line;
1180 else
1181 last->next = line;
1182 dbgPrintf("readConfig added %s to %p\n", getKeyByType(line->type, cfi), entry);
1183
1184 /* we could have seen this outside of an entry... if so, we
1185 * ignore it like any other line we don't grok */
1186 if (line->type == LT_ENTRY_END && sawEntry)
1187 sawEntry = 0;
1188 } else {
1189 if (!cfg->theLines)
1190 cfg->theLines = line;
1191 else
1192 last->next = line;
1193 dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi));
1194 }
1195
1196 last = line;
1197 }
1198
1199 free(incoming);
1200
1201 dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");
1202 if (defaultLine) {
1203 if (defaultLine->numElements > 2 &&
1204 cfi->defaultSupportSaved &&
1205 !strncmp(defaultLine->elements[2].item,"\"${saved_entry}\"", 16)) {
1206 cfg->defaultImage = DEFAULT_SAVED_GRUB2;
1207 } else if (cfi->defaultIsVariable) {
1208 char *value = defaultLine->elements[2].item;
1209 while (*value && (*value == '"' || *value == '\'' ||
1210 *value == ' ' || *value == '\t'))
1211 value++;
1212 cfg->defaultImage = strtol(value, &end, 10);
1213 while (*end && (*end == '"' || *end == '\'' ||
1214 *end == ' ' || *end == '\t'))
1215 end++;
1216 if (*end) cfg->defaultImage = -1;
1217 } else if (cfi->defaultSupportSaved &&
1218 !strncmp(defaultLine->elements[1].item, "saved", 5)) {
1219 cfg->defaultImage = DEFAULT_SAVED;
1220 } else if (cfi->defaultIsIndex) {
1221 cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);
1222 if (*end) cfg->defaultImage = -1;
1223 } else if (defaultLine->numElements >= 2) {
1224 i = 0;
1225 while ((entry = findEntryByIndex(cfg, i))) {
1226 for (line = entry->lines; line; line = line->next)
1227 if (line->type == LT_TITLE) break;
1228
1229 if (!cfi->titleBracketed) {
1230 if (line && (line->numElements >= 2) &&
1231 !strcmp(defaultLine->elements[1].item,
1232 line->elements[1].item)) break;
1233 } else if (line) {
1234 if (!strcmp(defaultLine->elements[1].item,
1235 extractTitle(line))) break;
1236 }
1237 i++;
1238 entry = NULL;
1239 }
1240
1241 if (entry){
1242 cfg->defaultImage = i;
1243 }else{
1244 cfg->defaultImage = -1;
1245 }
1246 }
1247 } else {
1248 cfg->defaultImage = 0;
1249 }
1250
1251 return cfg;
1252 }
1253
1254 static void writeDefault(FILE * out, char * indent,
1255 char * separator, struct grubConfig * cfg) {
1256 struct singleEntry * entry;
1257 struct singleLine * line;
1258 int i;
1259
1260 if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT) return;
1261
1262 if (cfg->defaultImage == DEFAULT_SAVED)
1263 fprintf(out, "%sdefault%ssaved\n", indent, separator);
1264 else if (cfg->defaultImage == DEFAULT_SAVED_GRUB2)
1265 fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1266 else if (cfg->defaultImage > -1) {
1267 if (cfg->cfi->defaultIsIndex) {
1268 if (cfg->cfi->defaultIsVariable) {
1269 fprintf(out, "%sset default=\"%d\"\n", indent,
1270 cfg->defaultImage);
1271 } else {
1272 fprintf(out, "%sdefault%s%d\n", indent, separator,
1273 cfg->defaultImage);
1274 }
1275 } else {
1276 int image = cfg->defaultImage;
1277
1278 entry = cfg->entries;
1279 while (entry && entry->skip) entry = entry->next;
1280
1281 i = 0;
1282 while (entry && i < image) {
1283 entry = entry->next;
1284
1285 while (entry && entry->skip) entry = entry->next;
1286 i++;
1287 }
1288
1289 if (!entry) return;
1290
1291 line = getLineByType(LT_TITLE, entry->lines);
1292
1293 if (line && line->numElements >= 2)
1294 fprintf(out, "%sdefault%s%s\n", indent, separator,
1295 line->elements[1].item);
1296 else if (line && (line->numElements == 1) &&
1297 cfg->cfi->titleBracketed) {
1298 fprintf(out, "%sdefault%s%s\n", indent, separator,
1299 extractTitle(line));
1300 }
1301 }
1302 }
1303 }
1304
1305 static int writeConfig(struct grubConfig * cfg, char * outName,
1306 const char * prefix) {
1307 FILE * out;
1308 struct singleLine * line;
1309 struct singleEntry * entry;
1310 char * tmpOutName;
1311 int needs = MAIN_DEFAULT;
1312 struct stat sb;
1313 int i;
1314
1315 if (!strcmp(outName, "-")) {
1316 out = stdout;
1317 tmpOutName = NULL;
1318 } else {
1319 if (!lstat(outName, &sb) && S_ISLNK(sb.st_mode)) {
1320 char * buf;
1321 int len = 256;
1322 int rc;
1323
1324 /* most likely the symlink is relative, so change our
1325 directory to the dir of the symlink */
1326 rc = chdir(dirname(strdupa(outName)));
1327 do {
1328 buf = alloca(len + 1);
1329 rc = readlink(basename(outName), buf, len);
1330 if (rc == len) len += 256;
1331 } while (rc == len);
1332
1333 if (rc < 0) {
1334 fprintf(stderr, _("grubby: error readlink link %s: %s\n"),
1335 outName, strerror(errno));
1336 return 1;
1337 }
1338
1339 outName = buf;
1340 outName[rc] = '\0';
1341 }
1342
1343 tmpOutName = alloca(strlen(outName) + 2);
1344 sprintf(tmpOutName, "%s-", outName);
1345 out = fopen(tmpOutName, "w");
1346 if (!out) {
1347 fprintf(stderr, _("grubby: error creating %s: %s\n"), tmpOutName,
1348 strerror(errno));
1349 return 1;
1350 }
1351
1352 if (!stat(outName, &sb)) {
1353 if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1354 fprintf(stderr, _("grubby: error setting perms on %s: %s\n"),
1355 tmpOutName, strerror(errno));
1356 fclose(out);
1357 unlink(tmpOutName);
1358 return 1;
1359 }
1360 }
1361 }
1362
1363 line = cfg->theLines;
1364 struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1365 while (line) {
1366 if (line->type == LT_SET_VARIABLE && defaultKw &&
1367 line->numElements == 3 &&
1368 !strcmp(line->elements[1].item, defaultKw->key)) {
1369 writeDefault(out, line->indent, line->elements[0].indent, cfg);
1370 needs &= ~MAIN_DEFAULT;
1371 } else if (line->type == LT_DEFAULT) {
1372 writeDefault(out, line->indent, line->elements[0].indent, cfg);
1373 needs &= ~MAIN_DEFAULT;
1374 } else if (line->type == LT_FALLBACK) {
1375 if (cfg->fallbackImage > -1)
1376 fprintf(out, "%s%s%s%d\n", line->indent,
1377 line->elements[0].item, line->elements[0].indent,
1378 cfg->fallbackImage);
1379 } else {
1380 if (lineWrite(out, line, cfg->cfi) == -1) {
1381 fprintf(stderr, _("grubby: error writing %s: %s\n"),
1382 tmpOutName, strerror(errno));
1383 fclose(out);
1384 unlink(tmpOutName);
1385 return 1;
1386 }
1387 }
1388
1389 line = line->next;
1390 }
1391
1392 if (needs & MAIN_DEFAULT) {
1393 writeDefault(out, cfg->primaryIndent, "=", cfg);
1394 needs &= ~MAIN_DEFAULT;
1395 }
1396
1397 i = 0;
1398 while ((entry = findEntryByIndex(cfg, i++))) {
1399 if (entry->skip) continue;
1400
1401 line = entry->lines;
1402 while (line) {
1403 if (lineWrite(out, line, cfg->cfi) == -1) {
1404 fprintf(stderr, _("grubby: error writing %s: %s\n"),
1405 tmpOutName, strerror(errno));
1406 fclose(out);
1407 unlink(tmpOutName);
1408 return 1;
1409 }
1410 line = line->next;
1411 }
1412 }
1413
1414 if (tmpOutName) {
1415 if (rename(tmpOutName, outName)) {
1416 fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),
1417 tmpOutName, outName, strerror(errno));
1418 unlink(outName);
1419 return 1;
1420 }
1421 }
1422
1423 return 0;
1424 }
1425
1426 static int numEntries(struct grubConfig *cfg) {
1427 int i = 0;
1428 struct singleEntry * entry;
1429
1430 entry = cfg->entries;
1431 while (entry) {
1432 if (!entry->skip)
1433 i++;
1434 entry = entry->next;
1435 }
1436 return i;
1437 }
1438
1439 static char *findDiskForRoot()
1440 {
1441 int fd;
1442 char buf[65536];
1443 char *devname;
1444 char *chptr;
1445 int rc;
1446
1447 if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1448 fprintf(stderr, "grubby: failed to open %s: %s\n",
1449 _PATH_MOUNTED, strerror(errno));
1450 return NULL;
1451 }
1452
1453 rc = read(fd, buf, sizeof(buf) - 1);
1454 if (rc <= 0) {
1455 fprintf(stderr, "grubby: failed to read %s: %s\n",
1456 _PATH_MOUNTED, strerror(errno));
1457 close(fd);
1458 return NULL;
1459 }
1460 close(fd);
1461 buf[rc] = '\0';
1462 chptr = buf;
1463
1464 while (chptr && chptr != buf+rc) {
1465 devname = chptr;
1466
1467 /*
1468 * The first column of a mtab entry is the device, but if the entry is a
1469 * special device it won't start with /, so move on to the next line.
1470 */
1471 if (*devname != '/') {
1472 chptr = strchr(chptr, '\n');
1473 if (chptr)
1474 chptr++;
1475 continue;
1476 }
1477
1478 /* Seek to the next space */
1479 chptr = strchr(chptr, ' ');
1480 if (!chptr) {
1481 fprintf(stderr, "grubby: error parsing %s: %s\n",
1482 _PATH_MOUNTED, strerror(errno));
1483 return NULL;
1484 }
1485
1486 /*
1487 * The second column of a mtab entry is the mount point, we are looking
1488 * for '/' obviously.
1489 */
1490 if (*(++chptr) == '/' && *(++chptr) == ' ') {
1491 /*
1492 * Move back 2, which is the first space after the device name, set
1493 * it to \0 so strdup will just get the devicename.
1494 */
1495 chptr -= 2;
1496 *chptr = '\0';
1497 return strdup(devname);
1498 }
1499
1500 /* Next line */
1501 chptr = strchr(chptr, '\n');
1502 if (chptr)
1503 chptr++;
1504 }
1505
1506 return NULL;
1507 }
1508
1509 void printEntry(struct singleEntry * entry) {
1510 int i;
1511 struct singleLine * line;
1512
1513 for (line = entry->lines; line; line = line->next) {
1514 fprintf(stderr, "DBG: %s", line->indent);
1515 for (i = 0; i < line->numElements; i++) {
1516 /* Need to handle this, because we strip the quotes from
1517 * menuentry when read it. */
1518 if (line->type == LT_MENUENTRY && i == 1) {
1519 if(!isquote(*line->elements[i].item))
1520 fprintf(stderr, "\'%s\'", line->elements[i].item);
1521 else
1522 fprintf(stderr, "%s", line->elements[i].item);
1523 fprintf(stderr, "%s", line->elements[i].indent);
1524
1525 continue;
1526 }
1527
1528 fprintf(stderr, "%s%s",
1529 line->elements[i].item, line->elements[i].indent);
1530 }
1531 fprintf(stderr, "\n");
1532 }
1533 }
1534
1535 void notSuitablePrintf(struct singleEntry * entry, const char *fmt, ...)
1536 {
1537 va_list argp;
1538
1539 if (!debug)
1540 return;
1541
1542 va_start(argp, fmt);
1543 fprintf(stderr, "DBG: Image entry failed: ");
1544 vfprintf(stderr, fmt, argp);
1545 printEntry(entry);
1546 va_end(argp);
1547 }
1548
1549 #define beginswith(s, c) ((s) && (s)[0] == (c))
1550
1551 static int endswith(const char *s, char c)
1552 {
1553 int slen;
1554
1555 if (!s || !s[0])
1556 return 0;
1557 slen = strlen(s) - 1;
1558
1559 return s[slen] == c;
1560 }
1561
1562 int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1563 int skipRemoved, int flags) {
1564 struct singleLine * line;
1565 char * fullName;
1566 int i;
1567 char * dev;
1568 char * rootspec;
1569 char * rootdev;
1570
1571 if (skipRemoved && entry->skip) {
1572 notSuitablePrintf(entry, "marked to skip\n");
1573 return 0;
1574 }
1575
1576 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1577 if (!line) {
1578 notSuitablePrintf(entry, "no line found\n");
1579 return 0;
1580 }
1581 if (line->numElements < 2) {
1582 notSuitablePrintf(entry, "line has only %d elements\n",
1583 line->numElements);
1584 return 0;
1585 }
1586
1587 if (flags & GRUBBY_BADIMAGE_OKAY) return 1;
1588
1589 fullName = alloca(strlen(bootPrefix) +
1590 strlen(line->elements[1].item) + 1);
1591 rootspec = getRootSpecifier(line->elements[1].item);
1592 int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1593 int hasslash = endswith(bootPrefix, '/') ||
1594 beginswith(line->elements[1].item + rootspec_offset, '/');
1595 sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1596 line->elements[1].item + rootspec_offset);
1597 if (access(fullName, R_OK)) {
1598 notSuitablePrintf(entry, "access to %s failed\n", fullName);
1599 return 0;
1600 }
1601 for (i = 2; i < line->numElements; i++)
1602 if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1603 if (i < line->numElements) {
1604 dev = line->elements[i].item + 5;
1605 } else {
1606 /* look for a lilo style LT_ROOT line */
1607 line = getLineByType(LT_ROOT, entry->lines);
1608
1609 if (line && line->numElements >= 2) {
1610 dev = line->elements[1].item;
1611 } else {
1612 /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1613 * grub+multiboot uses LT_MBMODULE for the args, so check that too.
1614 */
1615 line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
1616
1617 /* failed to find one */
1618 if (!line) {
1619 notSuitablePrintf(entry, "no line found\n");
1620 return 0;
1621 }
1622
1623 for (i = 1; i < line->numElements; i++)
1624 if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1625 if (i < line->numElements)
1626 dev = line->elements[i].item + 5;
1627 else {
1628 notSuitablePrintf(entry, "no root= entry found\n");
1629 /* it failed too... can't find root= */
1630 return 0;
1631 }
1632 }
1633 }
1634
1635 dev = getpathbyspec(dev);
1636 if (!getpathbyspec(dev)) {
1637 notSuitablePrintf(entry, "can't find blkid entry for %s\n", dev);
1638 return 0;
1639 } else
1640 dev = getpathbyspec(dev);
1641
1642 rootdev = findDiskForRoot();
1643 if (!rootdev) {
1644 notSuitablePrintf(entry, "can't find root device\n");
1645 return 0;
1646 }
1647
1648 if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1649 notSuitablePrintf(entry, "uuid missing: rootdev %s, dev %s\n",
1650 getuuidbydev(rootdev), getuuidbydev(dev));
1651 free(rootdev);
1652 return 0;
1653 }
1654
1655 if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1656 notSuitablePrintf(entry, "uuid mismatch: rootdev %s, dev %s\n",
1657 getuuidbydev(rootdev), getuuidbydev(dev));
1658 free(rootdev);
1659 return 0;
1660 }
1661
1662 free(rootdev);
1663
1664 return 1;
1665 }
1666
1667 /* returns the first match on or after the one pointed to by index (if index
1668 is not NULL) which is not marked as skip */
1669 struct singleEntry * findEntryByPath(struct grubConfig * config,
1670 const char * kernel, const char * prefix,
1671 int * index) {
1672 struct singleEntry * entry = NULL;
1673 struct singleLine * line;
1674 int i;
1675 char * chptr;
1676 char * rootspec = NULL;
1677 enum lineType_e checkType = LT_KERNEL;
1678
1679 if (isdigit(*kernel)) {
1680 int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
1681
1682 i = 0;
1683 indexVars[i] = strtol(kernel, &chptr, 10);
1684 while (*chptr == ',') {
1685 i++;
1686 kernel = chptr + 1;
1687 indexVars[i] = strtol(kernel, &chptr, 10);
1688 }
1689
1690 if (*chptr) {
1691 /* can't parse it, bail */
1692 return NULL;
1693 }
1694
1695 indexVars[i + 1] = -1;
1696
1697 i = 0;
1698 if (index) {
1699 while (i < *index) i++;
1700 if (indexVars[i] == -1) return NULL;
1701 }
1702
1703 entry = findEntryByIndex(config, indexVars[i]);
1704 if (!entry) return NULL;
1705
1706 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1707 if (!line) return NULL;
1708
1709 if (index) *index = indexVars[i];
1710 return entry;
1711 }
1712
1713 if (!strcmp(kernel, "DEFAULT")) {
1714 if (index && *index > config->defaultImage) {
1715 entry = NULL;
1716 } else {
1717 entry = findEntryByIndex(config, config->defaultImage);
1718 if (entry && entry->skip)
1719 entry = NULL;
1720 else if (index)
1721 *index = config->defaultImage;
1722 }
1723 } else if (!strcmp(kernel, "ALL")) {
1724 if (index)
1725 i = *index;
1726 else
1727 i = 0;
1728
1729 while ((entry = findEntryByIndex(config, i))) {
1730 if (!entry->skip) break;
1731 i++;
1732 }
1733
1734 if (entry && index)
1735 *index = i;
1736 } else {
1737 if (index)
1738 i = *index;
1739 else
1740 i = 0;
1741
1742 if (!strncmp(kernel, "TITLE=", 6)) {
1743 prefix = "";
1744 checkType = LT_TITLE|LT_MENUENTRY;
1745 kernel += 6;
1746 }
1747
1748 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
1749 if (entry->skip) continue;
1750
1751 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
1752
1753 /* check all the lines matching checkType */
1754 for (line = entry->lines; line; line = line->next) {
1755 line = getLineByType(entry->multiboot && checkType == LT_KERNEL ?
1756 LT_KERNEL|LT_MBMODULE|LT_HYPER :
1757 checkType, line);
1758 if (!line) break; /* not found in this entry */
1759
1760 if (line && line->type != LT_MENUENTRY &&
1761 line->numElements >= 2) {
1762 rootspec = getRootSpecifier(line->elements[1].item);
1763 if (!strcmp(line->elements[1].item +
1764 ((rootspec != NULL) ? strlen(rootspec) : 0),
1765 kernel + strlen(prefix)))
1766 break;
1767 }
1768 if(line->type == LT_MENUENTRY &&
1769 !strcmp(line->elements[1].item, kernel))
1770 break;
1771 }
1772
1773 /* make sure this entry has a kernel identifier; this skips
1774 * non-Linux boot entries (could find netbsd etc, though, which is
1775 * unfortunate)
1776 */
1777 if (line && getLineByType(LT_KERNEL|LT_HYPER, entry->lines))
1778 break; /* found 'im! */
1779 }
1780
1781 if (index) *index = i;
1782 }
1783
1784 return entry;
1785 }
1786
1787 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
1788 struct singleEntry * entry;
1789
1790 entry = cfg->entries;
1791 while (index && entry) {
1792 entry = entry->next;
1793 index--;
1794 }
1795
1796 return entry;
1797 }
1798
1799 /* Find a good template to use for the new kernel. An entry is
1800 * good if the kernel and mkinitrd exist (even if the entry
1801 * is going to be removed). Try and use the default entry, but
1802 * if that doesn't work just take the first. If we can't find one,
1803 * bail. */
1804 struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
1805 int * indexPtr, int skipRemoved, int flags) {
1806 struct singleEntry * entry, * entry2;
1807 int index;
1808
1809 if (cfg->defaultImage > -1) {
1810 entry = findEntryByIndex(cfg, cfg->defaultImage);
1811 if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
1812 if (indexPtr) *indexPtr = cfg->defaultImage;
1813 return entry;
1814 }
1815 }
1816
1817 index = 0;
1818 while ((entry = findEntryByIndex(cfg, index))) {
1819 if (suitableImage(entry, prefix, skipRemoved, flags)) {
1820 int j;
1821 for (j = 0; j < index; j++) {
1822 entry2 = findEntryByIndex(cfg, j);
1823 if (entry2->skip) index--;
1824 }
1825 if (indexPtr) *indexPtr = index;
1826
1827 return entry;
1828 }
1829
1830 index++;
1831 }
1832
1833 fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
1834
1835 return NULL;
1836 }
1837
1838 char * findBootPrefix(void) {
1839 struct stat sb, sb2;
1840
1841 stat("/", &sb);
1842 #ifdef __ia64__
1843 stat("/boot/efi/EFI/redhat/", &sb2);
1844 #else
1845 stat("/boot", &sb2);
1846 #endif
1847
1848 if (sb.st_dev == sb2.st_dev)
1849 return strdup("");
1850
1851 #ifdef __ia64__
1852 return strdup("/boot/efi/EFI/redhat/");
1853 #else
1854 return strdup("/boot");
1855 #endif
1856 }
1857
1858 void markRemovedImage(struct grubConfig * cfg, const char * image,
1859 const char * prefix) {
1860 struct singleEntry * entry;
1861
1862 if (!image)
1863 return;
1864
1865 /* check and see if we're removing the default image */
1866 if (isdigit(*image)) {
1867 entry = findEntryByPath(cfg, image, prefix, NULL);
1868 if(entry)
1869 entry->skip = 1;
1870 return;
1871 }
1872
1873 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
1874 entry->skip = 1;
1875 }
1876
1877 void setDefaultImage(struct grubConfig * config, int hasNew,
1878 const char * defaultKernelPath, int newIsDefault,
1879 const char * prefix, int flags) {
1880 struct singleEntry * entry, * entry2, * newDefault;
1881 int i, j;
1882
1883 if (newIsDefault) {
1884 config->defaultImage = 0;
1885 return;
1886 } else if (defaultKernelPath) {
1887 i = 0;
1888 if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
1889 config->defaultImage = i;
1890 } else {
1891 config->defaultImage = -1;
1892 return;
1893 }
1894 }
1895
1896 /* defaultImage now points to what we'd like to use, but before any order
1897 changes */
1898 if ((config->defaultImage == DEFAULT_SAVED) ||
1899 (config->defaultImage == DEFAULT_SAVED_GRUB2))
1900 /* default is set to saved, we don't want to change it */
1901 return;
1902
1903 if (config->defaultImage > -1)
1904 entry = findEntryByIndex(config, config->defaultImage);
1905 else
1906 entry = NULL;
1907
1908 if (entry && !entry->skip) {
1909 /* we can preserve the default */
1910 if (hasNew)
1911 config->defaultImage++;
1912
1913 /* count the number of entries erased before this one */
1914 for (j = 0; j < config->defaultImage; j++) {
1915 entry2 = findEntryByIndex(config, j);
1916 if (entry2->skip) config->defaultImage--;
1917 }
1918 } else if (hasNew) {
1919 config->defaultImage = 0;
1920 } else {
1921 /* Either we just erased the default (or the default line was bad
1922 * to begin with) and didn't put a new one in. We'll use the first
1923 * valid image. */
1924 newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
1925 flags);
1926 if (!newDefault)
1927 config->defaultImage = -1;
1928 }
1929 }
1930
1931 void setFallbackImage(struct grubConfig * config, int hasNew) {
1932 struct singleEntry * entry, * entry2;
1933 int j;
1934
1935 if (config->fallbackImage == -1) return;
1936
1937 entry = findEntryByIndex(config, config->fallbackImage);
1938 if (!entry || entry->skip) {
1939 config->fallbackImage = -1;
1940 return;
1941 }
1942
1943 if (hasNew)
1944 config->fallbackImage++;
1945
1946 /* count the number of entries erased before this one */
1947 for (j = 0; j < config->fallbackImage; j++) {
1948 entry2 = findEntryByIndex(config, j);
1949 if (entry2->skip) config->fallbackImage--;
1950 }
1951 }
1952
1953 void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
1954 struct singleLine * line;
1955 char * root = NULL;
1956 int i;
1957
1958 printf("index=%d\n", index);
1959
1960 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
1961 if (!line) {
1962 printf("non linux entry\n");
1963 return;
1964 }
1965
1966 printf("kernel=%s%s\n", prefix, line->elements[1].item);
1967
1968 if (line->numElements >= 3) {
1969 printf("args=\"");
1970 i = 2;
1971 while (i < line->numElements) {
1972 if (!strncmp(line->elements[i].item, "root=", 5)) {
1973 root = line->elements[i].item + 5;
1974 } else {
1975 printf("%s%s", line->elements[i].item,
1976 line->elements[i].indent);
1977 }
1978
1979 i++;
1980 }
1981 printf("\"\n");
1982 } else {
1983 line = getLineByType(LT_KERNELARGS, entry->lines);
1984 if (line) {
1985 char * s;
1986
1987 printf("args=\"");
1988 i = 1;
1989 while (i < line->numElements) {
1990 if (!strncmp(line->elements[i].item, "root=", 5)) {
1991 root = line->elements[i].item + 5;
1992 } else {
1993 s = line->elements[i].item;
1994
1995 printf("%s%s", s, line->elements[i].indent);
1996 }
1997
1998 i++;
1999 }
2000
2001 s = line->elements[i - 1].indent;
2002 printf("\"\n");
2003 }
2004 }
2005
2006 if (!root) {
2007 line = getLineByType(LT_ROOT, entry->lines);
2008 if (line && line->numElements >= 2)
2009 root=line->elements[1].item;
2010 }
2011
2012 if (root) {
2013 char * s = alloca(strlen(root) + 1);
2014
2015 strcpy(s, root);
2016 if (s[strlen(s) - 1] == '"')
2017 s[strlen(s) - 1] = '\0';
2018 /* make sure the root doesn't have a trailing " */
2019 printf("root=%s\n", s);
2020 }
2021
2022 line = getLineByType(LT_INITRD, entry->lines);
2023
2024 if (line && line->numElements >= 2) {
2025 printf("initrd=%s", prefix);
2026 for (i = 1; i < line->numElements; i++)
2027 printf("%s%s", line->elements[i].item, line->elements[i].indent);
2028 printf("\n");
2029 }
2030
2031 line = getLineByType(LT_TITLE, entry->lines);
2032 if (line) {
2033 printf("title=%s\n", line->elements[1].item);
2034 } else {
2035 char * title;
2036 line = getLineByType(LT_MENUENTRY, entry->lines);
2037 title = grub2ExtractTitle(line);
2038 if (title)
2039 printf("title=%s\n", title);
2040 }
2041 }
2042
2043 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
2044 FILE * in;
2045 char buf[1024];
2046 char * chptr;
2047 char * start;
2048 char * param;
2049
2050 in = fopen("/etc/conf.d/grub", "r");
2051 if (!in) return 1;
2052
2053 if (lbaPtr) *lbaPtr = 0;
2054 if (bootPtr) *bootPtr = NULL;
2055
2056 while (fgets(buf, sizeof(buf), in)) {
2057 start = buf;
2058 while (isspace(*start)) start++;
2059 if (*start == '#') continue;
2060
2061 chptr = strchr(start, '=');
2062 if (!chptr) continue;
2063 chptr--;
2064 while (*chptr && isspace(*chptr)) chptr--;
2065 chptr++;
2066 *chptr = '\0';
2067
2068 param = chptr + 1;
2069 while (*param && isspace(*param)) param++;
2070 if (*param == '=') {
2071 param++;
2072 while (*param && isspace(*param)) param++;
2073 }
2074
2075 chptr = param;
2076 while (*chptr && !isspace(*chptr)) chptr++;
2077 *chptr = '\0';
2078
2079 if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
2080 *lbaPtr = 1;
2081 else if (!strcmp(start, "boot") && bootPtr)
2082 *bootPtr = strdup(param);
2083 }
2084
2085 fclose(in);
2086
2087 return 0;
2088 }
2089
2090 void dumpSysconfigGrub(void) {
2091 char * boot;
2092 int lba;
2093
2094 if (!parseSysconfigGrub(&lba, &boot)) {
2095 if (lba) printf("lba\n");
2096 if (boot) printf("boot=%s\n", boot);
2097 }
2098 }
2099
2100 int displayInfo(struct grubConfig * config, char * kernel,
2101 const char * prefix) {
2102 int i = 0;
2103 struct singleEntry * entry;
2104 struct singleLine * line;
2105
2106 entry = findEntryByPath(config, kernel, prefix, &i);
2107 if (!entry) {
2108 fprintf(stderr, _("grubby: kernel not found\n"));
2109 return 1;
2110 }
2111
2112 /* this is a horrible hack to support /etc/conf.d/grub; there must
2113 be a better way */
2114 if (config->cfi == &grubConfigType) {
2115 dumpSysconfigGrub();
2116 } else {
2117 line = getLineByType(LT_BOOT, config->theLines);
2118 if (line && line->numElements >= 1) {
2119 printf("boot=%s\n", line->elements[1].item);
2120 }
2121
2122 line = getLineByType(LT_LBA, config->theLines);
2123 if (line) printf("lba\n");
2124 }
2125
2126 displayEntry(entry, prefix, i);
2127
2128 i++;
2129 while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
2130 displayEntry(entry, prefix, i);
2131 i++;
2132 }
2133
2134 return 0;
2135 }
2136
2137 struct singleLine * addLineTmpl(struct singleEntry * entry,
2138 struct singleLine * tmplLine,
2139 struct singleLine * prevLine,
2140 const char * val,
2141 struct configFileInfo * cfi)
2142 {
2143 struct singleLine * newLine = lineDup(tmplLine);
2144
2145 if (val) {
2146 /* override the inherited value with our own.
2147 * This is a little weak because it only applies to elements[1]
2148 */
2149 if (newLine->numElements > 1)
2150 removeElement(newLine, 1);
2151 insertElement(newLine, val, 1, cfi);
2152
2153 /* but try to keep the rootspec from the template... sigh */
2154 if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD)) {
2155 char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2156 if (rootspec != NULL) {
2157 free(newLine->elements[1].item);
2158 newLine->elements[1].item =
2159 sdupprintf("%s%s", rootspec, val);
2160 }
2161 }
2162 }
2163
2164 dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2165 newLine->elements[0].item : "");
2166
2167 if (!entry->lines) {
2168 /* first one on the list */
2169 entry->lines = newLine;
2170 } else if (prevLine) {
2171 /* add after prevLine */
2172 newLine->next = prevLine->next;
2173 prevLine->next = newLine;
2174 }
2175
2176 return newLine;
2177 }
2178
2179 /* val may be NULL */
2180 struct singleLine * addLine(struct singleEntry * entry,
2181 struct configFileInfo * cfi,
2182 enum lineType_e type, char * defaultIndent,
2183 const char * val) {
2184 struct singleLine * line, * prev;
2185 struct keywordTypes * kw;
2186 struct singleLine tmpl;
2187
2188 /* NB: This function shouldn't allocate items on the heap, rather on the
2189 * stack since it calls addLineTmpl which will make copies.
2190 */
2191
2192 if (type == LT_TITLE && cfi->titleBracketed) {
2193 /* we're doing a bracketed title (zipl) */
2194 tmpl.type = type;
2195 tmpl.numElements = 1;
2196 tmpl.elements = alloca(sizeof(*tmpl.elements));
2197 tmpl.elements[0].item = alloca(strlen(val)+3);
2198 sprintf(tmpl.elements[0].item, "[%s]", val);
2199 tmpl.elements[0].indent = "";
2200 val = NULL;
2201 } else if (type == LT_MENUENTRY) {
2202 char *lineend = "--class gnu-linux --class gnu --class os {";
2203 if (!val) {
2204 fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2205 abort();
2206 }
2207 kw = getKeywordByType(type, cfi);
2208 if (!kw) {
2209 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2210 abort();
2211 }
2212 tmpl.indent = "";
2213 tmpl.type = type;
2214 tmpl.numElements = 3;
2215 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2216 tmpl.elements[0].item = kw->key;
2217 tmpl.elements[0].indent = alloca(2);
2218 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2219 tmpl.elements[1].item = (char *)val;
2220 tmpl.elements[1].indent = alloca(2);
2221 sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2222 tmpl.elements[2].item = alloca(strlen(lineend)+1);
2223 strcpy(tmpl.elements[2].item, lineend);
2224 tmpl.elements[2].indent = "";
2225 } else {
2226 kw = getKeywordByType(type, cfi);
2227 if (!kw) {
2228 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2229 abort();
2230 }
2231 tmpl.type = type;
2232 tmpl.numElements = val ? 2 : 1;
2233 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2234 tmpl.elements[0].item = kw->key;
2235 tmpl.elements[0].indent = alloca(2);
2236 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2237 if (val) {
2238 tmpl.elements[1].item = (char *)val;
2239 tmpl.elements[1].indent = "";
2240 }
2241 }
2242
2243 /* The last non-empty line gives us the indention to us and the line
2244 to insert after. Note that comments are considered empty lines, which
2245 may not be ideal? If there are no lines or we are looking at the
2246 first line, we use defaultIndent (the first line is normally indented
2247 differently from the rest) */
2248 for (line = entry->lines, prev = NULL; line; line = line->next) {
2249 if (line->numElements) prev = line;
2250 /* fall back on the last line if prev isn't otherwise set */
2251 if (!line->next && !prev) prev = line;
2252 }
2253
2254 struct singleLine *menuEntry;
2255 menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2256 if (tmpl.type == LT_ENTRY_END) {
2257 if (menuEntry)
2258 tmpl.indent = menuEntry->indent;
2259 else
2260 tmpl.indent = defaultIndent ?: "";
2261 } else if (tmpl.type != LT_MENUENTRY) {
2262 if (menuEntry)
2263 tmpl.indent = "\t";
2264 else if (prev == entry->lines)
2265 tmpl.indent = defaultIndent ?: "";
2266 else
2267 tmpl.indent = prev->indent;
2268 }
2269
2270 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2271 }
2272
2273 void removeLine(struct singleEntry * entry, struct singleLine * line) {
2274 struct singleLine * prev;
2275 int i;
2276
2277 for (i = 0; i < line->numElements; i++) {
2278 free(line->elements[i].item);
2279 free(line->elements[i].indent);
2280 }
2281 free(line->elements);
2282 free(line->indent);
2283
2284 if (line == entry->lines) {
2285 entry->lines = line->next;
2286 } else {
2287 prev = entry->lines;
2288 while (prev->next != line) prev = prev->next;
2289 prev->next = line->next;
2290 }
2291
2292 free(line);
2293 }
2294
2295 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2296 {
2297 struct singleLine newLine = {
2298 .indent = tmplLine->indent,
2299 .type = tmplLine->type,
2300 .next = tmplLine->next,
2301 };
2302 int firstQuotedItem = -1;
2303 int quoteLen = 0;
2304 int j;
2305 int element = 0;
2306 char *c;
2307
2308 c = malloc(strlen(tmplLine->elements[0].item) + 1);
2309 strcpy(c, tmplLine->elements[0].item);
2310 insertElement(&newLine, c, element++, cfi);
2311 free(c);
2312 c = NULL;
2313
2314 for (j = 1; j < tmplLine->numElements; j++) {
2315 if (firstQuotedItem == -1) {
2316 quoteLen += strlen(tmplLine->elements[j].item);
2317
2318 if (isquote(tmplLine->elements[j].item[0])) {
2319 firstQuotedItem = j;
2320 quoteLen += strlen(tmplLine->elements[j].indent);
2321 } else {
2322 c = malloc(quoteLen + 1);
2323 strcpy(c, tmplLine->elements[j].item);
2324 insertElement(&newLine, c, element++, cfi);
2325 free(c);
2326 quoteLen = 0;
2327 }
2328 } else {
2329 int itemlen = strlen(tmplLine->elements[j].item);
2330 quoteLen += itemlen;
2331 quoteLen += strlen(tmplLine->elements[j].indent);
2332
2333 if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2334 c = malloc(quoteLen + 1);
2335 c[0] = '\0';
2336 for (int i = firstQuotedItem; i < j+1; i++) {
2337 strcat(c, tmplLine->elements[i].item);
2338 strcat(c, tmplLine->elements[i].indent);
2339 }
2340 insertElement(&newLine, c, element++, cfi);
2341 free(c);
2342
2343 firstQuotedItem = -1;
2344 quoteLen = 0;
2345 }
2346 }
2347 }
2348 while (tmplLine->numElements)
2349 removeElement(tmplLine, 0);
2350 if (tmplLine->elements)
2351 free(tmplLine->elements);
2352
2353 tmplLine->numElements = newLine.numElements;
2354 tmplLine->elements = newLine.elements;
2355 }
2356
2357 static void insertElement(struct singleLine * line,
2358 const char * item, int insertHere,
2359 struct configFileInfo * cfi)
2360 {
2361 struct keywordTypes * kw;
2362 char indent[2] = "";
2363
2364 /* sanity check */
2365 if (insertHere > line->numElements) {
2366 dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2367 insertHere, line->numElements);
2368 insertHere = line->numElements;
2369 }
2370
2371 line->elements = realloc(line->elements, (line->numElements + 1) *
2372 sizeof(*line->elements));
2373 memmove(&line->elements[insertHere+1],
2374 &line->elements[insertHere],
2375 (line->numElements - insertHere) *
2376 sizeof(*line->elements));
2377 line->elements[insertHere].item = strdup(item);
2378
2379 kw = getKeywordByType(line->type, cfi);
2380
2381 if (line->numElements == 0) {
2382 indent[0] = '\0';
2383 } else if (insertHere == 0) {
2384 indent[0] = kw->nextChar;
2385 } else if (kw->separatorChar != '\0') {
2386 indent[0] = kw->separatorChar;
2387 } else {
2388 indent[0] = ' ';
2389 }
2390
2391 if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
2392 /* move the end-of-line forward */
2393 line->elements[insertHere].indent =
2394 line->elements[insertHere-1].indent;
2395 line->elements[insertHere-1].indent = strdup(indent);
2396 } else {
2397 line->elements[insertHere].indent = strdup(indent);
2398 }
2399
2400 line->numElements++;
2401
2402 dbgPrintf("insertElement(%s, '%s%s', %d)\n",
2403 line->elements[0].item,
2404 line->elements[insertHere].item,
2405 line->elements[insertHere].indent,
2406 insertHere);
2407 }
2408
2409 static void removeElement(struct singleLine * line, int removeHere) {
2410 int i;
2411
2412 /* sanity check */
2413 if (removeHere >= line->numElements) return;
2414
2415 dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
2416 removeHere, line->elements[removeHere].item);
2417
2418 free(line->elements[removeHere].item);
2419
2420 if (removeHere > 1) {
2421 /* previous argument gets this argument's post-indentation */
2422 free(line->elements[removeHere-1].indent);
2423 line->elements[removeHere-1].indent =
2424 line->elements[removeHere].indent;
2425 } else {
2426 free(line->elements[removeHere].indent);
2427 }
2428
2429 /* now collapse the array, but don't bother to realloc smaller */
2430 for (i = removeHere; i < line->numElements - 1; i++)
2431 line->elements[i] = line->elements[i + 1];
2432
2433 line->numElements--;
2434 }
2435
2436 int argMatch(const char * one, const char * two) {
2437 char * first, * second;
2438 char * chptr;
2439
2440 first = strcpy(alloca(strlen(one) + 1), one);
2441 second = strcpy(alloca(strlen(two) + 1), two);
2442
2443 chptr = strchr(first, '=');
2444 if (chptr) *chptr = '\0';
2445
2446 chptr = strchr(second, '=');
2447 if (chptr) *chptr = '\0';
2448
2449 return strcmp(first, second);
2450 }
2451
2452 int updateActualImage(struct grubConfig * cfg, const char * image,
2453 const char * prefix, const char * addArgs,
2454 const char * removeArgs, int multibootArgs) {
2455 struct singleEntry * entry;
2456 struct singleLine * line, * rootLine;
2457 int index = 0;
2458 int i, k;
2459 const char ** newArgs, ** oldArgs;
2460 const char ** arg;
2461 int useKernelArgs, useRoot;
2462 int firstElement;
2463 int *usedElements;
2464 int doreplace;
2465
2466 if (!image) return 0;
2467
2468 if (!addArgs) {
2469 newArgs = malloc(sizeof(*newArgs));
2470 *newArgs = NULL;
2471 } else {
2472 if (poptParseArgvString(addArgs, NULL, &newArgs)) {
2473 fprintf(stderr,
2474 _("grubby: error separating arguments '%s'\n"), addArgs);
2475 return 1;
2476 }
2477 }
2478
2479 if (!removeArgs) {
2480 oldArgs = malloc(sizeof(*oldArgs));
2481 *oldArgs = NULL;
2482 } else {
2483 if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
2484 fprintf(stderr,
2485 _("grubby: error separating arguments '%s'\n"), removeArgs);
2486 free(newArgs);
2487 return 1;
2488 }
2489 }
2490
2491
2492 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
2493 && (!multibootArgs || cfg->cfi->mbConcatArgs));
2494
2495 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
2496 && !multibootArgs);
2497
2498 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2499
2500 if (multibootArgs && !entry->multiboot)
2501 continue;
2502
2503 /* Determine where to put the args. If this config supports
2504 * LT_KERNELARGS, use that. Otherwise use
2505 * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
2506 */
2507 if (useKernelArgs) {
2508 line = getLineByType(LT_KERNELARGS, entry->lines);
2509 if (!line) {
2510 /* no LT_KERNELARGS, need to add it */
2511 line = addLine(entry, cfg->cfi, LT_KERNELARGS,
2512 cfg->secondaryIndent, NULL);
2513 }
2514 firstElement = 1;
2515
2516 } else if (multibootArgs) {
2517 line = getLineByType(LT_HYPER, entry->lines);
2518 if (!line) {
2519 /* a multiboot entry without LT_HYPER? */
2520 continue;
2521 }
2522 firstElement = 2;
2523
2524 } else {
2525 line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines);
2526 if (!line) {
2527 /* no LT_KERNEL or LT_MBMODULE in this entry? */
2528 continue;
2529 }
2530 firstElement = 2;
2531 }
2532
2533 /* handle the elilo case which does:
2534 * append="hypervisor args -- kernel args"
2535 */
2536 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
2537 /* this is a multiboot entry, make sure there's
2538 * -- on the args line
2539 */
2540 for (i = firstElement; i < line->numElements; i++) {
2541 if (!strcmp(line->elements[i].item, "--"))
2542 break;
2543 }
2544 if (i == line->numElements) {
2545 /* assume all existing args are kernel args,
2546 * prepend -- to make it official
2547 */
2548 insertElement(line, "--", firstElement, cfg->cfi);
2549 i = firstElement;
2550 }
2551 if (!multibootArgs) {
2552 /* kernel args start after the -- */
2553 firstElement = i + 1;
2554 }
2555 } else if (cfg->cfi->mbConcatArgs) {
2556 /* this is a non-multiboot entry, remove hyper args */
2557 for (i = firstElement; i < line->numElements; i++) {
2558 if (!strcmp(line->elements[i].item, "--"))
2559 break;
2560 }
2561 if (i < line->numElements) {
2562 /* remove args up to -- */
2563 while (strcmp(line->elements[firstElement].item, "--"))
2564 removeElement(line, firstElement);
2565 /* remove -- */
2566 removeElement(line, firstElement);
2567 }
2568 }
2569
2570 usedElements = calloc(line->numElements, sizeof(*usedElements));
2571
2572 for (k = 0, arg = newArgs; *arg; arg++, k++) {
2573
2574 doreplace = 1;
2575 for (i = firstElement; i < line->numElements; i++) {
2576 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2577 !strcmp(line->elements[i].item, "--"))
2578 {
2579 /* reached the end of hyper args, insert here */
2580 doreplace = 0;
2581 break;
2582 }
2583 if (usedElements[i])
2584 continue;
2585 if (!argMatch(line->elements[i].item, *arg)) {
2586 usedElements[i]=1;
2587 break;
2588 }
2589 }
2590
2591 if (i < line->numElements && doreplace) {
2592 /* direct replacement */
2593 free(line->elements[i].item);
2594 line->elements[i].item = strdup(*arg);
2595
2596 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
2597 /* root= replacement */
2598 rootLine = getLineByType(LT_ROOT, entry->lines);
2599 if (rootLine) {
2600 free(rootLine->elements[1].item);
2601 rootLine->elements[1].item = strdup(*arg + 5);
2602 } else {
2603 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
2604 cfg->secondaryIndent, *arg + 5);
2605 }
2606 }
2607
2608 else {
2609 /* insert/append */
2610 insertElement(line, *arg, i, cfg->cfi);
2611 usedElements = realloc(usedElements, line->numElements *
2612 sizeof(*usedElements));
2613 memmove(&usedElements[i + 1], &usedElements[i],
2614 line->numElements - i - 1);
2615 usedElements[i] = 1;
2616
2617 /* if we updated a root= here even though there is a
2618 LT_ROOT available we need to remove the LT_ROOT entry
2619 (this will happen if we switch from a device to a label) */
2620 if (useRoot && !strncmp(*arg, "root=", 5)) {
2621 rootLine = getLineByType(LT_ROOT, entry->lines);
2622 if (rootLine)
2623 removeLine(entry, rootLine);
2624 }
2625 }
2626 }
2627
2628 free(usedElements);
2629
2630 for (arg = oldArgs; *arg; arg++) {
2631 for (i = firstElement; i < line->numElements; i++) {
2632 if (multibootArgs && cfg->cfi->mbConcatArgs &&
2633 !strcmp(line->elements[i].item, "--"))
2634 /* reached the end of hyper args, stop here */
2635 break;
2636 if (!argMatch(line->elements[i].item, *arg)) {
2637 removeElement(line, i);
2638 break;
2639 }
2640 }
2641 /* handle removing LT_ROOT line too */
2642 if (useRoot && !strncmp(*arg, "root=", 5)) {
2643 rootLine = getLineByType(LT_ROOT, entry->lines);
2644 if (rootLine)
2645 removeLine(entry, rootLine);
2646 }
2647 }
2648
2649 if (line->numElements == 1) {
2650 /* don't need the line at all (note it has to be a
2651 LT_KERNELARGS for this to happen */
2652 removeLine(entry, line);
2653 }
2654 }
2655
2656 free(newArgs);
2657 free(oldArgs);
2658
2659 return 0;
2660 }
2661
2662 int updateImage(struct grubConfig * cfg, const char * image,
2663 const char * prefix, const char * addArgs,
2664 const char * removeArgs,
2665 const char * addMBArgs, const char * removeMBArgs) {
2666 int rc = 0;
2667
2668 if (!image) return rc;
2669
2670 /* update the main args first... */
2671 if (addArgs || removeArgs)
2672 rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
2673 if (rc) return rc;
2674
2675 /* and now any multiboot args */
2676 if (addMBArgs || removeMBArgs)
2677 rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
2678 return rc;
2679 }
2680
2681 int updateInitrd(struct grubConfig * cfg, const char * image,
2682 const char * prefix, const char * initrd) {
2683 struct singleEntry * entry;
2684 struct singleLine * line, * kernelLine, *endLine = NULL;
2685 int index = 0;
2686
2687 if (!image) return 0;
2688
2689 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
2690 kernelLine = getLineByType(LT_KERNEL, entry->lines);
2691 if (!kernelLine) continue;
2692
2693 line = getLineByType(LT_INITRD, entry->lines);
2694 if (line)
2695 removeLine(entry, line);
2696 if (prefix) {
2697 int prefixLen = strlen(prefix);
2698 if (!strncmp(initrd, prefix, prefixLen))
2699 initrd += prefixLen;
2700 }
2701 endLine = getLineByType(LT_ENTRY_END, entry->lines);
2702 if (endLine)
2703 removeLine(entry, endLine);
2704 line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd);
2705 if (!line)
2706 return 1;
2707 if (endLine) {
2708 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
2709 if (!line)
2710 return 1;
2711 }
2712
2713 break;
2714 }
2715
2716 return 0;
2717 }
2718
2719 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
2720 int fd;
2721 unsigned char bootSect[512];
2722 int offset;
2723
2724 fd = open(device, O_RDONLY);
2725 if (fd < 0) {
2726 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
2727 device, strerror(errno));
2728 return 1;
2729 }
2730
2731 if (read(fd, bootSect, 512) != 512) {
2732 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2733 device, strerror(errno));
2734 return 1;
2735 }
2736 close(fd);
2737
2738 /* first three bytes should match, a jmp short should be in there */
2739 if (memcmp(boot, bootSect, 3))
2740 return 0;
2741
2742 if (boot[1] == JMP_SHORT_OPCODE) {
2743 offset = boot[2] + 2;
2744 } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
2745 offset = (boot[3] << 8) + boot[2] + 2;
2746 } else if (boot[0] == JMP_SHORT_OPCODE) {
2747 offset = boot[1] + 2;
2748 /*
2749 * it looks like grub, when copying stage1 into the mbr, patches stage1
2750 * right after the JMP location, replacing other instructions such as
2751 * JMPs for NOOPs. So, relax the check a little bit by skipping those
2752 * different bytes.
2753 */
2754 if ((bootSect[offset + 1] == NOOP_OPCODE)
2755 && (bootSect[offset + 2] == NOOP_OPCODE)) {
2756 offset = offset + 3;
2757 }
2758 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
2759 offset = (boot[2] << 8) + boot[1] + 2;
2760 } else {
2761 return 0;
2762 }
2763
2764 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
2765 return 0;
2766
2767 return 2;
2768 }
2769
2770 int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
2771 int fd;
2772 char buf[65536];
2773 char * end;
2774 char * chptr;
2775 char * chptr2;
2776 int rc;
2777
2778 /* it's on raid; we need to parse /proc/mdstat and check all of the
2779 *raw* devices listed in there */
2780
2781 if (!strncmp(mdDev, "/dev/", 5))
2782 mdDev += 5;
2783
2784 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
2785 fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
2786 strerror(errno));
2787 return 2;
2788 }
2789
2790 rc = read(fd, buf, sizeof(buf) - 1);
2791 if (rc < 0 || rc == (sizeof(buf) - 1)) {
2792 fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
2793 strerror(errno));
2794 close(fd);
2795 return 2;
2796 }
2797 close(fd);
2798 buf[rc] = '\0';
2799
2800 chptr = buf;
2801 while (*chptr) {
2802 end = strchr(chptr, '\n');
2803 if (!end) break;
2804 *end = '\0';
2805
2806 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
2807 chptr[strlen(mdDev)] == ' ') {
2808
2809 /* found the device */
2810 while (*chptr && *chptr != ':') chptr++;
2811 chptr++;
2812 while (*chptr && isspace(*chptr)) chptr++;
2813
2814 /* skip the "active" bit */
2815 while (*chptr && !isspace(*chptr)) chptr++;
2816 while (*chptr && isspace(*chptr)) chptr++;
2817
2818 /* skip the raid level */
2819 while (*chptr && !isspace(*chptr)) chptr++;
2820 while (*chptr && isspace(*chptr)) chptr++;
2821
2822 /* everything else is partition stuff */
2823 while (*chptr) {
2824 chptr2 = chptr;
2825 while (*chptr2 && *chptr2 != '[') chptr2++;
2826 if (!*chptr2) break;
2827
2828 /* yank off the numbers at the end */
2829 chptr2--;
2830 while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
2831 chptr2++;
2832 *chptr2 = '\0';
2833
2834 /* Better, now we need the /dev/ back. We're done with
2835 * everything before this point, so we can just put
2836 * the /dev/ part there. There will always be room. */
2837 memcpy(chptr - 5, "/dev/", 5);
2838 rc = checkDeviceBootloader(chptr - 5, boot);
2839 if (rc != 2) {
2840 return rc;
2841 }
2842
2843 chptr = chptr2 + 1;
2844 /* skip the [11] bit */
2845 while (*chptr && !isspace(*chptr)) chptr++;
2846 /* and move to the next one */
2847 while (*chptr && isspace(*chptr)) chptr++;
2848 }
2849
2850 /* we're good to go */
2851 return 2;
2852 }
2853
2854 chptr = end + 1;
2855 }
2856
2857 fprintf(stderr,
2858 _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
2859 mdDev);
2860 return 0;
2861 }
2862
2863 int checkForLilo(struct grubConfig * config) {
2864 int fd;
2865 unsigned char boot[512];
2866 struct singleLine * line;
2867
2868 for (line = config->theLines; line; line = line->next)
2869 if (line->type == LT_BOOT) break;
2870
2871 if (!line) {
2872 fprintf(stderr,
2873 _("grubby: no boot line found in lilo configuration\n"));
2874 return 1;
2875 }
2876
2877 if (line->numElements != 2) return 1;
2878
2879 fd = open("/boot/boot.b", O_RDONLY);
2880 if (fd < 0) {
2881 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
2882 "/boot/boot.b", strerror(errno));
2883 return 1;
2884 }
2885
2886 if (read(fd, boot, 512) != 512) {
2887 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2888 "/boot/boot.b", strerror(errno));
2889 return 1;
2890 }
2891 close(fd);
2892
2893 if (!strncmp("/dev/md", line->elements[1].item, 7))
2894 return checkLiloOnRaid(line->elements[1].item, boot);
2895
2896 return checkDeviceBootloader(line->elements[1].item, boot);
2897 }
2898
2899 int checkForGrub2(struct grubConfig * config) {
2900 if (!access("/etc/grub.d/", R_OK))
2901 return 2;
2902
2903 return 1;
2904 }
2905
2906 int checkForGrub(struct grubConfig * config) {
2907 int fd;
2908 unsigned char bootSect[512];
2909 char * boot;
2910
2911 if (parseSysconfigGrub(NULL, &boot))
2912 return 0;
2913
2914 /* assume grub is not installed -- not an error condition */
2915 if (!boot)
2916 return 0;
2917
2918 fd = open("/boot/grub/stage1", O_RDONLY);
2919 if (fd < 0)
2920 /* this doesn't exist if grub hasn't been installed */
2921 return 0;
2922
2923 if (read(fd, bootSect, 512) != 512) {
2924 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2925 "/boot/grub/stage1", strerror(errno));
2926 close(fd);
2927 return 1;
2928 }
2929 close(fd);
2930
2931 return checkDeviceBootloader(boot, bootSect);
2932 }
2933
2934 int checkForExtLinux(struct grubConfig * config) {
2935 int fd;
2936 unsigned char bootSect[512];
2937 char * boot;
2938 char executable[] = "/boot/extlinux/extlinux";
2939
2940 printf("entered: checkForExtLinux()\n");
2941
2942 if (parseSysconfigGrub(NULL, &boot))
2943 return 0;
2944
2945 /* assume grub is not installed -- not an error condition */
2946 if (!boot)
2947 return 0;
2948
2949 fd = open(executable, O_RDONLY);
2950 if (fd < 0)
2951 /* this doesn't exist if grub hasn't been installed */
2952 return 0;
2953
2954 if (read(fd, bootSect, 512) != 512) {
2955 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
2956 executable, strerror(errno));
2957 return 1;
2958 }
2959 close(fd);
2960
2961 return checkDeviceBootloader(boot, bootSect);
2962 }
2963
2964 static char * getRootSpecifier(char * str) {
2965 char * idx, * rootspec = NULL;
2966
2967 if (*str == '(') {
2968 idx = rootspec = strdup(str);
2969 while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
2970 *(++idx) = '\0';
2971 }
2972 return rootspec;
2973 }
2974
2975 static char * getInitrdVal(struct grubConfig * config,
2976 const char * prefix, struct singleLine *tmplLine,
2977 const char * newKernelInitrd,
2978 char ** extraInitrds, int extraInitrdCount)
2979 {
2980 char *initrdVal, *end;
2981 int i;
2982 size_t totalSize;
2983 size_t prefixLen;
2984 char separatorChar;
2985
2986 prefixLen = strlen(prefix);
2987 totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
2988
2989 for (i = 0; i < extraInitrdCount; i++) {
2990 totalSize += sizeof(separatorChar);
2991 totalSize += strlen(extraInitrds[i]) - prefixLen;
2992 }
2993
2994 initrdVal = end = malloc(totalSize);
2995
2996 end = stpcpy (end, newKernelInitrd + prefixLen);
2997
2998 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
2999 for (i = 0; i < extraInitrdCount; i++) {
3000 const char *extraInitrd;
3001 int j;
3002
3003 extraInitrd = extraInitrds[i] + prefixLen;
3004 /* Don't add entries that are already there */
3005 if (tmplLine != NULL) {
3006 for (j = 2; j < tmplLine->numElements; j++)
3007 if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3008 break;
3009
3010 if (j != tmplLine->numElements)
3011 continue;
3012 }
3013
3014 *end++ = separatorChar;
3015 end = stpcpy(end, extraInitrd);
3016 }
3017
3018 return initrdVal;
3019 }
3020
3021 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3022 const char * prefix,
3023 char * newKernelPath, char * newKernelTitle,
3024 char * newKernelArgs, char * newKernelInitrd,
3025 char ** extraInitrds, int extraInitrdCount,
3026 char * newMBKernel, char * newMBKernelArgs) {
3027 struct singleEntry * new;
3028 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3029 int needs;
3030 char * chptr;
3031
3032 if (!newKernelPath) return 0;
3033
3034 /* if the newKernelTitle is too long silently munge it into something
3035 * we can live with. truncating is first check, then we'll just mess with
3036 * it until it looks better */
3037 if (config->cfi->maxTitleLength &&
3038 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3039 char * buf = alloca(config->cfi->maxTitleLength + 7);
3040 char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3041 int i = 1;
3042
3043 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3044 while (findEntryByPath(config, buf, NULL, NULL)) {
3045 sprintf(numBuf, "%d", i++);
3046 strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3047 }
3048
3049 newKernelTitle = buf + 6;
3050 }
3051
3052 new = malloc(sizeof(*new));
3053 new->skip = 0;
3054 new->multiboot = 0;
3055 new->next = config->entries;
3056 new->lines = NULL;
3057 config->entries = new;
3058
3059 /* copy/update from the template */
3060 needs = NEED_KERNEL | NEED_TITLE;
3061 if (newKernelInitrd)
3062 needs |= NEED_INITRD;
3063 if (newMBKernel) {
3064 needs |= NEED_MB;
3065 new->multiboot = 1;
3066 }
3067
3068 if (template) {
3069 for (masterLine = template->lines;
3070 masterLine && (tmplLine = lineDup(masterLine));
3071 lineFree(tmplLine), masterLine = masterLine->next)
3072 {
3073 dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3074
3075 /* skip comments */
3076 chptr = tmplLine->indent;
3077 while (*chptr && isspace(*chptr)) chptr++;
3078 if (*chptr == '#') continue;
3079
3080 if (tmplLine->type == LT_KERNEL &&
3081 tmplLine->numElements >= 2) {
3082 if (!template->multiboot && (needs & NEED_MB)) {
3083 /* it's not a multiboot template and this is the kernel
3084 * line. Try to be intelligent about inserting the
3085 * hypervisor at the same time.
3086 */
3087 if (config->cfi->mbHyperFirst) {
3088 /* insert the hypervisor first */
3089 newLine = addLine(new, config->cfi, LT_HYPER,
3090 tmplLine->indent,
3091 newMBKernel + strlen(prefix));
3092 /* set up for adding the kernel line */
3093 free(tmplLine->indent);
3094 tmplLine->indent = strdup(config->secondaryIndent);
3095 needs &= ~NEED_MB;
3096 }
3097 if (needs & NEED_KERNEL) {
3098 /* use addLineTmpl to preserve line elements,
3099 * otherwise we could just call addLine. Unfortunately
3100 * this means making some changes to the template
3101 * such as the indent change above and the type
3102 * change below.
3103 */
3104 struct keywordTypes * mbm_kw =
3105 getKeywordByType(LT_MBMODULE, config->cfi);
3106 if (mbm_kw) {
3107 tmplLine->type = LT_MBMODULE;
3108 free(tmplLine->elements[0].item);
3109 tmplLine->elements[0].item = strdup(mbm_kw->key);
3110 }
3111 newLine = addLineTmpl(new, tmplLine, newLine,
3112 newKernelPath + strlen(prefix), config->cfi);
3113 needs &= ~NEED_KERNEL;
3114 }
3115 if (needs & NEED_MB) { /* !mbHyperFirst */
3116 newLine = addLine(new, config->cfi, LT_HYPER,
3117 config->secondaryIndent,
3118 newMBKernel + strlen(prefix));
3119 needs &= ~NEED_MB;
3120 }
3121 } else if (needs & NEED_KERNEL) {
3122 newLine = addLineTmpl(new, tmplLine, newLine,
3123 newKernelPath + strlen(prefix), config->cfi);
3124 needs &= ~NEED_KERNEL;
3125 }
3126
3127 } else if (tmplLine->type == LT_HYPER &&
3128 tmplLine->numElements >= 2) {
3129 if (needs & NEED_MB) {
3130 newLine = addLineTmpl(new, tmplLine, newLine,
3131 newMBKernel + strlen(prefix), config->cfi);
3132 needs &= ~NEED_MB;
3133 }
3134
3135 } else if (tmplLine->type == LT_MBMODULE &&
3136 tmplLine->numElements >= 2) {
3137 if (new->multiboot) {
3138 if (needs & NEED_KERNEL) {
3139 newLine = addLineTmpl(new, tmplLine, newLine,
3140 newKernelPath +
3141 strlen(prefix), config->cfi);
3142 needs &= ~NEED_KERNEL;
3143 } else if (config->cfi->mbInitRdIsModule &&
3144 (needs & NEED_INITRD)) {
3145 char *initrdVal;
3146 initrdVal = getInitrdVal(config, prefix, tmplLine,
3147 newKernelInitrd, extraInitrds,
3148 extraInitrdCount);
3149 newLine = addLineTmpl(new, tmplLine, newLine,
3150 initrdVal, config->cfi);
3151 free(initrdVal);
3152 needs &= ~NEED_INITRD;
3153 }
3154 } else if (needs & NEED_KERNEL) {
3155 /* template is multi but new is not,
3156 * insert the kernel in the first module slot
3157 */
3158 tmplLine->type = LT_KERNEL;
3159 free(tmplLine->elements[0].item);
3160 tmplLine->elements[0].item =
3161 strdup(getKeywordByType(LT_KERNEL, config->cfi)->key);
3162 newLine = addLineTmpl(new, tmplLine, newLine,
3163 newKernelPath + strlen(prefix), config->cfi);
3164 needs &= ~NEED_KERNEL;
3165 } else if (needs & NEED_INITRD) {
3166 char *initrdVal;
3167 /* template is multi but new is not,
3168 * insert the initrd in the second module slot
3169 */
3170 tmplLine->type = LT_INITRD;
3171 free(tmplLine->elements[0].item);
3172 tmplLine->elements[0].item =
3173 strdup(getKeywordByType(LT_INITRD, config->cfi)->key);
3174 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3175 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3176 free(initrdVal);
3177 needs &= ~NEED_INITRD;
3178 }
3179
3180 } else if (tmplLine->type == LT_INITRD &&
3181 tmplLine->numElements >= 2) {
3182 if (needs & NEED_INITRD &&
3183 new->multiboot && !template->multiboot &&
3184 config->cfi->mbInitRdIsModule) {
3185 /* make sure we don't insert the module initrd
3186 * before the module kernel... if we don't do it here,
3187 * it will be inserted following the template.
3188 */
3189 if (!needs & NEED_KERNEL) {
3190 char *initrdVal;
3191
3192 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3193 newLine = addLine(new, config->cfi, LT_MBMODULE,
3194 config->secondaryIndent,
3195 initrdVal);
3196 free(initrdVal);
3197 needs &= ~NEED_INITRD;
3198 }
3199 } else if (needs & NEED_INITRD) {
3200 char *initrdVal;
3201 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3202 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3203 free(initrdVal);
3204 needs &= ~NEED_INITRD;
3205 }
3206
3207 } else if (tmplLine->type == LT_MENUENTRY &&
3208 (needs & NEED_TITLE)) {
3209 requote(tmplLine, config->cfi);
3210 char *nkt = malloc(strlen(newKernelTitle)+3);
3211 strcpy(nkt, "'");
3212 strcat(nkt, newKernelTitle);
3213 strcat(nkt, "'");
3214 newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3215 free(nkt);
3216 needs &= ~NEED_TITLE;
3217 } else if (tmplLine->type == LT_TITLE &&
3218 (needs & NEED_TITLE)) {
3219 if (tmplLine->numElements >= 2) {
3220 newLine = addLineTmpl(new, tmplLine, newLine,
3221 newKernelTitle, config->cfi);
3222 needs &= ~NEED_TITLE;
3223 } else if (tmplLine->numElements == 1 &&
3224 config->cfi->titleBracketed) {
3225 /* addLineTmpl doesn't handle titleBracketed */
3226 newLine = addLine(new, config->cfi, LT_TITLE,
3227 tmplLine->indent, newKernelTitle);
3228 needs &= ~NEED_TITLE;
3229 }
3230 } else if (tmplLine->type == LT_ECHO) {
3231 requote(tmplLine, config->cfi);
3232 static const char *prefix = "'Loading ";
3233 if (tmplLine->numElements > 1 &&
3234 strstr(tmplLine->elements[1].item, prefix) &&
3235 masterLine->next && masterLine->next->type == LT_KERNEL) {
3236 char *newTitle = malloc(strlen(prefix) +
3237 strlen(newKernelTitle) + 2);
3238
3239 strcpy(newTitle, prefix);
3240 strcat(newTitle, newKernelTitle);
3241 strcat(newTitle, "'");
3242 newLine = addLine(new, config->cfi, LT_ECHO,
3243 tmplLine->indent, newTitle);
3244 free(newTitle);
3245 } else {
3246 /* pass through other lines from the template */
3247 newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3248 config->cfi);
3249 }
3250 } else {
3251 /* pass through other lines from the template */
3252 newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3253 }
3254 }
3255
3256 } else {
3257 /* don't have a template, so start the entry with the
3258 * appropriate starting line
3259 */
3260 switch (config->cfi->entryStart) {
3261 case LT_KERNEL:
3262 if (new->multiboot && config->cfi->mbHyperFirst) {
3263 /* fall through to LT_HYPER */
3264 } else {
3265 newLine = addLine(new, config->cfi, LT_KERNEL,
3266 config->primaryIndent,
3267 newKernelPath + strlen(prefix));
3268 needs &= ~NEED_KERNEL;
3269 break;
3270 }
3271
3272 case LT_HYPER:
3273 newLine = addLine(new, config->cfi, LT_HYPER,
3274 config->primaryIndent,
3275 newMBKernel + strlen(prefix));
3276 needs &= ~NEED_MB;
3277 break;
3278
3279 case LT_MENUENTRY: {
3280 char *nkt = malloc(strlen(newKernelTitle)+3);
3281 strcpy(nkt, "'");
3282 strcat(nkt, newKernelTitle);
3283 strcat(nkt, "'");
3284 newLine = addLine(new, config->cfi, LT_MENUENTRY,
3285 config->primaryIndent, nkt);
3286 free(nkt);
3287 needs &= ~NEED_TITLE;
3288 needs |= NEED_END;
3289 break;
3290 }
3291 case LT_TITLE:
3292 if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
3293 char * templabel;
3294 int x = 0, y = 0;
3295
3296 templabel = strdup(newKernelTitle);
3297 while( templabel[x]){
3298 if( templabel[x] == ' ' ){
3299 y = x;
3300 while( templabel[y] ){
3301 templabel[y] = templabel[y+1];
3302 y++;
3303 }
3304 }
3305 x++;
3306 }
3307 newLine = addLine(new, config->cfi, LT_TITLE,
3308 config->primaryIndent, templabel);
3309 free(templabel);
3310 }else{
3311 newLine = addLine(new, config->cfi, LT_TITLE,
3312 config->primaryIndent, newKernelTitle);
3313 }
3314 needs &= ~NEED_TITLE;
3315 break;
3316
3317 default:
3318 abort();
3319 }
3320 }
3321
3322 /* add the remainder of the lines, i.e. those that either
3323 * weren't present in the template, or in the case of no template,
3324 * all the lines following the entryStart.
3325 */
3326 if (needs & NEED_TITLE) {
3327 newLine = addLine(new, config->cfi, LT_TITLE,
3328 config->secondaryIndent,
3329 newKernelTitle);
3330 needs &= ~NEED_TITLE;
3331 }
3332 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
3333 newLine = addLine(new, config->cfi, LT_HYPER,
3334 config->secondaryIndent,
3335 newMBKernel + strlen(prefix));
3336 needs &= ~NEED_MB;
3337 }
3338 if (needs & NEED_KERNEL) {
3339 newLine = addLine(new, config->cfi,
3340 (new->multiboot && getKeywordByType(LT_MBMODULE,
3341 config->cfi)) ?
3342 LT_MBMODULE : LT_KERNEL,
3343 config->secondaryIndent,
3344 newKernelPath + strlen(prefix));
3345 needs &= ~NEED_KERNEL;
3346 }
3347 if (needs & NEED_MB) {
3348 newLine = addLine(new, config->cfi, LT_HYPER,
3349 config->secondaryIndent,
3350 newMBKernel + strlen(prefix));
3351 needs &= ~NEED_MB;
3352 }
3353 if (needs & NEED_INITRD) {
3354 char *initrdVal;
3355 initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
3356 newLine = addLine(new, config->cfi,
3357 (new->multiboot && getKeywordByType(LT_MBMODULE,
3358 config->cfi)) ?
3359 LT_MBMODULE : LT_INITRD,
3360 config->secondaryIndent,
3361 initrdVal);
3362 free(initrdVal);
3363 needs &= ~NEED_INITRD;
3364 }
3365 if (needs & NEED_END) {
3366 newLine = addLine(new, config->cfi, LT_ENTRY_END,
3367 config->secondaryIndent, NULL);
3368 needs &= ~NEED_END;
3369 }
3370
3371 if (needs) {
3372 printf(_("grubby: needs=%d, aborting\n"), needs);
3373 abort();
3374 }
3375
3376 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
3377 newMBKernelArgs, NULL)) return 1;
3378
3379 return 0;
3380 }
3381
3382 static void traceback(int signum)
3383 {
3384 void *array[40];
3385 size_t size;
3386
3387 signal(SIGSEGV, SIG_DFL);
3388 memset(array, '\0', sizeof (array));
3389 size = backtrace(array, 40);
3390
3391 fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n",
3392 (unsigned long)size);
3393 backtrace_symbols_fd(array, size, STDERR_FILENO);
3394 exit(1);
3395 }
3396
3397 int main(int argc, const char ** argv) {
3398 poptContext optCon;
3399 const char * grubConfig = NULL;
3400 char * outputFile = NULL;
3401 int arg = 0;
3402 int flags = 0;
3403 int badImageOkay = 0;
3404 int configureGrub2 = 0;
3405 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
3406 int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
3407 int configureExtLinux = 0;
3408 int bootloaderProbe = 0;
3409 int extraInitrdCount = 0;
3410 char * updateKernelPath = NULL;
3411 char * newKernelPath = NULL;
3412 char * removeKernelPath = NULL;
3413 char * newKernelArgs = NULL;
3414 char * newKernelInitrd = NULL;
3415 char * newKernelTitle = NULL;
3416 char * newKernelVersion = NULL;
3417 char * newMBKernel = NULL;
3418 char * newMBKernelArgs = NULL;
3419 char * removeMBKernelArgs = NULL;
3420 char * removeMBKernel = NULL;
3421 char * bootPrefix = NULL;
3422 char * defaultKernel = NULL;
3423 char * removeArgs = NULL;
3424 char * kernelInfo = NULL;
3425 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
3426 const char * chptr = NULL;
3427 struct configFileInfo * cfi = NULL;
3428 struct grubConfig * config;
3429 struct singleEntry * template = NULL;
3430 int copyDefault = 0, makeDefault = 0;
3431 int displayDefault = 0;
3432 int displayDefaultIndex = 0;
3433 int displayDefaultTitle = 0;
3434 struct poptOption options[] = {
3435 { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
3436 _("add an entry for the specified kernel"), _("kernel-path") },
3437 { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
3438 _("add an entry for the specified multiboot kernel"), NULL },
3439 { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
3440 _("default arguments for the new kernel or new arguments for "
3441 "kernel being updated"), _("args") },
3442 { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
3443 _("default arguments for the new multiboot kernel or "
3444 "new arguments for multiboot kernel being updated"), NULL },
3445 { "bad-image-okay", 0, 0, &badImageOkay, 0,
3446 _("don't sanity check images in boot entries (for testing only)"),
3447 NULL },
3448 { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
3449 _("filestystem which contains /boot directory (for testing only)"),
3450 _("bootfs") },
3451 #if defined(__i386__) || defined(__x86_64__)
3452 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
3453 _("check if lilo is installed on lilo.conf boot sector") },
3454 #endif
3455 { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
3456 _("path to grub config file to update (\"-\" for stdin)"),
3457 _("path") },
3458 { "copy-default", 0, 0, &copyDefault, 0,
3459 _("use the default boot entry as a template for the new entry "
3460 "being added; if the default is not a linux image, or if "
3461 "the kernel referenced by the default image does not exist, "
3462 "the first linux entry whose kernel does exist is used as the "
3463 "template"), NULL },
3464 { "debug", 0, 0, &debug, 0,
3465 _("print debugging information for failures") },
3466 { "default-kernel", 0, 0, &displayDefault, 0,
3467 _("display the path of the default kernel") },
3468 { "default-index", 0, 0, &displayDefaultIndex, 0,
3469 _("display the index of the default kernel") },
3470 { "default-title", 0, 0, &displayDefaultTitle, 0,
3471 _("display the title of the default kernel") },
3472 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
3473 _("configure elilo bootloader") },
3474 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
3475 _("configure extlinux bootloader (from syslinux)") },
3476 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
3477 _("configure grub bootloader") },
3478 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
3479 _("configure grub2 bootloader") },
3480 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
3481 _("display boot information for specified kernel"),
3482 _("kernel-path") },
3483 { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
3484 _("initrd image for the new kernel"), _("initrd-path") },
3485 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
3486 _("auxilliary initrd image for things other than the new kernel"), _("initrd-path") },
3487 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
3488 _("configure lilo bootloader") },
3489 { "make-default", 0, 0, &makeDefault, 0,
3490 _("make the newly added entry the default boot entry"), NULL },
3491 { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
3492 _("path to output updated config file (\"-\" for stdout)"),
3493 _("path") },
3494 { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
3495 _("remove kernel arguments"), NULL },
3496 { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
3497 _("remove multiboot kernel arguments"), NULL },
3498 { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
3499 _("remove all entries for the specified kernel"),
3500 _("kernel-path") },
3501 { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
3502 _("remove all entries for the specified multiboot kernel"), NULL },
3503 { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
3504 _("make the first entry referencing the specified kernel "
3505 "the default"), _("kernel-path") },
3506 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
3507 _("configure silo bootloader") },
3508 { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
3509 _("title to use for the new kernel entry"), _("entry-title") },
3510 { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
3511 _("updated information for the specified kernel"),
3512 _("kernel-path") },
3513 { "version", 'v', 0, NULL, 'v',
3514 _("print the version of this program and exit"), NULL },
3515 { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
3516 _("configure yaboot bootloader") },
3517 { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
3518 _("configure zipl bootloader") },
3519 POPT_AUTOHELP
3520 { 0, 0, 0, 0, 0 }
3521 };
3522
3523 useextlinuxmenu=0;
3524
3525 signal(SIGSEGV, traceback);
3526
3527 optCon = poptGetContext("grubby", argc, argv, options, 0);
3528 poptReadDefaultConfig(optCon, 1);
3529
3530 while ((arg = poptGetNextOpt(optCon)) >= 0) {
3531 switch (arg) {
3532 case 'v':
3533 printf("grubby version %s\n", VERSION);
3534 exit(0);
3535 break;
3536 case 'i':
3537 if (extraInitrdCount < MAX_EXTRA_INITRDS) {
3538 extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
3539 } else {
3540 fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
3541 return 1;
3542 }
3543 break;
3544 }
3545 }
3546
3547 if (arg < -1) {
3548 fprintf(stderr, _("grubby: bad argument %s: %s\n"),
3549 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
3550 poptStrerror(arg));
3551 return 1;
3552 }
3553
3554 if ((chptr = poptGetArg(optCon))) {
3555 fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
3556 return 1;
3557 }
3558
3559 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
3560 configureYaboot + configureSilo + configureZipl +
3561 configureExtLinux ) > 1) {
3562 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
3563 return 1;
3564 } else if (bootloaderProbe && grubConfig) {
3565 fprintf(stderr,
3566 _("grubby: cannot specify config file with --bootloader-probe\n"));
3567 return 1;
3568 } else if (configureGrub2) {
3569 cfi = &grub2ConfigType;
3570 } else if (configureLilo) {
3571 cfi = &liloConfigType;
3572 } else if (configureGrub) {
3573 cfi = &grubConfigType;
3574 } else if (configureELilo) {
3575 cfi = &eliloConfigType;
3576 } else if (configureYaboot) {
3577 cfi = &yabootConfigType;
3578 } else if (configureSilo) {
3579 cfi = &siloConfigType;
3580 } else if (configureZipl) {
3581 cfi = &ziplConfigType;
3582 } else if (configureExtLinux) {
3583 cfi = &extlinuxConfigType;
3584 useextlinuxmenu=1;
3585 }
3586
3587 if (!cfi) {
3588 if (grub2FindConfig(&grub2ConfigType))
3589 cfi = &grub2ConfigType;
3590 else
3591 #ifdef __ia64__
3592 cfi = &eliloConfigType;
3593 #elif __powerpc__
3594 cfi = &yabootConfigType;
3595 #elif __sparc__
3596 cfi = &siloConfigType;
3597 #elif __s390__
3598 cfi = &ziplConfigType;
3599 #elif __s390x__
3600 cfi = &ziplConfigtype;
3601 #else
3602 cfi = &grubConfigType;
3603 #endif
3604 }
3605
3606 if (!grubConfig) {
3607 if (cfi->findConfig)
3608 grubConfig = cfi->findConfig(cfi);
3609 if (!grubConfig)
3610 grubConfig = cfi->defaultConfig;
3611 }
3612
3613 if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
3614 newKernelPath || removeKernelPath || makeDefault ||
3615 defaultKernel || displayDefaultIndex || displayDefaultTitle)) {
3616 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
3617 "specified option"));
3618 return 1;
3619 }
3620
3621 if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
3622 removeKernelPath)) {
3623 fprintf(stderr, _("grubby: --default-kernel and --info may not "
3624 "be used when adding or removing kernels\n"));
3625 return 1;
3626 }
3627
3628 if (newKernelPath && !newKernelTitle) {
3629 fprintf(stderr, _("grubby: kernel title must be specified\n"));
3630 return 1;
3631 } else if (!newKernelPath && (newKernelTitle || copyDefault ||
3632 (newKernelInitrd && !updateKernelPath)||
3633 makeDefault || extraInitrdCount > 0)) {
3634 fprintf(stderr, _("grubby: kernel path expected\n"));
3635 return 1;
3636 }
3637
3638 if (newKernelPath && updateKernelPath) {
3639 fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
3640 "not be used together"));
3641 return 1;
3642 }
3643
3644 if (makeDefault && defaultKernel) {
3645 fprintf(stderr, _("grubby: --make-default and --default-kernel "
3646 "may not be used together\n"));
3647 return 1;
3648 } else if (defaultKernel && removeKernelPath &&
3649 !strcmp(defaultKernel, removeKernelPath)) {
3650 fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
3651 return 1;
3652 } else if (defaultKernel && newKernelPath &&
3653 !strcmp(defaultKernel, newKernelPath)) {
3654 makeDefault = 1;
3655 defaultKernel = NULL;
3656 }
3657
3658 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
3659 fprintf(stderr, _("grubby: output file must be specified if stdin "
3660 "is used\n"));
3661 return 1;
3662 }
3663
3664 if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
3665 && !kernelInfo && !bootloaderProbe && !updateKernelPath
3666 && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle) {
3667 fprintf(stderr, _("grubby: no action specified\n"));
3668 return 1;
3669 }
3670
3671 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
3672
3673 if (cfi->needsBootPrefix) {
3674 if (!bootPrefix) {
3675 bootPrefix = findBootPrefix();
3676 if (!bootPrefix) return 1;
3677 } else {
3678 /* this shouldn't end with a / */
3679 if (bootPrefix[strlen(bootPrefix) - 1] == '/')
3680 bootPrefix[strlen(bootPrefix) - 1] = '\0';
3681 }
3682 } else {
3683 bootPrefix = "";
3684 }
3685
3686 if (!cfi->mbAllowExtraInitRds &&
3687 extraInitrdCount > 0) {
3688 fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
3689 return 1;
3690 }
3691
3692 if (bootloaderProbe) {
3693 int lrc = 0, grc = 0, gr2c = 0, erc = 0;
3694 struct grubConfig * lconfig, * gconfig;
3695
3696 const char *grub2config = grub2FindConfig(&grub2ConfigType);
3697 if (grub2config) {
3698 gconfig = readConfig(grub2config, &grub2ConfigType);
3699 if (!gconfig)
3700 gr2c = 1;
3701 else
3702 gr2c = checkForGrub2(gconfig);
3703 }
3704
3705 const char *grubconfig = grubFindConfig(&grubConfigType);
3706 if (!access(grubconfig, F_OK)) {
3707 gconfig = readConfig(grubconfig, &grubConfigType);
3708 if (!gconfig)
3709 grc = 1;
3710 else
3711 grc = checkForGrub(gconfig);
3712 }
3713
3714 if (!access(liloConfigType.defaultConfig, F_OK)) {
3715 lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
3716 if (!lconfig)
3717 lrc = 1;
3718 else
3719 lrc = checkForLilo(lconfig);
3720 }
3721
3722 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
3723 lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
3724 if (!lconfig)
3725 erc = 1;
3726 else
3727 erc = checkForExtLinux(lconfig);
3728 }
3729
3730 if (lrc == 1 || grc == 1 || gr2c == 1) return 1;
3731
3732 if (lrc == 2) printf("lilo\n");
3733 if (gr2c == 2) printf("grub2\n");
3734 if (grc == 2) printf("grub\n");
3735 if (erc == 2) printf("extlinux\n");
3736
3737 return 0;
3738 }
3739
3740 config = readConfig(grubConfig, cfi);
3741 if (!config) return 1;
3742
3743 if (displayDefault) {
3744 struct singleLine * line;
3745 struct singleEntry * entry;
3746 char * rootspec;
3747
3748 if (config->defaultImage == -1) return 0;
3749 entry = findEntryByIndex(config, config->defaultImage);
3750 if (!entry) return 0;
3751 if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
3752
3753 line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);
3754 if (!line) return 0;
3755
3756 rootspec = getRootSpecifier(line->elements[1].item);
3757 printf("%s%s\n", bootPrefix, line->elements[1].item +
3758 ((rootspec != NULL) ? strlen(rootspec) : 0));
3759
3760 return 0;
3761
3762 } else if (displayDefaultTitle) {
3763 struct singleLine * line;
3764 struct singleEntry * entry;
3765
3766 if (config->defaultImage == -1) return 0;
3767 entry = findEntryByIndex(config, config->defaultImage);
3768 if (!entry) return 0;
3769
3770 if (!configureGrub2) {
3771 line = getLineByType(LT_TITLE, entry->lines);
3772 if (!line) return 0;
3773 printf("%s\n", line->elements[1].item);
3774
3775 } else {
3776 char * title;
3777
3778 dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
3779 line = getLineByType(LT_MENUENTRY, entry->lines);
3780 if (!line) return 0;
3781 title = grub2ExtractTitle(line);
3782 if (title)
3783 printf("%s\n", title);
3784 }
3785 return 0;
3786
3787 } else if (displayDefaultIndex) {
3788 if (config->defaultImage == -1) return 0;
3789 printf("%i\n", config->defaultImage);
3790
3791 } else if (kernelInfo)
3792 return displayInfo(config, kernelInfo, bootPrefix);
3793
3794 if (copyDefault) {
3795 template = findTemplate(config, bootPrefix, NULL, 0, flags);
3796 if (!template) return 1;
3797 }
3798
3799 markRemovedImage(config, removeKernelPath, bootPrefix);
3800 markRemovedImage(config, removeMBKernel, bootPrefix);
3801 setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
3802 bootPrefix, flags);
3803 setFallbackImage(config, newKernelPath != NULL);
3804 if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
3805 removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
3806 if (updateKernelPath && newKernelInitrd) {
3807 if (updateInitrd(config, updateKernelPath, bootPrefix,
3808 newKernelInitrd)) return 1;
3809 }
3810 if (addNewKernel(config, template, bootPrefix, newKernelPath,
3811 newKernelTitle, newKernelArgs, newKernelInitrd,
3812 extraInitrds, extraInitrdCount,
3813 newMBKernel, newMBKernelArgs)) return 1;
3814
3815
3816 if (numEntries(config) == 0) {
3817 fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
3818 "Not writing out new config.\n"));
3819 return 1;
3820 }
3821
3822 if (!outputFile)
3823 outputFile = (char *)grubConfig;
3824
3825 return writeConfig(config, outputFile, bootPrefix);
3826 }