Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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