Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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