Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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