Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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