Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


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