Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2970 - (show annotations) (download)
Thu Jun 30 09:57:47 2016 UTC (7 years, 9 months ago) by niro
File MIME type: text/plain
File size: 130445 byte(s)
tagged 'grubby-8_36'
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) {
1967 i++;
1968 if (indexVars[i] == -1) return NULL;
1969 }
1970 }
1971
1972 entry = findEntryByIndex(config, indexVars[i]);
1973 if (!entry) return NULL;
1974
1975 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
1976 if (!line) return NULL;
1977
1978 if (index) *index = indexVars[i];
1979 return entry;
1980 }
1981
1982 if (!strcmp(kernel, "DEFAULT")) {
1983 if (index && *index > config->defaultImage) {
1984 entry = NULL;
1985 } else {
1986 entry = findEntryByIndex(config, config->defaultImage);
1987 if (entry && entry->skip)
1988 entry = NULL;
1989 else if (index)
1990 *index = config->defaultImage;
1991 }
1992 } else if (!strcmp(kernel, "ALL")) {
1993 if (index)
1994 i = *index;
1995 else
1996 i = 0;
1997
1998 while ((entry = findEntryByIndex(config, i))) {
1999 if (!entry->skip) break;
2000 i++;
2001 }
2002
2003 if (entry && index)
2004 *index = i;
2005 } else {
2006 if (index)
2007 i = *index;
2008 else
2009 i = 0;
2010
2011 if (!strncmp(kernel, "TITLE=", 6)) {
2012 prefix = "";
2013 checkType = LT_TITLE|LT_MENUENTRY;
2014 kernel += 6;
2015 }
2016
2017 for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
2018 if (entry->skip) continue;
2019
2020 dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
2021
2022 /* check all the lines matching checkType */
2023 for (line = entry->lines; line; line = line->next) {
2024 enum lineType_e ct = checkType;
2025 if (entry->multiboot && checkType == LT_KERNEL)
2026 ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER|LT_KERNEL_16;
2027 else if (checkType & LT_KERNEL)
2028 ct = checkType | LT_KERNEL_EFI | LT_KERNEL_16;
2029 line = getLineByType(ct, line);
2030 if (!line)
2031 break; /* not found in this entry */
2032
2033 if (line && line->type != LT_MENUENTRY &&
2034 line->numElements >= 2) {
2035 rootspec = getRootSpecifier(line->elements[1].item);
2036 if (!strcmp(line->elements[1].item +
2037 ((rootspec != NULL) ? strlen(rootspec) : 0),
2038 kernel + strlen(prefix)))
2039 break;
2040 }
2041 if(line->type == LT_MENUENTRY &&
2042 !strcmp(line->elements[1].item, kernel))
2043 break;
2044 }
2045
2046 /* make sure this entry has a kernel identifier; this skips
2047 * non-Linux boot entries (could find netbsd etc, though, which is
2048 * unfortunate)
2049 */
2050 if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines))
2051 break; /* found 'im! */
2052 }
2053
2054 if (index) *index = i;
2055 }
2056
2057 return entry;
2058 }
2059
2060 struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
2061 int * index) {
2062 struct singleEntry * entry;
2063 struct singleLine * line;
2064 int i;
2065 char * newtitle;
2066
2067 for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2068 if (index && i < *index)
2069 continue;
2070 line = getLineByType(LT_TITLE, entry->lines);
2071 if (!line)
2072 line = getLineByType(LT_MENUENTRY, entry->lines);
2073 if (!line)
2074 continue;
2075 newtitle = grub2ExtractTitle(line);
2076 if (!newtitle)
2077 continue;
2078 if (!strcmp(title, newtitle))
2079 break;
2080 }
2081
2082 if (!entry)
2083 return NULL;
2084
2085 if (index)
2086 *index = i;
2087 return entry;
2088 }
2089
2090 struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
2091 struct singleEntry * entry;
2092
2093 entry = cfg->entries;
2094 while (index && entry) {
2095 entry = entry->next;
2096 index--;
2097 }
2098
2099 return entry;
2100 }
2101
2102 /* Find a good template to use for the new kernel. An entry is
2103 * good if the kernel and mkinitrd exist (even if the entry
2104 * is going to be removed). Try and use the default entry, but
2105 * if that doesn't work just take the first. If we can't find one,
2106 * bail. */
2107 struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
2108 int * indexPtr, int skipRemoved, int flags) {
2109 struct singleEntry * entry, * entry2;
2110 int index;
2111
2112 if (cfg->cfi->defaultIsSaved) {
2113 if (cfg->cfi->getEnv) {
2114 char *defTitle = cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2115 if (defTitle) {
2116 int index = 0;
2117 if (isnumber(defTitle)) {
2118 index = atoi(defTitle);
2119 entry = findEntryByIndex(cfg, index);
2120 } else {
2121 entry = findEntryByTitle(cfg, defTitle, &index);
2122 }
2123 if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2124 cfg->defaultImage = index;
2125 if (indexPtr)
2126 *indexPtr = index;
2127 return entry;
2128 }
2129 }
2130 }
2131 } else if (cfg->defaultImage > -1) {
2132 entry = findEntryByIndex(cfg, cfg->defaultImage);
2133 if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2134 if (indexPtr) *indexPtr = cfg->defaultImage;
2135 return entry;
2136 }
2137 }
2138
2139 index = 0;
2140 while ((entry = findEntryByIndex(cfg, index))) {
2141 if (suitableImage(entry, prefix, skipRemoved, flags)) {
2142 int j;
2143 for (j = 0; j < index; j++) {
2144 entry2 = findEntryByIndex(cfg, j);
2145 if (entry2->skip) index--;
2146 }
2147 if (indexPtr) *indexPtr = index;
2148
2149 return entry;
2150 }
2151
2152 index++;
2153 }
2154
2155 fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
2156
2157 return NULL;
2158 }
2159
2160 char * findBootPrefix(void) {
2161 struct stat sb, sb2;
2162
2163 stat("/", &sb);
2164 #ifdef __ia64__
2165 stat("/boot/efi/EFI/redhat/", &sb2);
2166 #else
2167 stat("/boot", &sb2);
2168 #endif
2169
2170 if (sb.st_dev == sb2.st_dev)
2171 return strdup("");
2172
2173 #ifdef __ia64__
2174 return strdup("/boot/efi/EFI/redhat/");
2175 #else
2176 return strdup("/boot");
2177 #endif
2178 }
2179
2180 void markRemovedImage(struct grubConfig * cfg, const char * image,
2181 const char * prefix) {
2182 struct singleEntry * entry;
2183
2184 if (!image)
2185 return;
2186
2187 /* check and see if we're removing the default image */
2188 if (isdigit(*image)) {
2189 entry = findEntryByPath(cfg, image, prefix, NULL);
2190 if(entry)
2191 entry->skip = 1;
2192 return;
2193 }
2194
2195 while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2196 entry->skip = 1;
2197 }
2198
2199 void setDefaultImage(struct grubConfig * config, int hasNew,
2200 const char * defaultKernelPath, int newIsDefault,
2201 const char * prefix, int flags, int index) {
2202 struct singleEntry * entry, * entry2, * newDefault;
2203 int i, j;
2204
2205 if (newIsDefault) {
2206 config->defaultImage = 0;
2207 return;
2208 } else if ((index >= 0) && config->cfi->defaultIsIndex) {
2209 if (findEntryByIndex(config, index))
2210 config->defaultImage = index;
2211 else
2212 config->defaultImage = -1;
2213 return;
2214 } else if (defaultKernelPath) {
2215 i = 0;
2216 if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
2217 config->defaultImage = i;
2218 } else {
2219 config->defaultImage = -1;
2220 return;
2221 }
2222 }
2223
2224 /* defaultImage now points to what we'd like to use, but before any order
2225 changes */
2226 if ((config->defaultImage == DEFAULT_SAVED) ||
2227 (config->defaultImage == DEFAULT_SAVED_GRUB2))
2228 /* default is set to saved, we don't want to change it */
2229 return;
2230
2231 if (config->defaultImage > -1)
2232 entry = findEntryByIndex(config, config->defaultImage);
2233 else
2234 entry = NULL;
2235
2236 if (entry && !entry->skip) {
2237 /* we can preserve the default */
2238 if (hasNew)
2239 config->defaultImage++;
2240
2241 /* count the number of entries erased before this one */
2242 for (j = 0; j < config->defaultImage; j++) {
2243 entry2 = findEntryByIndex(config, j);
2244 if (entry2->skip) config->defaultImage--;
2245 }
2246 } else if (hasNew) {
2247 config->defaultImage = 0;
2248 } else {
2249 /* Either we just erased the default (or the default line was bad
2250 * to begin with) and didn't put a new one in. We'll use the first
2251 * valid image. */
2252 newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
2253 flags);
2254 if (!newDefault)
2255 config->defaultImage = -1;
2256 }
2257 }
2258
2259 void setFallbackImage(struct grubConfig * config, int hasNew) {
2260 struct singleEntry * entry, * entry2;
2261 int j;
2262
2263 if (config->fallbackImage == -1) return;
2264
2265 entry = findEntryByIndex(config, config->fallbackImage);
2266 if (!entry || entry->skip) {
2267 config->fallbackImage = -1;
2268 return;
2269 }
2270
2271 if (hasNew)
2272 config->fallbackImage++;
2273
2274 /* count the number of entries erased before this one */
2275 for (j = 0; j < config->fallbackImage; j++) {
2276 entry2 = findEntryByIndex(config, j);
2277 if (entry2->skip) config->fallbackImage--;
2278 }
2279 }
2280
2281 void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
2282 struct singleLine * line;
2283 char * root = NULL;
2284 int i;
2285 int j;
2286
2287 printf("index=%d\n", index);
2288
2289 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
2290 if (!line) {
2291 printf("non linux entry\n");
2292 return;
2293 }
2294
2295 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2296 printf("kernel=%s\n", line->elements[1].item);
2297 else
2298 printf("kernel=%s%s\n", prefix, line->elements[1].item);
2299
2300 if (line->numElements >= 3) {
2301 printf("args=\"");
2302 i = 2;
2303 while (i < line->numElements) {
2304 if (!strncmp(line->elements[i].item, "root=", 5)) {
2305 root = line->elements[i].item + 5;
2306 } else {
2307 printf("%s%s", line->elements[i].item,
2308 line->elements[i].indent);
2309 }
2310
2311 i++;
2312 }
2313 printf("\"\n");
2314 } else {
2315 line = getLineByType(LT_KERNELARGS, entry->lines);
2316 if (line) {
2317 char * s;
2318
2319 printf("args=\"");
2320 i = 1;
2321 while (i < line->numElements) {
2322 if (!strncmp(line->elements[i].item, "root=", 5)) {
2323 root = line->elements[i].item + 5;
2324 } else {
2325 s = line->elements[i].item;
2326
2327 printf("%s%s", s, line->elements[i].indent);
2328 }
2329
2330 i++;
2331 }
2332
2333 s = line->elements[i - 1].indent;
2334 printf("\"\n");
2335 }
2336 }
2337
2338 if (!root) {
2339 line = getLineByType(LT_ROOT, entry->lines);
2340 if (line && line->numElements >= 2)
2341 root=line->elements[1].item;
2342 }
2343
2344 if (root) {
2345 char * s = alloca(strlen(root) + 1);
2346
2347 strcpy(s, root);
2348 if (s[strlen(s) - 1] == '"')
2349 s[strlen(s) - 1] = '\0';
2350 /* make sure the root doesn't have a trailing " */
2351 printf("root=%s\n", s);
2352 }
2353
2354 line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
2355
2356 if (line && line->numElements >= 2) {
2357 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2358 printf("initrd=");
2359 else
2360 printf("initrd=%s", prefix);
2361
2362 for (i = 1; i < line->numElements; i++)
2363 printf("%s%s", line->elements[i].item, line->elements[i].indent);
2364 printf("\n");
2365 }
2366
2367 line = getLineByType(LT_TITLE, entry->lines);
2368 if (line) {
2369 printf("title=%s\n", line->elements[1].item);
2370 } else {
2371 char * title;
2372 line = getLineByType(LT_MENUENTRY, entry->lines);
2373 if (line) {
2374 title = grub2ExtractTitle(line);
2375 if (title)
2376 printf("title=%s\n", title);
2377 }
2378 }
2379
2380 for (j = 0, line = entry->lines; line; line = line->next) {
2381 if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2382 if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2383 printf("mbmodule%d=", j);
2384 else
2385 printf("mbmodule%d=%s", j, prefix);
2386
2387 for (i = 1; i < line->numElements; i++)
2388 printf("%s%s", line->elements[i].item, line->elements[i].indent);
2389 printf("\n");
2390 j++;
2391 }
2392 }
2393 }
2394
2395 int isSuseSystem(void) {
2396 const char * path;
2397 const static char default_path[] = "/etc/SuSE-release";
2398
2399 if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2400 path = default_path;
2401
2402 if (!access(path, R_OK))
2403 return 1;
2404 return 0;
2405 }
2406
2407 int isSuseGrubConf(const char * path) {
2408 FILE * grubConf;
2409 char * line = NULL;
2410 size_t len = 0, res = 0;
2411
2412 grubConf = fopen(path, "r");
2413 if (!grubConf) {
2414 dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2415 return 0;
2416 }
2417
2418 while ((res = getline(&line, &len, grubConf)) != -1) {
2419 if (!strncmp(line, "setup", 5)) {
2420 fclose(grubConf);
2421 free(line);
2422 return 1;
2423 }
2424 }
2425
2426 dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2427 path);
2428
2429 fclose(grubConf);
2430 free(line);
2431 return 0;
2432 }
2433
2434 int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2435 FILE * grubConf;
2436 char * line = NULL;
2437 size_t res = 0, len = 0;
2438
2439 if (!path) return 1;
2440 if (!lbaPtr) return 1;
2441
2442 grubConf = fopen(path, "r");
2443 if (!grubConf) return 1;
2444
2445 while ((res = getline(&line, &len, grubConf)) != -1) {
2446 if (line[res - 1] == '\n')
2447 line[res - 1] = '\0';
2448 else if (len > res)
2449 line[res] = '\0';
2450 else {
2451 line = realloc(line, res + 1);
2452 line[res] = '\0';
2453 }
2454
2455 if (!strncmp(line, "setup", 5)) {
2456 if (strstr(line, "--force-lba")) {
2457 *lbaPtr = 1;
2458 } else {
2459 *lbaPtr = 0;
2460 }
2461 dbgPrintf("lba: %i\n", *lbaPtr);
2462 break;
2463 }
2464 }
2465
2466 free(line);
2467 fclose(grubConf);
2468 return 0;
2469 }
2470
2471 int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2472 FILE * grubConf;
2473 char * line = NULL;
2474 size_t res = 0, len = 0;
2475 char * lastParamPtr = NULL;
2476 char * secLastParamPtr = NULL;
2477 char installDeviceNumber = '\0';
2478 char * bounds = NULL;
2479
2480 if (!path) return 1;
2481 if (!devicePtr) return 1;
2482
2483 grubConf = fopen(path, "r");
2484 if (!grubConf) return 1;
2485
2486 while ((res = getline(&line, &len, grubConf)) != -1) {
2487 if (strncmp(line, "setup", 5))
2488 continue;
2489
2490 if (line[res - 1] == '\n')
2491 line[res - 1] = '\0';
2492 else if (len > res)
2493 line[res] = '\0';
2494 else {
2495 line = realloc(line, res + 1);
2496 line[res] = '\0';
2497 }
2498
2499 lastParamPtr = bounds = line + res;
2500
2501 /* Last parameter in grub may be an optional IMAGE_DEVICE */
2502 while (!isspace(*lastParamPtr))
2503 lastParamPtr--;
2504 lastParamPtr++;
2505
2506 secLastParamPtr = lastParamPtr - 2;
2507 dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2508
2509 if (lastParamPtr + 3 > bounds) {
2510 dbgPrintf("lastParamPtr going over boundary");
2511 fclose(grubConf);
2512 free(line);
2513 return 1;
2514 }
2515 if (!strncmp(lastParamPtr, "(hd", 3))
2516 lastParamPtr += 3;
2517 dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2518
2519 /*
2520 * Second last parameter will decide wether last parameter is
2521 * an IMAGE_DEVICE or INSTALL_DEVICE
2522 */
2523 while (!isspace(*secLastParamPtr))
2524 secLastParamPtr--;
2525 secLastParamPtr++;
2526
2527 if (secLastParamPtr + 3 > bounds) {
2528 dbgPrintf("secLastParamPtr going over boundary");
2529 fclose(grubConf);
2530 free(line);
2531 return 1;
2532 }
2533 dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2534 if (!strncmp(secLastParamPtr, "(hd", 3)) {
2535 secLastParamPtr += 3;
2536 dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2537 installDeviceNumber = *secLastParamPtr;
2538 } else {
2539 installDeviceNumber = *lastParamPtr;
2540 }
2541
2542 *devicePtr = malloc(6);
2543 snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2544 dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2545 fclose(grubConf);
2546 free(line);
2547 return 0;
2548 }
2549
2550 free(line);
2551 fclose(grubConf);
2552 return 1;
2553 }
2554
2555 int grubGetBootFromDeviceMap(const char * device,
2556 char ** bootPtr) {
2557 FILE * deviceMap;
2558 char * line = NULL;
2559 size_t res = 0, len = 0;
2560 char * devicePtr;
2561 char * bounds = NULL;
2562 const char * path;
2563 const static char default_path[] = "/boot/grub/device.map";
2564
2565 if (!device) return 1;
2566 if (!bootPtr) return 1;
2567
2568 if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2569 path = default_path;
2570
2571 dbgPrintf("opening grub device.map file from: %s\n", path);
2572 deviceMap = fopen(path, "r");
2573 if (!deviceMap)
2574 return 1;
2575
2576 while ((res = getline(&line, &len, deviceMap)) != -1) {
2577 if (!strncmp(line, "#", 1))
2578 continue;
2579
2580 if (line[res - 1] == '\n')
2581 line[res - 1] = '\0';
2582 else if (len > res)
2583 line[res] = '\0';
2584 else {
2585 line = realloc(line, res + 1);
2586 line[res] = '\0';
2587 }
2588
2589 devicePtr = line;
2590 bounds = line + res;
2591
2592 while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2593 devicePtr++;
2594 dbgPrintf("device: %s\n", devicePtr);
2595
2596 if (!strncmp(devicePtr, device, strlen(device))) {
2597 devicePtr += strlen(device);
2598 while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2599 devicePtr++;
2600
2601 *bootPtr = strdup(devicePtr);
2602 break;
2603 }
2604 }
2605
2606 free(line);
2607 fclose(deviceMap);
2608 return 0;
2609 }
2610
2611 int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2612 char * grubDevice;
2613
2614 if (suseGrubConfGetInstallDevice(path, &grubDevice))
2615 dbgPrintf("error looking for grub installation device\n");
2616 else
2617 dbgPrintf("grubby installation device: %s\n", grubDevice);
2618
2619 if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2620 dbgPrintf("error looking for grub boot device\n");
2621 else
2622 dbgPrintf("grubby boot device: %s\n", *bootPtr);
2623
2624 free(grubDevice);
2625 return 0;
2626 }
2627
2628 int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2629 /*
2630 * This SuSE grub configuration file at this location is not your average
2631 * grub configuration file, but instead the grub commands used to setup
2632 * grub on that system.
2633 */
2634 const char * path;
2635 const static char default_path[] = "/etc/grub.conf";
2636
2637 if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2638 path = default_path;
2639
2640 if (!isSuseGrubConf(path)) return 1;
2641
2642 if (lbaPtr) {
2643 *lbaPtr = 0;
2644 if (suseGrubConfGetLba(path, lbaPtr))
2645 return 1;
2646 }
2647
2648 if (bootPtr) {
2649 *bootPtr = NULL;
2650 suseGrubConfGetBoot(path, bootPtr);
2651 }
2652
2653 return 0;
2654 }
2655
2656 int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
2657 FILE * in;
2658 char buf[1024];
2659 char * chptr;
2660 char * start;
2661 char * param;
2662
2663 in = fopen("/etc/conf.d/grub", "r");
2664 if (!in) return 1;
2665
2666 if (lbaPtr) *lbaPtr = 0;
2667 if (bootPtr) *bootPtr = NULL;
2668
2669 while (fgets(buf, sizeof(buf), in)) {
2670 start = buf;
2671 while (isspace(*start)) start++;
2672 if (*start == '#') continue;
2673
2674 chptr = strchr(start, '=');
2675 if (!chptr) continue;
2676 chptr--;
2677 while (*chptr && isspace(*chptr)) chptr--;
2678 chptr++;
2679 *chptr = '\0';
2680
2681 param = chptr + 1;
2682 while (*param && isspace(*param)) param++;
2683 if (*param == '=') {
2684 param++;
2685 while (*param && isspace(*param)) param++;
2686 }
2687
2688 chptr = param;
2689 while (*chptr && !isspace(*chptr)) chptr++;
2690 *chptr = '\0';
2691
2692 if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
2693 *lbaPtr = 1;
2694 else if (!strcmp(start, "boot") && bootPtr)
2695 *bootPtr = strdup(param);
2696 }
2697
2698 fclose(in);
2699
2700 return 0;
2701 }
2702
2703 void dumpSysconfigGrub(void) {
2704 char * boot = NULL;
2705 int lba;
2706
2707 if (isSuseSystem()) {
2708 if (parseSuseGrubConf(&lba, &boot)) {
2709 free(boot);
2710 return;
2711 }
2712 } else {
2713 if (parseSysconfigGrub(&lba, &boot)) {
2714 free(boot);
2715 return;
2716 }
2717 }
2718
2719 if (lba) printf("lba\n");
2720 if (boot) {
2721 printf("boot=%s\n", boot);
2722 free(boot);
2723 }
2724 }
2725
2726 int displayInfo(struct grubConfig * config, char * kernel,
2727 const char * prefix) {
2728 int i = 0;
2729 struct singleEntry * entry;
2730 struct singleLine * line;
2731
2732 entry = findEntryByPath(config, kernel, prefix, &i);
2733 if (!entry) {
2734 fprintf(stderr, _("grubby: kernel not found\n"));
2735 return 1;
2736 }
2737
2738 /* this is a horrible hack to support /etc/conf.d/grub; there must
2739 be a better way */
2740 if (config->cfi == &grubConfigType) {
2741 dumpSysconfigGrub();
2742 } else {
2743 line = getLineByType(LT_BOOT, config->theLines);
2744 if (line && line->numElements >= 1) {
2745 printf("boot=%s\n", line->elements[1].item);
2746 }
2747
2748 line = getLineByType(LT_LBA, config->theLines);
2749 if (line) printf("lba\n");
2750 }
2751
2752 displayEntry(entry, prefix, i);
2753
2754 i++;
2755 while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
2756 displayEntry(entry, prefix, i);
2757 i++;
2758 }
2759
2760 return 0;
2761 }
2762
2763 struct singleLine * addLineTmpl(struct singleEntry * entry,
2764 struct singleLine * tmplLine,
2765 struct singleLine * prevLine,
2766 const char * val,
2767 struct configFileInfo * cfi)
2768 {
2769 struct singleLine * newLine = lineDup(tmplLine);
2770
2771 if (isEfi && cfi == &grub2ConfigType) {
2772 enum lineType_e old = newLine->type;
2773 newLine->type = preferredLineType(newLine->type, cfi);
2774 if (old != newLine->type)
2775 newLine->elements[0].item = getKeyByType(newLine->type, cfi);
2776 }
2777
2778 if (val) {
2779 /* override the inherited value with our own.
2780 * This is a little weak because it only applies to elements[1]
2781 */
2782 if (newLine->numElements > 1)
2783 removeElement(newLine, 1);
2784 insertElement(newLine, val, 1, cfi);
2785
2786 /* but try to keep the rootspec from the template... sigh */
2787 if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) {
2788 char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2789 if (rootspec != NULL) {
2790 free(newLine->elements[1].item);
2791 newLine->elements[1].item =
2792 sdupprintf("%s%s", rootspec, val);
2793 }
2794 }
2795 }
2796
2797 dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2798 newLine->elements[0].item : "");
2799
2800 if (!entry->lines) {
2801 /* first one on the list */
2802 entry->lines = newLine;
2803 } else if (prevLine) {
2804 /* add after prevLine */
2805 newLine->next = prevLine->next;
2806 prevLine->next = newLine;
2807 }
2808
2809 return newLine;
2810 }
2811
2812 /* val may be NULL */
2813 struct singleLine * addLine(struct singleEntry * entry,
2814 struct configFileInfo * cfi,
2815 enum lineType_e type, char * defaultIndent,
2816 const char * val) {
2817 struct singleLine * line, * prev;
2818 struct keywordTypes * kw;
2819 struct singleLine tmpl;
2820
2821 /* NB: This function shouldn't allocate items on the heap, rather on the
2822 * stack since it calls addLineTmpl which will make copies.
2823 */
2824 if (type == LT_TITLE && cfi->titleBracketed) {
2825 /* we're doing a bracketed title (zipl) */
2826 tmpl.type = type;
2827 tmpl.numElements = 1;
2828 tmpl.elements = alloca(sizeof(*tmpl.elements));
2829 tmpl.elements[0].item = alloca(strlen(val)+3);
2830 sprintf(tmpl.elements[0].item, "[%s]", val);
2831 tmpl.elements[0].indent = "";
2832 val = NULL;
2833 } else if (type == LT_MENUENTRY) {
2834 char *lineend = "--class gnu-linux --class gnu --class os {";
2835 if (!val) {
2836 fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2837 abort();
2838 }
2839 kw = getKeywordByType(type, cfi);
2840 if (!kw) {
2841 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2842 abort();
2843 }
2844 tmpl.indent = "";
2845 tmpl.type = type;
2846 tmpl.numElements = 3;
2847 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2848 tmpl.elements[0].item = kw->key;
2849 tmpl.elements[0].indent = alloca(2);
2850 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2851 tmpl.elements[1].item = (char *)val;
2852 tmpl.elements[1].indent = alloca(2);
2853 sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2854 tmpl.elements[2].item = alloca(strlen(lineend)+1);
2855 strcpy(tmpl.elements[2].item, lineend);
2856 tmpl.elements[2].indent = "";
2857 } else {
2858 kw = getKeywordByType(type, cfi);
2859 if (!kw) {
2860 fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2861 abort();
2862 }
2863 tmpl.type = type;
2864 tmpl.numElements = val ? 2 : 1;
2865 tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2866 tmpl.elements[0].item = kw->key;
2867 tmpl.elements[0].indent = alloca(2);
2868 sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2869 if (val) {
2870 tmpl.elements[1].item = (char *)val;
2871 tmpl.elements[1].indent = "";
2872 }
2873 }
2874
2875 /* The last non-empty line gives us the indention to us and the line
2876 to insert after. Note that comments are considered empty lines, which
2877 may not be ideal? If there are no lines or we are looking at the
2878 first line, we use defaultIndent (the first line is normally indented
2879 differently from the rest) */
2880 for (line = entry->lines, prev = NULL; line; line = line->next) {
2881 if (line->numElements) prev = line;
2882 /* fall back on the last line if prev isn't otherwise set */
2883 if (!line->next && !prev) prev = line;
2884 }
2885
2886 struct singleLine *menuEntry;
2887 menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2888 if (tmpl.type == LT_ENTRY_END) {
2889 if (menuEntry)
2890 tmpl.indent = menuEntry->indent;
2891 else
2892 tmpl.indent = defaultIndent ?: "";
2893 } else if (tmpl.type != LT_MENUENTRY) {
2894 if (menuEntry)
2895 tmpl.indent = "\t";
2896 else if (prev == entry->lines)
2897 tmpl.indent = defaultIndent ?: "";
2898 else
2899 tmpl.indent = prev->indent;
2900 }
2901
2902 return addLineTmpl(entry, &tmpl, prev, val, cfi);
2903 }
2904
2905 void removeLine(struct singleEntry * entry, struct singleLine * line) {
2906 struct singleLine * prev;
2907 int i;
2908
2909 for (i = 0; i < line->numElements; i++) {
2910 free(line->elements[i].item);
2911 free(line->elements[i].indent);
2912 }
2913 free(line->elements);
2914 free(line->indent);
2915
2916 if (line == entry->lines) {
2917 entry->lines = line->next;
2918 } else {
2919 prev = entry->lines;
2920 while (prev->next != line) prev = prev->next;
2921 prev->next = line->next;
2922 }
2923
2924 free(line);
2925 }
2926
2927 static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2928 {
2929 struct singleLine newLine = {
2930 .indent = tmplLine->indent,
2931 .type = tmplLine->type,
2932 .next = tmplLine->next,
2933 };
2934 int firstQuotedItem = -1;
2935 int quoteLen = 0;
2936 int j;
2937 int element = 0;
2938 char *c;
2939
2940 c = malloc(strlen(tmplLine->elements[0].item) + 1);
2941 strcpy(c, tmplLine->elements[0].item);
2942 insertElement(&newLine, c, element++, cfi);
2943 free(c);
2944 c = NULL;
2945
2946 for (j = 1; j < tmplLine->numElements; j++) {
2947 if (firstQuotedItem == -1) {
2948 quoteLen += strlen(tmplLine->elements[j].item);
2949
2950 if (isquote(tmplLine->elements[j].item[0])) {
2951 firstQuotedItem = j;
2952 quoteLen += strlen(tmplLine->elements[j].indent);
2953 } else {
2954 c = malloc(quoteLen + 1);
2955 strcpy(c, tmplLine->elements[j].item);
2956 insertElement(&newLine, c, element++, cfi);
2957 free(c);
2958 quoteLen = 0;
2959 }
2960 } else {
2961 int itemlen = strlen(tmplLine->elements[j].item);
2962 quoteLen += itemlen;
2963 quoteLen += strlen(tmplLine->elements[j].indent);
2964
2965 if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2966 c = malloc(quoteLen + 1);
2967 c[0] = '\0';
2968 for (int i = firstQuotedItem; i < j+1; i++) {
2969 strcat(c, tmplLine->elements[i].item);
2970 strcat(c, tmplLine->elements[i].indent);
2971 }
2972 insertElement(&newLine, c, element++, cfi);
2973 free(c);
2974
2975 firstQuotedItem = -1;
2976 quoteLen = 0;
2977 }
2978 }
2979 }
2980 while (tmplLine->numElements)
2981 removeElement(tmplLine, 0);
2982 if (tmplLine->elements)
2983 free(tmplLine->elements);
2984
2985 tmplLine->numElements = newLine.numElements;
2986 tmplLine->elements = newLine.elements;
2987 }
2988
2989 static void insertElement(struct singleLine * line,
2990 const char * item, int insertHere,
2991 struct configFileInfo * cfi)
2992 {
2993 struct keywordTypes * kw;
2994 char indent[2] = "";
2995
2996 /* sanity check */
2997 if (insertHere > line->numElements) {
2998 dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2999 insertHere, line->numElements);
3000 insertHere = line->numElements;
3001 }
3002
3003 line->elements = realloc(line->elements, (line->numElements + 1) *
3004 sizeof(*line->elements));
3005 memmove(&line->elements[insertHere+1],
3006 &line->elements[insertHere],
3007 (line->numElements - insertHere) *
3008 sizeof(*line->elements));
3009 line->elements[insertHere].item = strdup(item);
3010
3011 kw = getKeywordByType(line->type, cfi);
3012
3013 if (line->numElements == 0) {
3014 indent[0] = '\0';
3015 } else if (insertHere == 0) {
3016 indent[0] = kw->nextChar;
3017 } else if (kw->separatorChar != '\0') {
3018 indent[0] = kw->separatorChar;
3019 } else {
3020 indent[0] = ' ';
3021 }
3022
3023 if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
3024 /* move the end-of-line forward */
3025 line->elements[insertHere].indent =
3026 line->elements[insertHere-1].indent;
3027 line->elements[insertHere-1].indent = strdup(indent);
3028 } else {
3029 line->elements[insertHere].indent = strdup(indent);
3030 }
3031
3032 line->numElements++;
3033
3034 dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3035 line->elements[0].item,
3036 line->elements[insertHere].item,
3037 line->elements[insertHere].indent,
3038 insertHere);
3039 }
3040
3041 static void removeElement(struct singleLine * line, int removeHere) {
3042 int i;
3043
3044 /* sanity check */
3045 if (removeHere >= line->numElements) return;
3046
3047 dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3048 removeHere, line->elements[removeHere].item);
3049
3050 free(line->elements[removeHere].item);
3051
3052 if (removeHere > 1) {
3053 /* previous argument gets this argument's post-indentation */
3054 free(line->elements[removeHere-1].indent);
3055 line->elements[removeHere-1].indent =
3056 line->elements[removeHere].indent;
3057 } else {
3058 free(line->elements[removeHere].indent);
3059 }
3060
3061 /* now collapse the array, but don't bother to realloc smaller */
3062 for (i = removeHere; i < line->numElements - 1; i++)
3063 line->elements[i] = line->elements[i + 1];
3064
3065 line->numElements--;
3066 }
3067
3068 int argMatch(const char * one, const char * two) {
3069 char * first, * second;
3070 char * chptr;
3071
3072 first = strcpy(alloca(strlen(one) + 1), one);
3073 second = strcpy(alloca(strlen(two) + 1), two);
3074
3075 chptr = strchr(first, '=');
3076 if (chptr) *chptr = '\0';
3077
3078 chptr = strchr(second, '=');
3079 if (chptr) *chptr = '\0';
3080
3081 return strcmp(first, second);
3082 }
3083
3084 int updateActualImage(struct grubConfig * cfg, const char * image,
3085 const char * prefix, const char * addArgs,
3086 const char * removeArgs, int multibootArgs) {
3087 struct singleEntry * entry;
3088 struct singleLine * line, * rootLine;
3089 int index = 0;
3090 int i, k;
3091 const char ** newArgs, ** oldArgs;
3092 const char ** arg;
3093 int useKernelArgs, useRoot;
3094 int firstElement;
3095 int *usedElements;
3096 int doreplace;
3097
3098 if (!image) return 0;
3099
3100 if (!addArgs) {
3101 newArgs = malloc(sizeof(*newArgs));
3102 *newArgs = NULL;
3103 } else {
3104 if (poptParseArgvString(addArgs, NULL, &newArgs)) {
3105 fprintf(stderr,
3106 _("grubby: error separating arguments '%s'\n"), addArgs);
3107 return 1;
3108 }
3109 }
3110
3111 if (!removeArgs) {
3112 oldArgs = malloc(sizeof(*oldArgs));
3113 *oldArgs = NULL;
3114 } else {
3115 if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
3116 fprintf(stderr,
3117 _("grubby: error separating arguments '%s'\n"), removeArgs);
3118 free(newArgs);
3119 return 1;
3120 }
3121 }
3122
3123
3124 useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3125 && (!multibootArgs || cfg->cfi->mbConcatArgs));
3126
3127 useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3128 && !multibootArgs);
3129
3130 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3131
3132 if (multibootArgs && !entry->multiboot)
3133 continue;
3134
3135 /* Determine where to put the args. If this config supports
3136 * LT_KERNELARGS, use that. Otherwise use
3137 * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3138 */
3139 if (useKernelArgs) {
3140 line = getLineByType(LT_KERNELARGS, entry->lines);
3141 if (!line) {
3142 /* no LT_KERNELARGS, need to add it */
3143 line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3144 cfg->secondaryIndent, NULL);
3145 }
3146 firstElement = 1;
3147
3148 } else if (multibootArgs) {
3149 line = getLineByType(LT_HYPER, entry->lines);
3150 if (!line) {
3151 /* a multiboot entry without LT_HYPER? */
3152 continue;
3153 }
3154 firstElement = 2;
3155
3156 } else {
3157 line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3158 if (!line) {
3159 /* no LT_KERNEL or LT_MBMODULE in this entry? */
3160 continue;
3161 }
3162 firstElement = 2;
3163 }
3164
3165 /* handle the elilo case which does:
3166 * append="hypervisor args -- kernel args"
3167 */
3168 if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3169 /* this is a multiboot entry, make sure there's
3170 * -- on the args line
3171 */
3172 for (i = firstElement; i < line->numElements; i++) {
3173 if (!strcmp(line->elements[i].item, "--"))
3174 break;
3175 }
3176 if (i == line->numElements) {
3177 /* assume all existing args are kernel args,
3178 * prepend -- to make it official
3179 */
3180 insertElement(line, "--", firstElement, cfg->cfi);
3181 i = firstElement;
3182 }
3183 if (!multibootArgs) {
3184 /* kernel args start after the -- */
3185 firstElement = i + 1;
3186 }
3187 } else if (cfg->cfi->mbConcatArgs) {
3188 /* this is a non-multiboot entry, remove hyper args */
3189 for (i = firstElement; i < line->numElements; i++) {
3190 if (!strcmp(line->elements[i].item, "--"))
3191 break;
3192 }
3193 if (i < line->numElements) {
3194 /* remove args up to -- */
3195 while (strcmp(line->elements[firstElement].item, "--"))
3196 removeElement(line, firstElement);
3197 /* remove -- */
3198 removeElement(line, firstElement);
3199 }
3200 }
3201
3202 usedElements = calloc(line->numElements, sizeof(*usedElements));
3203
3204 for (k = 0, arg = newArgs; *arg; arg++, k++) {
3205
3206 doreplace = 1;
3207 for (i = firstElement; i < line->numElements; i++) {
3208 if (multibootArgs && cfg->cfi->mbConcatArgs &&
3209 !strcmp(line->elements[i].item, "--"))
3210 {
3211 /* reached the end of hyper args, insert here */
3212 doreplace = 0;
3213 break;
3214 }
3215 if (usedElements[i])
3216 continue;
3217 if (!argMatch(line->elements[i].item, *arg)) {
3218 usedElements[i]=1;
3219 break;
3220 }
3221 }
3222
3223 if (i < line->numElements && doreplace) {
3224 /* direct replacement */
3225 free(line->elements[i].item);
3226 line->elements[i].item = strdup(*arg);
3227
3228 } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3229 /* root= replacement */
3230 rootLine = getLineByType(LT_ROOT, entry->lines);
3231 if (rootLine) {
3232 free(rootLine->elements[1].item);
3233 rootLine->elements[1].item = strdup(*arg + 5);
3234 } else {
3235 rootLine = addLine(entry, cfg->cfi, LT_ROOT,
3236 cfg->secondaryIndent, *arg + 5);
3237 }
3238 }
3239
3240 else {
3241 /* insert/append */
3242 insertElement(line, *arg, i, cfg->cfi);
3243 usedElements = realloc(usedElements, line->numElements *
3244 sizeof(*usedElements));
3245 memmove(&usedElements[i + 1], &usedElements[i],
3246 line->numElements - i - 1);
3247 usedElements[i] = 1;
3248
3249 /* if we updated a root= here even though there is a
3250 LT_ROOT available we need to remove the LT_ROOT entry
3251 (this will happen if we switch from a device to a label) */
3252 if (useRoot && !strncmp(*arg, "root=", 5)) {
3253 rootLine = getLineByType(LT_ROOT, entry->lines);
3254 if (rootLine)
3255 removeLine(entry, rootLine);
3256 }
3257 }
3258 }
3259
3260 free(usedElements);
3261
3262 for (arg = oldArgs; *arg; arg++) {
3263 for (i = firstElement; i < line->numElements; i++) {
3264 if (multibootArgs && cfg->cfi->mbConcatArgs &&
3265 !strcmp(line->elements[i].item, "--"))
3266 /* reached the end of hyper args, stop here */
3267 break;
3268 if (!argMatch(line->elements[i].item, *arg)) {
3269 removeElement(line, i);
3270 break;
3271 }
3272 }
3273 /* handle removing LT_ROOT line too */
3274 if (useRoot && !strncmp(*arg, "root=", 5)) {
3275 rootLine = getLineByType(LT_ROOT, entry->lines);
3276 if (rootLine)
3277 removeLine(entry, rootLine);
3278 }
3279 }
3280
3281 if (line->numElements == 1) {
3282 /* don't need the line at all (note it has to be a
3283 LT_KERNELARGS for this to happen */
3284 removeLine(entry, line);
3285 }
3286 }
3287
3288 free(newArgs);
3289 free(oldArgs);
3290
3291 return 0;
3292 }
3293
3294 int updateImage(struct grubConfig * cfg, const char * image,
3295 const char * prefix, const char * addArgs,
3296 const char * removeArgs,
3297 const char * addMBArgs, const char * removeMBArgs) {
3298 int rc = 0;
3299
3300 if (!image) return rc;
3301
3302 /* update the main args first... */
3303 if (addArgs || removeArgs)
3304 rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
3305 if (rc) return rc;
3306
3307 /* and now any multiboot args */
3308 if (addMBArgs || removeMBArgs)
3309 rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
3310 return rc;
3311 }
3312
3313 int addMBInitrd(struct grubConfig * cfg, const char *newMBKernel,
3314 const char * image, const char * prefix, const char * initrd,
3315 const char * title) {
3316 struct singleEntry * entry;
3317 struct singleLine * line, * kernelLine, *endLine = NULL;
3318 int index = 0;
3319
3320 if (!image) return 0;
3321
3322 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3323 kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3324 if (!kernelLine) continue;
3325
3326 /* if title is supplied, the entry's title must match it. */
3327 if (title) {
3328 char *linetitle;
3329
3330 line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3331 if (!line)
3332 continue;
3333
3334 linetitle = extractTitle(line);
3335 if (!linetitle)
3336 continue;
3337 if (strcmp(title, linetitle)) {
3338 free(linetitle);
3339 continue;
3340 }
3341 free(linetitle);
3342 }
3343
3344 if (prefix) {
3345 int prefixLen = strlen(prefix);
3346 if (!strncmp(initrd, prefix, prefixLen))
3347 initrd += prefixLen;
3348 }
3349 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3350 if (endLine)
3351 removeLine(entry, endLine);
3352 line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),
3353 kernelLine->indent, initrd);
3354 if (!line)
3355 return 1;
3356 if (endLine) {
3357 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3358 if (!line)
3359 return 1;
3360 }
3361
3362 break;
3363 }
3364
3365 return 0;
3366 }
3367
3368 int updateInitrd(struct grubConfig * cfg, const char * image,
3369 const char * prefix, const char * initrd, const char * title) {
3370 struct singleEntry * entry;
3371 struct singleLine * line, * kernelLine, *endLine = NULL;
3372 int index = 0;
3373
3374 if (!image) return 0;
3375
3376 for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3377 kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3378 if (!kernelLine) continue;
3379
3380 /* if title is supplied, the entry's title must match it. */
3381 if (title) {
3382 char *linetitle;
3383
3384 line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3385 if (!line)
3386 continue;
3387
3388 linetitle = extractTitle(line);
3389 if (!linetitle)
3390 continue;
3391 if (strcmp(title, linetitle)) {
3392 free(linetitle);
3393 continue;
3394 }
3395 free(linetitle);
3396 }
3397
3398 line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
3399 if (line)
3400 removeLine(entry, line);
3401 if (prefix) {
3402 int prefixLen = strlen(prefix);
3403 if (!strncmp(initrd, prefix, prefixLen))
3404 initrd += prefixLen;
3405 }
3406 endLine = getLineByType(LT_ENTRY_END, entry->lines);
3407 if (endLine)
3408 removeLine(entry, endLine);
3409 enum lineType_e lt;
3410 switch(kernelLine->type) {
3411 case LT_KERNEL:
3412 lt = LT_INITRD;
3413 break;
3414 case LT_KERNEL_EFI:
3415 lt = LT_INITRD_EFI;
3416 break;
3417 case LT_KERNEL_16:
3418 lt = LT_INITRD_16;
3419 break;
3420 default:
3421 lt = preferredLineType(LT_INITRD, cfg->cfi);
3422 }
3423 line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3424 if (!line)
3425 return 1;
3426 if (endLine) {
3427 line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3428 if (!line)
3429 return 1;
3430 }
3431
3432 break;
3433 }
3434
3435 return 0;
3436 }
3437
3438 int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3439 int fd;
3440 unsigned char bootSect[512];
3441 int offset;
3442
3443 fd = open(device, O_RDONLY);
3444 if (fd < 0) {
3445 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3446 device, strerror(errno));
3447 return 1;
3448 }
3449
3450 if (read(fd, bootSect, 512) != 512) {
3451 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3452 device, strerror(errno));
3453 return 1;
3454 }
3455 close(fd);
3456
3457 /* first three bytes should match, a jmp short should be in there */
3458 if (memcmp(boot, bootSect, 3))
3459 return 0;
3460
3461 if (boot[1] == JMP_SHORT_OPCODE) {
3462 offset = boot[2] + 2;
3463 } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3464 offset = (boot[3] << 8) + boot[2] + 2;
3465 } else if (boot[0] == JMP_SHORT_OPCODE) {
3466 offset = boot[1] + 2;
3467 /*
3468 * it looks like grub, when copying stage1 into the mbr, patches stage1
3469 * right after the JMP location, replacing other instructions such as
3470 * JMPs for NOOPs. So, relax the check a little bit by skipping those
3471 * different bytes.
3472 */
3473 if ((bootSect[offset + 1] == NOOP_OPCODE)
3474 && (bootSect[offset + 2] == NOOP_OPCODE)) {
3475 offset = offset + 3;
3476 }
3477 } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3478 offset = (boot[2] << 8) + boot[1] + 2;
3479 } else {
3480 return 0;
3481 }
3482
3483 if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3484 return 0;
3485
3486 return 2;
3487 }
3488
3489 int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
3490 int fd;
3491 char buf[65536];
3492 char * end;
3493 char * chptr;
3494 char * chptr2;
3495 int rc;
3496
3497 /* it's on raid; we need to parse /proc/mdstat and check all of the
3498 *raw* devices listed in there */
3499
3500 if (!strncmp(mdDev, "/dev/", 5))
3501 mdDev += 5;
3502
3503 if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3504 fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3505 strerror(errno));
3506 return 2;
3507 }
3508
3509 rc = read(fd, buf, sizeof(buf) - 1);
3510 if (rc < 0 || rc == (sizeof(buf) - 1)) {
3511 fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3512 strerror(errno));
3513 close(fd);
3514 return 2;
3515 }
3516 close(fd);
3517 buf[rc] = '\0';
3518
3519 chptr = buf;
3520 while (*chptr) {
3521 end = strchr(chptr, '\n');
3522 if (!end) break;
3523 *end = '\0';
3524
3525 if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3526 chptr[strlen(mdDev)] == ' ') {
3527
3528 /* found the device */
3529 while (*chptr && *chptr != ':') chptr++;
3530 chptr++;
3531 while (*chptr && isspace(*chptr)) chptr++;
3532
3533 /* skip the "active" bit */
3534 while (*chptr && !isspace(*chptr)) chptr++;
3535 while (*chptr && isspace(*chptr)) chptr++;
3536
3537 /* skip the raid level */
3538 while (*chptr && !isspace(*chptr)) chptr++;
3539 while (*chptr && isspace(*chptr)) chptr++;
3540
3541 /* everything else is partition stuff */
3542 while (*chptr) {
3543 chptr2 = chptr;
3544 while (*chptr2 && *chptr2 != '[') chptr2++;
3545 if (!*chptr2) break;
3546
3547 /* yank off the numbers at the end */
3548 chptr2--;
3549 while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
3550 chptr2++;
3551 *chptr2 = '\0';
3552
3553 /* Better, now we need the /dev/ back. We're done with
3554 * everything before this point, so we can just put
3555 * the /dev/ part there. There will always be room. */
3556 memcpy(chptr - 5, "/dev/", 5);
3557 rc = checkDeviceBootloader(chptr - 5, boot);
3558 if (rc != 2) {
3559 return rc;
3560 }
3561
3562 chptr = chptr2 + 1;
3563 /* skip the [11] bit */
3564 while (*chptr && !isspace(*chptr)) chptr++;
3565 /* and move to the next one */
3566 while (*chptr && isspace(*chptr)) chptr++;
3567 }
3568
3569 /* we're good to go */
3570 return 2;
3571 }
3572
3573 chptr = end + 1;
3574 }
3575
3576 fprintf(stderr,
3577 _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3578 mdDev);
3579 return 0;
3580 }
3581
3582 int checkForLilo(struct grubConfig * config) {
3583 int fd;
3584 unsigned char boot[512];
3585 struct singleLine * line;
3586
3587 for (line = config->theLines; line; line = line->next)
3588 if (line->type == LT_BOOT) break;
3589
3590 if (!line) {
3591 fprintf(stderr,
3592 _("grubby: no boot line found in lilo configuration\n"));
3593 return 1;
3594 }
3595
3596 if (line->numElements != 2) return 1;
3597
3598 fd = open("/boot/boot.b", O_RDONLY);
3599 if (fd < 0) {
3600 fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3601 "/boot/boot.b", strerror(errno));
3602 return 1;
3603 }
3604
3605 if (read(fd, boot, 512) != 512) {
3606 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3607 "/boot/boot.b", strerror(errno));
3608 return 1;
3609 }
3610 close(fd);
3611
3612 if (!strncmp("/dev/md", line->elements[1].item, 7))
3613 return checkLiloOnRaid(line->elements[1].item, boot);
3614
3615 return checkDeviceBootloader(line->elements[1].item, boot);
3616 }
3617
3618 int checkForGrub2(struct grubConfig * config) {
3619 if (!access("/etc/grub.d/", R_OK))
3620 return 2;
3621
3622 return 1;
3623 }
3624
3625 int checkForGrub(struct grubConfig * config) {
3626 int fd;
3627 unsigned char bootSect[512];
3628 char * boot;
3629 int onSuse = isSuseSystem();
3630
3631
3632 if (onSuse) {
3633 if (parseSuseGrubConf(NULL, &boot))
3634 return 0;
3635 } else {
3636 if (parseSysconfigGrub(NULL, &boot))
3637 return 0;
3638 }
3639
3640 /* assume grub is not installed -- not an error condition */
3641 if (!boot)
3642 return 0;
3643
3644 fd = open("/boot/grub/stage1", O_RDONLY);
3645 if (fd < 0)
3646 /* this doesn't exist if grub hasn't been installed */
3647 return 0;
3648
3649 if (read(fd, bootSect, 512) != 512) {
3650 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3651 "/boot/grub/stage1", strerror(errno));
3652 close(fd);
3653 return 1;
3654 }
3655 close(fd);
3656
3657 /* The more elaborate checks do not work on SuSE. The checks done
3658 * seem to be reasonble (at least for now), so just return success
3659 */
3660 if (onSuse)
3661 return 2;
3662
3663 return checkDeviceBootloader(boot, bootSect);
3664 }
3665
3666 int checkForExtLinux(struct grubConfig * config) {
3667 int fd;
3668 unsigned char bootSect[512];
3669 char * boot;
3670 char executable[] = "/boot/extlinux/extlinux";
3671
3672 printf("entered: checkForExtLinux()\n");
3673
3674 if (parseSysconfigGrub(NULL, &boot))
3675 return 0;
3676
3677 /* assume grub is not installed -- not an error condition */
3678 if (!boot)
3679 return 0;
3680
3681 fd = open(executable, O_RDONLY);
3682 if (fd < 0)
3683 /* this doesn't exist if grub hasn't been installed */
3684 return 0;
3685
3686 if (read(fd, bootSect, 512) != 512) {
3687 fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3688 executable, strerror(errno));
3689 return 1;
3690 }
3691 close(fd);
3692
3693 return checkDeviceBootloader(boot, bootSect);
3694 }
3695
3696 int checkForYaboot(struct grubConfig * config) {
3697 /*
3698 * This is a simplistic check that we consider good enough for own puporses
3699 *
3700 * If we were to properly check if yaboot is *installed* we'd need to:
3701 * 1) get the system boot device (LT_BOOT)
3702 * 2) considering it's a raw filesystem, check if the yaboot binary matches
3703 * the content on the boot device
3704 * 3) if not, copy the binary to a temporary file and run "addnote" on it
3705 * 4) check again if binary and boot device contents match
3706 */
3707 if (!access("/etc/yaboot.conf", R_OK))
3708 return 2;
3709
3710 return 1;
3711 }
3712
3713 int checkForElilo(struct grubConfig * config) {
3714 if (!access("/etc/elilo.conf", R_OK))
3715 return 2;
3716
3717 return 1;
3718 }
3719
3720 static char * getRootSpecifier(char * str) {
3721 char * idx, * rootspec = NULL;
3722
3723 if (*str == '(') {
3724 idx = rootspec = strdup(str);
3725 while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
3726 *(++idx) = '\0';
3727 }
3728 return rootspec;
3729 }
3730
3731 static char * getInitrdVal(struct grubConfig * config,
3732 const char * prefix, struct singleLine *tmplLine,
3733 const char * newKernelInitrd,
3734 const char ** extraInitrds, int extraInitrdCount)
3735 {
3736 char *initrdVal, *end;
3737 int i;
3738 size_t totalSize;
3739 size_t prefixLen;
3740 char separatorChar;
3741
3742 prefixLen = strlen(prefix);
3743 totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3744
3745 for (i = 0; i < extraInitrdCount; i++) {
3746 totalSize += sizeof(separatorChar);
3747 totalSize += strlen(extraInitrds[i]) - prefixLen;
3748 }
3749
3750 initrdVal = end = malloc(totalSize);
3751
3752 end = stpcpy (end, newKernelInitrd + prefixLen);
3753
3754 separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3755 for (i = 0; i < extraInitrdCount; i++) {
3756 const char *extraInitrd;
3757 int j;
3758
3759 extraInitrd = extraInitrds[i] + prefixLen;
3760 /* Don't add entries that are already there */
3761 if (tmplLine != NULL) {
3762 for (j = 2; j < tmplLine->numElements; j++)
3763 if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3764 break;
3765
3766 if (j != tmplLine->numElements)
3767 continue;
3768 }
3769
3770 *end++ = separatorChar;
3771 end = stpcpy(end, extraInitrd);
3772 }
3773
3774 return initrdVal;
3775 }
3776
3777 int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3778 const char * prefix,
3779 const char * newKernelPath, const char * newKernelTitle,
3780 const char * newKernelArgs, const char * newKernelInitrd,
3781 const char ** extraInitrds, int extraInitrdCount,
3782 const char * newMBKernel, const char * newMBKernelArgs,
3783 const char * newDevTreePath) {
3784 struct singleEntry * new;
3785 struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3786 int needs;
3787 char * chptr;
3788
3789 if (!newKernelPath) return 0;
3790
3791 /* if the newKernelTitle is too long silently munge it into something
3792 * we can live with. truncating is first check, then we'll just mess with
3793 * it until it looks better */
3794 if (config->cfi->maxTitleLength &&
3795 (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
3796 char * buf = alloca(config->cfi->maxTitleLength + 7);
3797 char * numBuf = alloca(config->cfi->maxTitleLength + 1);
3798 int i = 1;
3799
3800 sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
3801 while (findEntryByPath(config, buf, NULL, NULL)) {
3802 sprintf(numBuf, "%d", i++);
3803 strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
3804 }
3805
3806 newKernelTitle = buf + 6;
3807 }
3808
3809 new = malloc(sizeof(*new));
3810 new->skip = 0;
3811 new->multiboot = 0;
3812 new->next = config->entries;
3813 new->lines = NULL;
3814 config->entries = new;
3815
3816 /* copy/update from the template */
3817 needs = NEED_KERNEL | NEED_TITLE;
3818 if (newKernelInitrd)
3819 needs |= NEED_INITRD;
3820 if (newMBKernel) {
3821 needs |= NEED_MB;
3822 new->multiboot = 1;
3823 }
3824 if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
3825 needs |= NEED_DEVTREE;
3826
3827 if (template) {
3828 for (masterLine = template->lines;
3829 masterLine && (tmplLine = lineDup(masterLine));
3830 lineFree(tmplLine), masterLine = masterLine->next)
3831 {
3832 dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3833
3834 /* skip comments */
3835 chptr = tmplLine->indent;
3836 while (*chptr && isspace(*chptr)) chptr++;
3837 if (*chptr == '#') continue;
3838
3839 if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3840 if (!template->multiboot && (needs & NEED_MB)) {
3841 /* it's not a multiboot template and this is the kernel
3842 * line. Try to be intelligent about inserting the
3843 * hypervisor at the same time.
3844 */
3845 if (config->cfi->mbHyperFirst) {
3846 /* insert the hypervisor first */
3847 newLine = addLine(new, config->cfi, LT_HYPER,
3848 tmplLine->indent,
3849 newMBKernel + strlen(prefix));
3850 /* set up for adding the kernel line */
3851 free(tmplLine->indent);
3852 tmplLine->indent = strdup(config->secondaryIndent);
3853 needs &= ~NEED_MB;
3854 }
3855 if (needs & NEED_KERNEL) {
3856 /* use addLineTmpl to preserve line elements,
3857 * otherwise we could just call addLine. Unfortunately
3858 * this means making some changes to the template
3859 * such as the indent change above and the type
3860 * change below.
3861 */
3862 struct keywordTypes * mbm_kw =
3863 getKeywordByType(LT_MBMODULE, config->cfi);
3864 if (mbm_kw) {
3865 tmplLine->type = LT_MBMODULE;
3866 free(tmplLine->elements[0].item);
3867 tmplLine->elements[0].item = strdup(mbm_kw->key);
3868 }
3869 newLine = addLineTmpl(new, tmplLine, newLine,
3870 newKernelPath + strlen(prefix), config->cfi);
3871 needs &= ~NEED_KERNEL;
3872 }
3873 if (needs & NEED_MB) { /* !mbHyperFirst */
3874 newLine = addLine(new, config->cfi, LT_HYPER,
3875 config->secondaryIndent,
3876 newMBKernel + strlen(prefix));
3877 needs &= ~NEED_MB;
3878 }
3879 } else if (needs & NEED_KERNEL) {
3880 newLine = addLineTmpl(new, tmplLine, newLine,
3881 newKernelPath + strlen(prefix), config->cfi);
3882 needs &= ~NEED_KERNEL;
3883 }
3884
3885 } else if (tmplLine->type == LT_HYPER &&
3886 tmplLine->numElements >= 2) {
3887 if (needs & NEED_MB) {
3888 newLine = addLineTmpl(new, tmplLine, newLine,
3889 newMBKernel + strlen(prefix), config->cfi);
3890 needs &= ~NEED_MB;
3891 }
3892
3893 } else if (tmplLine->type == LT_MBMODULE &&
3894 tmplLine->numElements >= 2) {
3895 if (new->multiboot) {
3896 if (needs & NEED_KERNEL) {
3897 newLine = addLineTmpl(new, tmplLine, newLine,
3898 newKernelPath +
3899 strlen(prefix), config->cfi);
3900 needs &= ~NEED_KERNEL;
3901 } else if (config->cfi->mbInitRdIsModule &&
3902 (needs & NEED_INITRD)) {
3903 char *initrdVal;
3904 initrdVal = getInitrdVal(config, prefix, tmplLine,
3905 newKernelInitrd, extraInitrds,
3906 extraInitrdCount);
3907 newLine = addLineTmpl(new, tmplLine, newLine,
3908 initrdVal, config->cfi);
3909 free(initrdVal);
3910 needs &= ~NEED_INITRD;
3911 }
3912 } else if (needs & NEED_KERNEL) {
3913 /* template is multi but new is not,
3914 * insert the kernel in the first module slot
3915 */
3916 tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3917 free(tmplLine->elements[0].item);
3918 tmplLine->elements[0].item =
3919 strdup(getKeywordByType(tmplLine->type,
3920 config->cfi)->key);
3921 newLine = addLineTmpl(new, tmplLine, newLine,
3922 newKernelPath + strlen(prefix),
3923 config->cfi);
3924 needs &= ~NEED_KERNEL;
3925 } else if (needs & NEED_INITRD) {
3926 char *initrdVal;
3927 /* template is multi but new is not,
3928 * insert the initrd in the second module slot
3929 */
3930 tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3931 free(tmplLine->elements[0].item);
3932 tmplLine->elements[0].item =
3933 strdup(getKeywordByType(tmplLine->type,
3934 config->cfi)->key);
3935 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3936 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3937 free(initrdVal);
3938 needs &= ~NEED_INITRD;
3939 }
3940
3941 } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3942 if (needs & NEED_INITRD &&
3943 new->multiboot && !template->multiboot &&
3944 config->cfi->mbInitRdIsModule) {
3945 /* make sure we don't insert the module initrd
3946 * before the module kernel... if we don't do it here,
3947 * it will be inserted following the template.
3948 */
3949 if (!needs & NEED_KERNEL) {
3950 char *initrdVal;
3951
3952 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3953 newLine = addLine(new, config->cfi, LT_MBMODULE,
3954 config->secondaryIndent,
3955 initrdVal);
3956 free(initrdVal);
3957 needs &= ~NEED_INITRD;
3958 }
3959 } else if (needs & NEED_INITRD) {
3960 char *initrdVal;
3961 initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3962 newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3963 free(initrdVal);
3964 needs &= ~NEED_INITRD;
3965 }
3966
3967 } else if (tmplLine->type == LT_MENUENTRY &&
3968 (needs & NEED_TITLE)) {
3969 requote(tmplLine, config->cfi);
3970 char *nkt = malloc(strlen(newKernelTitle)+3);
3971 strcpy(nkt, "'");
3972 strcat(nkt, newKernelTitle);
3973 strcat(nkt, "'");
3974 newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3975 free(nkt);
3976 needs &= ~NEED_TITLE;
3977 } else if (tmplLine->type == LT_TITLE &&
3978 (needs & NEED_TITLE)) {
3979 if (tmplLine->numElements >= 2) {
3980 newLine = addLineTmpl(new, tmplLine, newLine,
3981 newKernelTitle, config->cfi);
3982 needs &= ~NEED_TITLE;
3983 } else if (tmplLine->numElements == 1 &&
3984 config->cfi->titleBracketed) {
3985 /* addLineTmpl doesn't handle titleBracketed */
3986 newLine = addLine(new, config->cfi, LT_TITLE,
3987 tmplLine->indent, newKernelTitle);
3988 needs &= ~NEED_TITLE;
3989 }
3990 } else if (tmplLine->type == LT_ECHO) {
3991 requote(tmplLine, config->cfi);
3992 static const char *prefix = "'Loading ";
3993 if (tmplLine->numElements > 1 &&
3994 strstr(tmplLine->elements[1].item, prefix) &&
3995 masterLine->next &&
3996 iskernel(masterLine->next->type)) {
3997 char *newTitle = malloc(strlen(prefix) +
3998 strlen(newKernelTitle) + 2);
3999
4000 strcpy(newTitle, prefix);
4001 strcat(newTitle, newKernelTitle);
4002 strcat(newTitle, "'");
4003 newLine = addLine(new, config->cfi, LT_ECHO,
4004 tmplLine->indent, newTitle);
4005 free(newTitle);
4006 } else {
4007 /* pass through other lines from the template */
4008 newLine = addLineTmpl(new, tmplLine, newLine, NULL,
4009 config->cfi);
4010 }
4011 } else if (tmplLine->type == LT_DEVTREE &&
4012 tmplLine->numElements == 2 && newDevTreePath) {
4013 newLine = addLineTmpl(new, tmplLine, newLine,
4014 newDevTreePath + strlen(prefix),
4015 config->cfi);
4016 needs &= ~NEED_DEVTREE;
4017 } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {
4018 const char *ndtp = newDevTreePath;
4019 if (!strncmp(newDevTreePath, prefix, strlen(prefix)))
4020 ndtp += strlen(prefix);
4021 newLine = addLine(new, config->cfi, LT_DEVTREE,
4022 config->secondaryIndent,
4023 ndtp);
4024 needs &= ~NEED_DEVTREE;
4025 newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4026 } else {
4027 /* pass through other lines from the template */
4028 newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4029 }
4030 }
4031
4032 } else {
4033 /* don't have a template, so start the entry with the
4034 * appropriate starting line
4035 */
4036 switch (config->cfi->entryStart) {
4037 case LT_KERNEL:
4038 case LT_KERNEL_EFI:
4039 case LT_KERNEL_16:
4040 if (new->multiboot && config->cfi->mbHyperFirst) {
4041 /* fall through to LT_HYPER */
4042 } else {
4043 newLine = addLine(new, config->cfi,
4044 preferredLineType(LT_KERNEL, config->cfi),
4045 config->primaryIndent,
4046 newKernelPath + strlen(prefix));
4047 needs &= ~NEED_KERNEL;
4048 break;
4049 }
4050
4051 case LT_HYPER:
4052 newLine = addLine(new, config->cfi, LT_HYPER,
4053 config->primaryIndent,
4054 newMBKernel + strlen(prefix));
4055 needs &= ~NEED_MB;
4056 break;
4057
4058 case LT_MENUENTRY: {
4059 char *nkt = malloc(strlen(newKernelTitle)+3);
4060 strcpy(nkt, "'");
4061 strcat(nkt, newKernelTitle);
4062 strcat(nkt, "'");
4063 newLine = addLine(new, config->cfi, LT_MENUENTRY,
4064 config->primaryIndent, nkt);
4065 free(nkt);
4066 needs &= ~NEED_TITLE;
4067 needs |= NEED_END;
4068 break;
4069 }
4070 case LT_TITLE:
4071 if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
4072 char * templabel;
4073 int x = 0, y = 0;
4074
4075 templabel = strdup(newKernelTitle);
4076 while( templabel[x]){
4077 if( templabel[x] == ' ' ){
4078 y = x;
4079 while( templabel[y] ){
4080 templabel[y] = templabel[y+1];
4081 y++;
4082 }
4083 }
4084 x++;
4085 }
4086 newLine = addLine(new, config->cfi, LT_TITLE,
4087 config->primaryIndent, templabel);
4088 free(templabel);
4089 }else{
4090 newLine = addLine(new, config->cfi, LT_TITLE,
4091 config->primaryIndent, newKernelTitle);
4092 }
4093 needs &= ~NEED_TITLE;
4094 break;
4095
4096 default:
4097 abort();
4098 }
4099 }
4100
4101 struct singleLine *endLine = NULL;
4102 endLine = getLineByType(LT_ENTRY_END, new->lines);
4103 if (endLine) {
4104 removeLine(new, endLine);
4105 needs |= NEED_END;
4106 }
4107
4108 /* add the remainder of the lines, i.e. those that either
4109 * weren't present in the template, or in the case of no template,
4110 * all the lines following the entryStart.
4111 */
4112 if (needs & NEED_TITLE) {
4113 newLine = addLine(new, config->cfi, LT_TITLE,
4114 config->secondaryIndent,
4115 newKernelTitle);
4116 needs &= ~NEED_TITLE;
4117 }
4118 if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4119 newLine = addLine(new, config->cfi, LT_HYPER,
4120 config->secondaryIndent,
4121 newMBKernel + strlen(prefix));
4122 needs &= ~NEED_MB;
4123 }
4124 if (needs & NEED_KERNEL) {
4125 newLine = addLine(new, config->cfi,
4126 (new->multiboot && getKeywordByType(LT_MBMODULE,
4127 config->cfi))
4128 ? LT_MBMODULE
4129 : preferredLineType(LT_KERNEL, config->cfi),
4130 config->secondaryIndent,
4131 newKernelPath + strlen(prefix));
4132 needs &= ~NEED_KERNEL;
4133 }
4134 if (needs & NEED_MB) {
4135 newLine = addLine(new, config->cfi, LT_HYPER,
4136 config->secondaryIndent,
4137 newMBKernel + strlen(prefix));
4138 needs &= ~NEED_MB;
4139 }
4140 if (needs & NEED_INITRD) {
4141 char *initrdVal;
4142 initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
4143 newLine = addLine(new, config->cfi,
4144 (new->multiboot && getKeywordByType(LT_MBMODULE,
4145 config->cfi))
4146 ? LT_MBMODULE
4147 : preferredLineType(LT_INITRD, config->cfi),
4148 config->secondaryIndent,
4149 initrdVal);
4150 free(initrdVal);
4151 needs &= ~NEED_INITRD;
4152 }
4153 if (needs & NEED_DEVTREE) {
4154 newLine = addLine(new, config->cfi, LT_DEVTREE,
4155 config->secondaryIndent,
4156 newDevTreePath);
4157 needs &= ~NEED_DEVTREE;
4158 }
4159
4160 /* NEEDS_END must be last on bootloaders that need it... */
4161 if (needs & NEED_END) {
4162 newLine = addLine(new, config->cfi, LT_ENTRY_END,
4163 config->secondaryIndent, NULL);
4164 needs &= ~NEED_END;
4165 }
4166
4167 if (needs) {
4168 printf(_("grubby: needs=%d, aborting\n"), needs);
4169 abort();
4170 }
4171
4172 if (updateImage(config, "0", prefix, newKernelArgs, NULL,
4173 newMBKernelArgs, NULL)) return 1;
4174
4175 return 0;
4176 }
4177
4178 static void traceback(int signum)
4179 {
4180 void *array[40];
4181 size_t size;
4182
4183 signal(SIGSEGV, SIG_DFL);
4184 memset(array, '\0', sizeof (array));
4185 size = backtrace(array, 40);
4186
4187 fprintf(stderr, "grubby received SIGSEGV! Backtrace (%ld):\n",
4188 (unsigned long)size);
4189 backtrace_symbols_fd(array, size, STDERR_FILENO);
4190 exit(1);
4191 }
4192
4193 int main(int argc, const char ** argv) {
4194 poptContext optCon;
4195 const char * grubConfig = NULL;
4196 char * outputFile = NULL;
4197 int arg = 0;
4198 int flags = 0;
4199 int badImageOkay = 0;
4200 int configureGrub2 = 0;
4201 int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4202 int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4203 int configureExtLinux = 0;
4204 int bootloaderProbe = 0;
4205 int extraInitrdCount = 0;
4206 char * updateKernelPath = NULL;
4207 char * newKernelPath = NULL;
4208 char * removeKernelPath = NULL;
4209 char * newKernelArgs = NULL;
4210 char * newKernelInitrd = NULL;
4211 char * newKernelTitle = NULL;
4212 char * newDevTreePath = NULL;
4213 char * newMBKernel = NULL;
4214 char * newMBKernelArgs = NULL;
4215 char * removeMBKernelArgs = NULL;
4216 char * removeMBKernel = NULL;
4217 char * bootPrefix = NULL;
4218 char * defaultKernel = NULL;
4219 char * removeArgs = NULL;
4220 char * kernelInfo = NULL;
4221 char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4222 char * envPath = NULL;
4223 const char * chptr = NULL;
4224 struct configFileInfo * cfi = NULL;
4225 struct grubConfig * config;
4226 struct singleEntry * template = NULL;
4227 int copyDefault = 0, makeDefault = 0;
4228 int displayDefault = 0;
4229 int displayDefaultIndex = 0;
4230 int displayDefaultTitle = 0;
4231 int defaultIndex = -1;
4232 struct poptOption options[] = {
4233 { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4234 _("add an entry for the specified kernel"), _("kernel-path") },
4235 { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4236 _("add an entry for the specified multiboot kernel"), NULL },
4237 { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4238 _("default arguments for the new kernel or new arguments for "
4239 "kernel being updated"), _("args") },
4240 { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4241 _("default arguments for the new multiboot kernel or "
4242 "new arguments for multiboot kernel being updated"), NULL },
4243 { "bad-image-okay", 0, 0, &badImageOkay, 0,
4244 _("don't sanity check images in boot entries (for testing only)"),
4245 NULL },
4246 { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4247 _("filestystem which contains /boot directory (for testing only)"),
4248 _("bootfs") },
4249 #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4250 { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4251 _("check which bootloader is installed on boot sector") },
4252 #endif
4253 { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4254 _("path to grub config file to update (\"-\" for stdin)"),
4255 _("path") },
4256 { "copy-default", 0, 0, &copyDefault, 0,
4257 _("use the default boot entry as a template for the new entry "
4258 "being added; if the default is not a linux image, or if "
4259 "the kernel referenced by the default image does not exist, "
4260 "the first linux entry whose kernel does exist is used as the "
4261 "template"), NULL },
4262 { "debug", 0, 0, &debug, 0,
4263 _("print debugging information for failures") },
4264 { "default-kernel", 0, 0, &displayDefault, 0,
4265 _("display the path of the default kernel") },
4266 { "default-index", 0, 0, &displayDefaultIndex, 0,
4267 _("display the index of the default kernel") },
4268 { "default-title", 0, 0, &displayDefaultTitle, 0,
4269 _("display the title of the default kernel") },
4270 { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4271 _("device tree file for new stanza"), _("dtb-path") },
4272 { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4273 _("configure elilo bootloader") },
4274 { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4275 _("force grub2 stanzas to use efi") },
4276 { "env", 0, POPT_ARG_STRING, &envPath, 0,
4277 _("path for environment data"),
4278 _("path") },
4279 { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4280 _("configure extlinux bootloader (from syslinux)") },
4281 { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4282 _("configure grub bootloader") },
4283 { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4284 _("configure grub2 bootloader") },
4285 { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4286 _("display boot information for specified kernel"),
4287 _("kernel-path") },
4288 { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4289 _("initrd image for the new kernel"), _("initrd-path") },
4290 { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4291 _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4292 { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4293 _("configure lilo bootloader") },
4294 { "make-default", 0, 0, &makeDefault, 0,
4295 _("make the newly added entry the default boot entry"), NULL },
4296 { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4297 _("path to output updated config file (\"-\" for stdout)"),
4298 _("path") },
4299 { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4300 _("remove kernel arguments"), NULL },
4301 { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4302 _("remove multiboot kernel arguments"), NULL },
4303 { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4304 _("remove all entries for the specified kernel"),
4305 _("kernel-path") },
4306 { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4307 _("remove all entries for the specified multiboot kernel"), NULL },
4308 { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4309 _("make the first entry referencing the specified kernel "
4310 "the default"), _("kernel-path") },
4311 { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4312 _("make the given entry index the default entry"),
4313 _("entry-index") },
4314 { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4315 _("configure silo bootloader") },
4316 { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
4317 _("title to use for the new kernel entry"), _("entry-title") },
4318 { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
4319 _("updated information for the specified kernel"),
4320 _("kernel-path") },
4321 { "version", 'v', 0, NULL, 'v',
4322 _("print the version of this program and exit"), NULL },
4323 { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
4324 _("configure yaboot bootloader") },
4325 { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
4326 _("configure zipl bootloader") },
4327 POPT_AUTOHELP
4328 { 0, 0, 0, 0, 0 }
4329 };
4330
4331 useextlinuxmenu=0;
4332
4333 signal(SIGSEGV, traceback);
4334
4335 int i = 0;
4336 for (int j = 1; j < argc; j++)
4337 i += strlen(argv[j]) + 1;
4338 saved_command_line = malloc(i);
4339 if (!saved_command_line) {
4340 fprintf(stderr, "grubby: %m\n");
4341 exit(1);
4342 }
4343 saved_command_line[0] = '\0';
4344 for (int j = 1; j < argc; j++) {
4345 strcat(saved_command_line, argv[j]);
4346 strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4347 }
4348
4349 optCon = poptGetContext("grubby", argc, argv, options, 0);
4350 poptReadDefaultConfig(optCon, 1);
4351
4352 while ((arg = poptGetNextOpt(optCon)) >= 0) {
4353 switch (arg) {
4354 case 'v':
4355 printf("grubby version %s\n", VERSION);
4356 exit(0);
4357 break;
4358 case 'i':
4359 if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4360 extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4361 } else {
4362 fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4363 return 1;
4364 }
4365 break;
4366 }
4367 }
4368
4369 if (arg < -1) {
4370 fprintf(stderr, _("grubby: bad argument %s: %s\n"),
4371 poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
4372 poptStrerror(arg));
4373 return 1;
4374 }
4375
4376 if ((chptr = poptGetArg(optCon))) {
4377 fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
4378 return 1;
4379 }
4380
4381 if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4382 configureYaboot + configureSilo + configureZipl +
4383 configureExtLinux ) > 1) {
4384 fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4385 return 1;
4386 } else if (bootloaderProbe && grubConfig) {
4387 fprintf(stderr,
4388 _("grubby: cannot specify config file with --bootloader-probe\n"));
4389 return 1;
4390 } else if (configureGrub2) {
4391 cfi = &grub2ConfigType;
4392 if (envPath)
4393 cfi->envFile = envPath;
4394 } else if (configureLilo) {
4395 cfi = &liloConfigType;
4396 } else if (configureGrub) {
4397 cfi = &grubConfigType;
4398 } else if (configureELilo) {
4399 cfi = &eliloConfigType;
4400 } else if (configureYaboot) {
4401 cfi = &yabootConfigType;
4402 } else if (configureSilo) {
4403 cfi = &siloConfigType;
4404 } else if (configureZipl) {
4405 cfi = &ziplConfigType;
4406 } else if (configureExtLinux) {
4407 cfi = &extlinuxConfigType;
4408 useextlinuxmenu=1;
4409 }
4410
4411 if (!cfi) {
4412 if (grub2FindConfig(&grub2ConfigType))
4413 cfi = &grub2ConfigType;
4414 else
4415 #ifdef __ia64__
4416 cfi = &eliloConfigType;
4417 #elif __powerpc__
4418 cfi = &yabootConfigType;
4419 #elif __sparc__
4420 cfi = &siloConfigType;
4421 #elif __s390__
4422 cfi = &ziplConfigType;
4423 #elif __s390x__
4424 cfi = &ziplConfigtype;
4425 #else
4426 cfi = &grubConfigType;
4427 #endif
4428 }
4429
4430 if (!grubConfig) {
4431 if (cfi->findConfig)
4432 grubConfig = cfi->findConfig(cfi);
4433 if (!grubConfig)
4434 grubConfig = cfi->defaultConfig;
4435 }
4436
4437 if (bootloaderProbe && (displayDefault || kernelInfo ||
4438 newKernelPath || removeKernelPath || makeDefault ||
4439 defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4440 (defaultIndex >= 0))) {
4441 fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4442 "specified option"));
4443 return 1;
4444 }
4445
4446 if ((displayDefault || kernelInfo) && (newKernelPath ||
4447 removeKernelPath)) {
4448 fprintf(stderr, _("grubby: --default-kernel and --info may not "
4449 "be used when adding or removing kernels\n"));
4450 return 1;
4451 }
4452
4453 if (newKernelPath && !newKernelTitle) {
4454 fprintf(stderr, _("grubby: kernel title must be specified\n"));
4455 return 1;
4456 } else if (!newKernelPath && (copyDefault ||
4457 (newKernelInitrd && !updateKernelPath)||
4458 makeDefault || extraInitrdCount > 0)) {
4459 fprintf(stderr, _("grubby: kernel path expected\n"));
4460 return 1;
4461 }
4462
4463 if (newKernelPath && updateKernelPath) {
4464 fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
4465 "not be used together"));
4466 return 1;
4467 }
4468
4469 if (makeDefault && defaultKernel) {
4470 fprintf(stderr, _("grubby: --make-default and --default-kernel "
4471 "may not be used together\n"));
4472 return 1;
4473 } else if (defaultKernel && removeKernelPath &&
4474 !strcmp(defaultKernel, removeKernelPath)) {
4475 fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
4476 return 1;
4477 } else if (defaultKernel && newKernelPath &&
4478 !strcmp(defaultKernel, newKernelPath)) {
4479 makeDefault = 1;
4480 defaultKernel = NULL;
4481 }
4482 else if (defaultKernel && (defaultIndex >= 0)) {
4483 fprintf(stderr, _("grubby: --set-default and --set-default-index "
4484 "may not be used together\n"));
4485 return 1;
4486 }
4487
4488 if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4489 fprintf(stderr, _("grubby: output file must be specified if stdin "
4490 "is used\n"));
4491 return 1;
4492 }
4493
4494 if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4495 && !kernelInfo && !bootloaderProbe && !updateKernelPath
4496 && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4497 && (defaultIndex == -1)) {
4498 fprintf(stderr, _("grubby: no action specified\n"));
4499 return 1;
4500 }
4501
4502 flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
4503
4504 if (cfi->needsBootPrefix) {
4505 if (!bootPrefix) {
4506 bootPrefix = findBootPrefix();
4507 if (!bootPrefix) return 1;
4508 } else {
4509 /* this shouldn't end with a / */
4510 if (bootPrefix[strlen(bootPrefix) - 1] == '/')
4511 bootPrefix[strlen(bootPrefix) - 1] = '\0';
4512 }
4513 } else {
4514 bootPrefix = "";
4515 }
4516
4517 if (!cfi->mbAllowExtraInitRds &&
4518 extraInitrdCount > 0) {
4519 fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4520 return 1;
4521 }
4522
4523 if (bootloaderProbe) {
4524 int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4525 struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4526
4527 const char *grub2config = grub2FindConfig(&grub2ConfigType);
4528 if (grub2config) {
4529 gconfig = readConfig(grub2config, &grub2ConfigType);
4530 if (!gconfig)
4531 gr2c = 1;
4532 else
4533 gr2c = checkForGrub2(gconfig);
4534 }
4535
4536 const char *grubconfig = grubFindConfig(&grubConfigType);
4537 if (!access(grubconfig, F_OK)) {
4538 gconfig = readConfig(grubconfig, &grubConfigType);
4539 if (!gconfig)
4540 grc = 1;
4541 else
4542 grc = checkForGrub(gconfig);
4543 }
4544
4545 if (!access(liloConfigType.defaultConfig, F_OK)) {
4546 lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
4547 if (!lconfig)
4548 lrc = 1;
4549 else
4550 lrc = checkForLilo(lconfig);
4551 }
4552
4553 if (!access(eliloConfigType.defaultConfig, F_OK)) {
4554 econfig = readConfig(eliloConfigType.defaultConfig,
4555 &eliloConfigType);
4556 if (!econfig)
4557 erc = 1;
4558 else
4559 erc = checkForElilo(econfig);
4560 }
4561
4562 if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4563 lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4564 if (!lconfig)
4565 extrc = 1;
4566 else
4567 extrc = checkForExtLinux(lconfig);
4568 }
4569
4570
4571 if (!access(yabootConfigType.defaultConfig, F_OK)) {
4572 yconfig = readConfig(yabootConfigType.defaultConfig,
4573 &yabootConfigType);
4574 if (!yconfig)
4575 yrc = 1;
4576 else
4577 yrc = checkForYaboot(yconfig);
4578 }
4579
4580 if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4581 erc == 1)
4582 return 1;
4583
4584 if (lrc == 2) printf("lilo\n");
4585 if (gr2c == 2) printf("grub2\n");
4586 if (grc == 2) printf("grub\n");
4587 if (extrc == 2) printf("extlinux\n");
4588 if (yrc == 2) printf("yaboot\n");
4589 if (erc == 2) printf("elilo\n");
4590
4591 return 0;
4592 }
4593
4594 if (grubConfig == NULL) {
4595 printf("Could not find bootloader configuration file.\n");
4596 exit(1);
4597 }
4598
4599 config = readConfig(grubConfig, cfi);
4600 if (!config) return 1;
4601
4602 if (displayDefault) {
4603 struct singleLine * line;
4604 struct singleEntry * entry;
4605 char * rootspec;
4606
4607 if (config->defaultImage == -1) return 0;
4608 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4609 cfi->defaultIsSaved)
4610 config->defaultImage = 0;
4611 entry = findEntryByIndex(config, config->defaultImage);
4612 if (!entry) return 0;
4613 if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4614
4615 line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
4616 if (!line) return 0;
4617
4618 rootspec = getRootSpecifier(line->elements[1].item);
4619 printf("%s%s\n", bootPrefix, line->elements[1].item +
4620 ((rootspec != NULL) ? strlen(rootspec) : 0));
4621
4622 return 0;
4623
4624 } else if (displayDefaultTitle) {
4625 struct singleLine * line;
4626 struct singleEntry * entry;
4627
4628 if (config->defaultImage == -1) return 0;
4629 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4630 cfi->defaultIsSaved)
4631 config->defaultImage = 0;
4632 entry = findEntryByIndex(config, config->defaultImage);
4633 if (!entry) return 0;
4634
4635 if (!configureGrub2) {
4636 line = getLineByType(LT_TITLE, entry->lines);
4637 if (!line) return 0;
4638 printf("%s\n", line->elements[1].item);
4639
4640 } else {
4641 char * title;
4642
4643 dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4644 line = getLineByType(LT_MENUENTRY, entry->lines);
4645 if (!line) return 0;
4646 title = grub2ExtractTitle(line);
4647 if (title)
4648 printf("%s\n", title);
4649 }
4650 return 0;
4651
4652 } else if (displayDefaultIndex) {
4653 if (config->defaultImage == -1) return 0;
4654 if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4655 cfi->defaultIsSaved)
4656 config->defaultImage = 0;
4657 printf("%i\n", config->defaultImage);
4658 return 0;
4659
4660 } else if (kernelInfo)
4661 return displayInfo(config, kernelInfo, bootPrefix);
4662
4663 if (copyDefault) {
4664 template = findTemplate(config, bootPrefix, NULL, 0, flags);
4665 if (!template) return 1;
4666 }
4667
4668 markRemovedImage(config, removeKernelPath, bootPrefix);
4669 markRemovedImage(config, removeMBKernel, bootPrefix);
4670 setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4671 bootPrefix, flags, defaultIndex);
4672 setFallbackImage(config, newKernelPath != NULL);
4673 if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4674 removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4675 if (updateKernelPath && newKernelInitrd) {
4676 if (newMBKernel) {
4677 if (addMBInitrd(config, newMBKernel, updateKernelPath,
4678 bootPrefix, newKernelInitrd,
4679 newKernelTitle))
4680 return 1;
4681 } else {
4682 if (updateInitrd(config, updateKernelPath, bootPrefix,
4683 newKernelInitrd, newKernelTitle))
4684 return 1;
4685 }
4686 }
4687 if (addNewKernel(config, template, bootPrefix, newKernelPath,
4688 newKernelTitle, newKernelArgs, newKernelInitrd,
4689 (const char **)extraInitrds, extraInitrdCount,
4690 newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;
4691
4692
4693 if (numEntries(config) == 0) {
4694 fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
4695 "Not writing out new config.\n"));
4696 return 1;
4697 }
4698
4699 if (!outputFile)
4700 outputFile = (char *)grubConfig;
4701
4702 return writeConfig(config, outputFile, bootPrefix);
4703 }