Magellan Linux

Contents of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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