Magellan Linux

Annotation of /tags/grubby-8_40_20170706/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log


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