Magellan Linux

Annotation of /trunk/grubby/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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