Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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