Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

trunk/mkinitrd-magellan/grubby/grubby.c revision 532 by niro, Sat Sep 1 22:45:15 2007 UTC trunk/grubby/grubby.c revision 3003 by niro, Tue Jun 27 14:14:00 2017 UTC
# Line 1  Line 1 
1  /* Copyright (C) 2001-2005 Red Hat, Inc.  /*
2     * grubby.c
3     This program is free software; you can redistribute it and/or   *
4     modify it under the terms of the General Public License as published   * Copyright (C) 2001-2008 Red Hat, Inc.
5     by the Free Software Foundation; either version 2 of the License, or   * All rights reserved.
6     (at your option) any later version.   *
7     * This program is free software; you can redistribute it and/or modify
8     This program is distributed in the hope that it will be useful,   * it under the terms of the GNU General Public License as published by
9     but WITHOUT ANY WARRANTY; without even the implied warranty of   * the Free Software Foundation; either version 2 of the License, or
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   * (at your option) any later version.
11     General Public License for more details.   *
12     * This program is distributed in the hope that it will be useful,
13     You should have received a copy of the GNU General Public   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14     License along with this program; if not, write to the Free   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA   * GNU General Public License for more details.
16     02111-1307 USA.  */   *
17     * You should have received a copy of the GNU General Public License
18     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19     */
20    
21    #ifndef _GNU_SOURCE
22    #define _GNU_SOURCE
23    #endif
24  #include <ctype.h>  #include <ctype.h>
25  #include <errno.h>  #include <errno.h>
26  #include <fcntl.h>  #include <fcntl.h>
# Line 25  Line 31 
31  #include <string.h>  #include <string.h>
32  #include <sys/stat.h>  #include <sys/stat.h>
33  #include <unistd.h>  #include <unistd.h>
34    #include <libgen.h>
35    #include <execinfo.h>
36    #include <signal.h>
37    #include <blkid/blkid.h>
38    
39    #include "log.h"
40    
41    #ifndef DEBUG
42    #define DEBUG 0
43    #endif
44    
45    #if DEBUG
46    #define dbgPrintf(format, args...) fprintf(stderr, format , ## args)
47    #else
48    #define dbgPrintf(format, args...)
49    #endif
50    
51  #include "mount_by_label.h"  int debug = 0; /* Currently just for template debugging */
52    
53  #define _(A) (A)  #define _(A) (A)
54    
55    #define MAX_EXTRA_INITRDS  16 /* code segment checked by --bootloader-probe */
56  #define CODE_SEG_SIZE  128 /* code segment checked by --bootloader-probe */  #define CODE_SEG_SIZE  128 /* code segment checked by --bootloader-probe */
57    
58    #define NOOP_OPCODE 0x90
59    #define JMP_SHORT_OPCODE 0xeb
60    
61    int isEfi = 0;
62    
63    #if defined(__aarch64__)
64    #define isEfiOnly 1
65    #else
66    #define isEfiOnly 0
67    #endif
68    
69    char *saved_command_line = NULL;
70    
71  /* comments get lumped in with indention */  /* comments get lumped in with indention */
72  struct lineElement {  struct lineElement {
73      char * item;   char *item;
74      char * indent;   char *indent;
75  };  };
76    
77  enum lineType_e { LT_WHITESPACE, LT_TITLE, LT_KERNEL, LT_INITRD, LT_DEFAULT,  enum lineType_e {
78         LT_UNKNOWN, LT_ROOT, LT_FALLBACK, LT_KERNELARGS, LT_BOOT,   LT_UNIDENTIFIED = 0,
79         LT_BOOTROOT, LT_LBA, LT_MBMODULE, LT_OTHER, LT_GENERIC };   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    };
105    
106  struct singleLine {  struct singleLine {
107      char * indent;   char *indent;
108      int numElements;   int numElements;
109      struct lineElement * elements;   struct lineElement *elements;
110      struct singleLine * next;   struct singleLine *next;
111      enum lineType_e type;   enum lineType_e type;
112  };  };
113    
114  struct singleEntry {  struct singleEntry {
115      struct singleLine * lines;   struct singleLine *lines;
116      int skip;   int skip;
117      int multiboot;   int multiboot;
118      struct singleEntry * next;   struct singleEntry *next;
119  };  };
120    
121  #define GRUBBY_BADIMAGE_OKAY (1 << 0)  #define GRUBBY_BADIMAGE_OKAY (1 << 0)
122    
123  #define GRUB_CONFIG_NO_DEFAULT    (1 << 0) /* don't write out default=0 */  #define GRUB_CONFIG_NO_DEFAULT    (1 << 0) /* don't write out default=0 */
124    
125  #define KERNEL_KERNEL    (1 << 0)  /* These defines are (only) used in addNewKernel() */
126  #define KERNEL_INITRD    (1 << 2)  #define NEED_KERNEL  (1 << 0)
127  #define KERNEL_TITLE    (1 << 3)  #define NEED_INITRD  (1 << 1)
128  #define KERNEL_ARGS    (1 << 4)  #define NEED_TITLE   (1 << 2)
129  #define KERNEL_MB           (1 << 5)  #define NEED_ARGS    (1 << 3)
130    #define NEED_MB      (1 << 4)
131    #define NEED_END     (1 << 5)
132    #define NEED_DEVTREE (1 << 6)
133    
134  #define MAIN_DEFAULT    (1 << 0)  #define MAIN_DEFAULT    (1 << 0)
135  #define DEFAULT_SAVED       -2  #define DEFAULT_SAVED       -2
136    #define DEFAULT_SAVED_GRUB2 -3
137    
138  struct keywordTypes {  struct keywordTypes {
139      char * key;   char *key;
140      enum lineType_e type;   enum lineType_e type;
141      char nextChar;   char nextChar;
142  } ;   char separatorChar;
143    };
144    
145    struct configFileInfo;
146    
147    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    
153  struct configFileInfo {  struct configFileInfo {
154      char * defaultConfig;   char *defaultConfig;
155      struct keywordTypes * keywords;   findConfigFunc findConfig;
156      int defaultIsIndex;   writeLineFunc writeLine;
157      int defaultSupportSaved;   getEnvFunc getEnv;
158      enum lineType_e entrySeparator;   setEnvFunc setEnv;
159      int needsBootPrefix;   struct keywordTypes *keywords;
160      int argsInQuotes;   int caseInsensitive;
161      int maxTitleLength;   int defaultIsIndex;
162      int titleBracketed;   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  };  };
179    
180  struct keywordTypes grubKeywords[] = {  struct keywordTypes grubKeywords[] = {
181      { "title",    LT_TITLE,    ' ' },   {"title", LT_TITLE, ' '},
182      { "root",    LT_BOOTROOT,    ' ' },   {"root", LT_BOOTROOT, ' '},
183      { "default",    LT_DEFAULT,    ' ' },   {"default", LT_DEFAULT, ' '},
184      { "fallback",   LT_FALLBACK,    ' ' },   {"fallback", LT_FALLBACK, ' '},
185      { "kernel",    LT_KERNEL,    ' ' },   {"kernel", LT_KERNEL, ' '},
186      { "initrd",    LT_INITRD,    ' ' },   {"initrd", LT_INITRD, ' ', ' '},
187      { "module",     LT_MBMODULE,    ' ' },   {"module", LT_MBMODULE, ' '},
188      { NULL,    0, 0 },   {"kernel", LT_HYPER, ' '},
189     {NULL, 0, 0},
190  };  };
191    
192    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    
202     if (i == -1) {
203     for (i = 0; configFiles[i] != NULL; i++) {
204     dbgPrintf("Checking \"%s\": ", configFiles[i]);
205     if (!access(configFiles[i], R_OK)) {
206     dbgPrintf("found\n");
207     return configFiles[i];
208     }
209     dbgPrintf("not found\n");
210     }
211     }
212     return configFiles[i];
213    }
214    
215  struct configFileInfo grubConfigType = {  struct configFileInfo grubConfigType = {
216      "/boot/grub/grub.conf",    /* defaultConfig */   .findConfig = grubFindConfig,
217      grubKeywords,    /* keywords */   .keywords = grubKeywords,
218      1,    /* defaultIsIndex */   .defaultIsIndex = 1,
219      1,    /* defaultSupportSaved */   .defaultSupportSaved = 1,
220      LT_TITLE,    /* entrySeparator */   .entryStart = LT_TITLE,
221      1,    /* needsBootPrefix */   .needsBootPrefix = 1,
222      0,    /* argsInQuotes */   .mbHyperFirst = 1,
223      0,    /* maxTitleLength */   .mbInitRdIsModule = 1,
224      0,                                      /* titleBracketed */   .mbAllowExtraInitRds = 1,
225     .titlePosition = 1,
226    };
227    
228    struct keywordTypes grub2Keywords[] = {
229     {"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    };
247    
248    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    
263     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     }
279     }
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     dbgPrintf("found\n");
286     return grub_cfg;
287     }
288    
289     dbgPrintf("not found\n");
290     return configFiles[i];
291    }
292    
293    /* kind of hacky.  It'll give the first 1024 bytes, ish. */
294    static char *grub2GetEnv(struct configFileInfo *info, char *name)
295    {
296     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    
303     if (rc < 0)
304     return NULL;
305    
306     FILE *f = popen(s, "r");
307     if (!f)
308     goto out;
309    
310     memset(buf, '\0', sizeof(buf));
311     ret = fgets(buf, 1024, f);
312     pclose(f);
313    
314     if (ret) {
315     ret += strlen(name) + 1;
316     ret[strlen(ret) - 1] = '\0';
317     }
318     dbgPrintf("grub2GetEnv(%s): %s\n", name, ret);
319    out:
320     free(s);
321     return ret;
322    }
323    
324    static int sPopCount(const char *s, const char *c)
325    {
326     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    }
335    
336    static char *shellEscape(const char *s)
337    {
338     int l = strlen(s) + sPopCount(s, "'") * 2;
339    
340     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    }
350    
351    static void unquote(char *s)
352    {
353     int l = strlen(s);
354    
355     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    }
361    
362    static int grub2SetEnv(struct configFileInfo *info, char *name, char *value)
363    {
364     char *s = NULL;
365     int rc = 0;
366     char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
367    
368     unquote(value);
369     value = shellEscape(value);
370     if (!value)
371     return -1;
372    
373     rc = asprintf(&s, "grub-editenv %s set '%s=%s'", envFile, name, value);
374     free(value);
375     if (rc < 0)
376     return -1;
377    
378     dbgPrintf("grub2SetEnv(%s): %s\n", name, s);
379     rc = system(s);
380     free(s);
381     return rc;
382    }
383    
384    /* this is a gigantic hack to avoid clobbering grub2 variables... */
385    static int is_special_grub2_variable(const char *name)
386    {
387     if (!strcmp(name, "\"${next_entry}\""))
388     return 1;
389     if (!strcmp(name, "\"${prev_saved_entry}\""))
390     return 1;
391     return 0;
392    }
393    
394    int sizeOfSingleLine(struct singleLine *line)
395    {
396     int count = 0;
397    
398     for (int i = 0; i < line->numElements; i++) {
399     int indentSize = 0;
400    
401     count = count + strlen(line->elements[i].item);
402    
403     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    
411     /* room for trailing terminator */
412     count = count + 1;
413    
414     return count;
415    }
416    
417    static int isquote(char q)
418    {
419     if (q == '\'' || q == '\"')
420     return 1;
421     return 0;
422    }
423    
424    static int iskernel(enum lineType_e type)
425    {
426     return (type == LT_KERNEL || type == LT_KERNEL_EFI
427     || type == LT_KERNEL_16);
428    }
429    
430    static int isinitrd(enum lineType_e type)
431    {
432     return (type == LT_INITRD || type == LT_INITRD_EFI
433     || type == LT_INITRD_16);
434    }
435    
436    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    
444     /* bail out if line does not start with menuentry */
445     if (strcmp(line->elements[0].item, "menuentry"))
446     return NULL;
447    
448     i = 1;
449     current = line->elements[i].item;
450     current_len = strlen(current);
451    
452     /* 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    
456     tmp = strdup(current + 1);
457     if (!tmp)
458     return NULL;
459     tmp[strlen(tmp) - 1] = '\0';
460     return tmp;
461     }
462    
463     /* if no quotes, return second word verbatim */
464     if (!isquote(*current))
465     return current;
466    
467     /* 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    
474     resultMaxSize = sizeOfSingleLine(line);
475     result = malloc(resultMaxSize);
476     snprintf(result, resultMaxSize, "%s", ++current);
477    
478     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    
485     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     }
493     return result;
494    }
495    
496    struct configFileInfo grub2ConfigType = {
497     .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  };  };
512    
513  struct keywordTypes yabootKeywords[] = {  struct keywordTypes yabootKeywords[] = {
514      { "label",    LT_TITLE,    '=' },   {"label", LT_TITLE, '='},
515      { "root",    LT_ROOT,    '=' },   {"root", LT_ROOT, '='},
516      { "default",    LT_DEFAULT,    '=' },   {"default", LT_DEFAULT, '='},
517      { "image",    LT_KERNEL,    '=' },   {"image", LT_KERNEL, '='},
518      { "bsd",    LT_GENERIC,    '=' },   {"bsd", LT_GENERIC, '='},
519      { "macos",    LT_GENERIC,    '=' },   {"macos", LT_GENERIC, '='},
520      { "macosx",    LT_GENERIC,    '=' },   {"macosx", LT_GENERIC, '='},
521      { "magicboot",  LT_GENERIC,    '=' },   {"magicboot", LT_GENERIC, '='},
522      { "darwin",    LT_GENERIC,    '=' },   {"darwin", LT_GENERIC, '='},
523      { "timeout",    LT_GENERIC,    '=' },   {"timeout", LT_GENERIC, '='},
524      { "install",    LT_GENERIC,    '=' },   {"install", LT_GENERIC, '='},
525      { "fstype",    LT_GENERIC,    '=' },   {"fstype", LT_GENERIC, '='},
526      { "hfstype",    LT_GENERIC,    '=' },   {"hfstype", LT_GENERIC, '='},
527      { "delay",    LT_GENERIC,    '=' },   {"delay", LT_GENERIC, '='},
528      { "defaultos",  LT_GENERIC,     '=' },   {"defaultos", LT_GENERIC, '='},
529      { "init-message", LT_GENERIC,   '=' },   {"init-message", LT_GENERIC, '='},
530      { "enablecdboot", LT_GENERIC,   ' ' },   {"enablecdboot", LT_GENERIC, ' '},
531      { "enableofboot", LT_GENERIC,   ' ' },   {"enableofboot", LT_GENERIC, ' '},
532      { "enablenetboot", LT_GENERIC,  ' ' },   {"enablenetboot", LT_GENERIC, ' '},
533      { "nonvram",    LT_GENERIC,    ' ' },   {"nonvram", LT_GENERIC, ' '},
534      { "hide",    LT_GENERIC,    ' ' },   {"hide", LT_GENERIC, ' '},
535      { "protect",    LT_GENERIC,    ' ' },   {"protect", LT_GENERIC, ' '},
536      { "nobless",    LT_GENERIC,    ' ' },   {"nobless", LT_GENERIC, ' '},
537      { "nonvram",    LT_GENERIC,    ' ' },   {"nonvram", LT_GENERIC, ' '},
538      { "brokenosx",  LT_GENERIC,    ' ' },   {"brokenosx", LT_GENERIC, ' '},
539      { "usemount",   LT_GENERIC,    ' ' },   {"usemount", LT_GENERIC, ' '},
540      { "mntpoint",   LT_GENERIC,    '=' },   {"mntpoint", LT_GENERIC, '='},
541      { "partition",  LT_GENERIC,    '=' },   {"partition", LT_GENERIC, '='},
542      { "device",    LT_GENERIC,    '=' },   {"device", LT_GENERIC, '='},
543      { "fstype",    LT_GENERIC,    '=' },   {"fstype", LT_GENERIC, '='},
544      { "initrd",    LT_INITRD,    '=' },   {"initrd", LT_INITRD, '=', ';'},
545      { "append",    LT_KERNELARGS,  '=' },   {"append", LT_KERNELARGS, '='},
546      { "boot",    LT_BOOT,    '=' },   {"boot", LT_BOOT, '='},
547      { "lba",    LT_LBA,    ' ' },   {"lba", LT_LBA, ' '},
548      { NULL,    0, 0 },   {NULL, 0, 0},
549  };  };
550    
551  struct keywordTypes liloKeywords[] = {  struct keywordTypes liloKeywords[] = {
552      { "label",    LT_TITLE,    '=' },   {"label", LT_TITLE, '='},
553      { "root",    LT_ROOT,    '=' },   {"root", LT_ROOT, '='},
554      { "default",    LT_DEFAULT,    '=' },   {"default", LT_DEFAULT, '='},
555      { "image",    LT_KERNEL,    '=' },   {"image", LT_KERNEL, '='},
556      { "other",    LT_OTHER,    '=' },   {"other", LT_OTHER, '='},
557      { "initrd",    LT_INITRD,    '=' },   {"initrd", LT_INITRD, '='},
558      { "append",    LT_KERNELARGS,  '=' },   {"append", LT_KERNELARGS, '='},
559      { "boot",    LT_BOOT,    '=' },   {"boot", LT_BOOT, '='},
560      { "lba",    LT_LBA,    ' ' },   {"lba", LT_LBA, ' '},
561      { NULL,    0, 0 },   {NULL, 0, 0},
562    };
563    
564    struct keywordTypes eliloKeywords[] = {
565     {"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  };  };
574    
575  struct keywordTypes siloKeywords[] = {  struct keywordTypes siloKeywords[] = {
576      { "label",    LT_TITLE,    '=' },   {"label", LT_TITLE, '='},
577      { "root",    LT_ROOT,    '=' },   {"root", LT_ROOT, '='},
578      { "default",    LT_DEFAULT,    '=' },   {"default", LT_DEFAULT, '='},
579      { "image",    LT_KERNEL,    '=' },   {"image", LT_KERNEL, '='},
580      { "other",    LT_OTHER,    '=' },   {"other", LT_OTHER, '='},
581      { "initrd",    LT_INITRD,    '=' },   {"initrd", LT_INITRD, '='},
582      { "append",    LT_KERNELARGS,  '=' },   {"append", LT_KERNELARGS, '='},
583      { "boot",    LT_BOOT,    '=' },   {"boot", LT_BOOT, '='},
584      { NULL,    0, 0 },   {NULL, 0, 0},
585  };  };
586    
587  struct keywordTypes ziplKeywords[] = {  struct keywordTypes ziplKeywords[] = {
588      { "target",     LT_BOOTROOT,    '=' },   {"target", LT_BOOTROOT, '='},
589      { "image",      LT_KERNEL,      '=' },   {"image", LT_KERNEL, '='},
590      { "ramdisk",    LT_INITRD,      '=' },   {"ramdisk", LT_INITRD, '='},
591      { "parameters", LT_KERNELARGS,  '=' },   {"parameters", LT_KERNELARGS, '='},
592      { "default",    LT_DEFAULT,     '=' },   {"default", LT_DEFAULT, '='},
593      { NULL,         0, 0 },   {NULL, 0, 0},
594    };
595    
596    struct keywordTypes extlinuxKeywords[] = {
597     {"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  };  };
608    
609    int useextlinuxmenu;
610  struct configFileInfo eliloConfigType = {  struct configFileInfo eliloConfigType = {
611      "/boot/efi/EFI/redhat/elilo.conf",    /* defaultConfig */   .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf",
612      liloKeywords,    /* keywords */   .keywords = eliloKeywords,
613      0,    /* defaultIsIndex */   .entryStart = LT_KERNEL,
614      0,    /* defaultSupportSaved */   .needsBootPrefix = 1,
615      LT_KERNEL,    /* entrySeparator */   .argsInQuotes = 1,
616      1,                    /* needsBootPrefix */   .mbConcatArgs = 1,
617      1,    /* argsInQuotes */   .titlePosition = 1,
     0,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
618  };  };
619    
620  struct configFileInfo liloConfigType = {  struct configFileInfo liloConfigType = {
621      "/etc/lilo.conf",    /* defaultConfig */   .defaultConfig = "/etc/lilo.conf",
622      liloKeywords,    /* keywords */   .keywords = liloKeywords,
623      0,    /* defaultIsIndex */   .entryStart = LT_KERNEL,
624      0,    /* defaultSupportSaved */   .argsInQuotes = 1,
625      LT_KERNEL,    /* entrySeparator */   .maxTitleLength = 15,
626      0,    /* needsBootPrefix */   .titlePosition = 1,
     1,    /* argsInQuotes */  
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
627  };  };
628    
629  struct configFileInfo yabootConfigType = {  struct configFileInfo yabootConfigType = {
630      "/etc/yaboot.conf",    /* defaultConfig */   .defaultConfig = "/etc/yaboot.conf",
631      yabootKeywords,    /* keywords */   .keywords = yabootKeywords,
632      0,    /* defaultIsIndex */   .entryStart = LT_KERNEL,
633      0,    /* defaultSupportSaved */   .needsBootPrefix = 1,
634      LT_KERNEL,    /* entrySeparator */   .argsInQuotes = 1,
635      1,    /* needsBootPrefix */   .maxTitleLength = 15,
636      1,    /* argsInQuotes */   .mbAllowExtraInitRds = 1,
637      15,    /* maxTitleLength */   .titlePosition = 1,
     0,                                      /* titleBracketed */  
638  };  };
639    
640  struct configFileInfo siloConfigType = {  struct configFileInfo siloConfigType = {
641      "/etc/silo.conf",    /* defaultConfig */   .defaultConfig = "/etc/silo.conf",
642      siloKeywords,    /* keywords */   .keywords = siloKeywords,
643      0,    /* defaultIsIndex */   .entryStart = LT_KERNEL,
644      0,    /* defaultSupportSaved */   .needsBootPrefix = 1,
645      LT_KERNEL,    /* entrySeparator */   .argsInQuotes = 1,
646      1,    /* needsBootPrefix */   .maxTitleLength = 15,
647      1,    /* argsInQuotes */   .titlePosition = 1,
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
648  };  };
649    
650  struct configFileInfo ziplConfigType = {  struct configFileInfo ziplConfigType = {
651      "/etc/zipl.conf",    /* defaultConfig */   .defaultConfig = "/etc/zipl.conf",
652      ziplKeywords,    /* keywords */   .keywords = ziplKeywords,
653      0,    /* defaultIsIndex */   .entryStart = LT_TITLE,
654      0,    /* defaultSupportSaved */   .argsInQuotes = 1,
655      LT_TITLE,    /* entrySeparator */   .titleBracketed = 1,
     0,    /* needsBootPrefix */  
     1,    /* argsInQuotes */  
     15,    /* maxTitleLength */  
     1,                                      /* titleBracketed */  
656  };  };
657    
658  struct grubConfig {  struct configFileInfo extlinuxConfigType = {
659      struct singleLine * theLines;   .defaultConfig = "/boot/extlinux/extlinux.conf",
660      struct singleEntry * entries;   .keywords = extlinuxKeywords,
661      char * primaryIndent;   .caseInsensitive = 1,
662      char * secondaryIndent;   .entryStart = LT_TITLE,
663      int defaultImage;    /* -1 if none specified -- this value is   .needsBootPrefix = 1,
664       * written out, overriding original */   .maxTitleLength = 255,
665      int fallbackImage;    /* just like defaultImage */   .mbAllowExtraInitRds = 1,
666      int flags;   .defaultIsUnquoted = 1,
667      struct configFileInfo * cfi;   .titlePosition = 1,
668  };  };
669    
670    struct grubConfig {
671     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    };
681    
682  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);  blkid_cache blkid;
 struct singleEntry * findEntryByPath(struct grubConfig * cfg,  
      const char * path, const char * prefix,  
      int * index);  
 static char * strndup(char * from, int len);  
 static int readFile(int fd, char ** bufPtr);  
 static void lineInit(struct singleLine * line);  
 static void lineFree(struct singleLine * line);  
 static int lineWrite(FILE * out, struct singleLine * line,  
      struct configFileInfo * cfi);  
 static int getNextLine(char ** bufPtr, struct singleLine * line,  
        struct configFileInfo * cfi);  
 static char * getRootSpecifier(char * str);  
   
 static char * strndup(char * from, int len) {  
     char * to;  
   
     to = malloc(len + 1);  
     strncpy(to, from, len);  
     to[len] = '\0';  
683    
684      return to;  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    
720  static char * sdupprintf(const char *format, ...)  static char *sdupprintf(const char *format, ...)
721  #ifdef __GNUC__  #ifdef __GNUC__
722          __attribute__ ((format (printf, 1, 2)));      __attribute__ ((format(printf, 1, 2)));
723  #else  #else
724          ;  ;
725  #endif  #endif
726    
727  static char * sdupprintf(const char *format, ...) {  static char *sdupprintf(const char *format, ...)
728      char *buf = NULL;  {
729      char c;   char *buf = NULL;
730      va_list args;   char c;
731      size_t size = 0;   va_list args;
732      va_start(args, format);   size_t size = 0;
733         va_start(args, format);
734      /* XXX requires C99 vsnprintf behavior */  
735      size = vsnprintf(&c, 1, format, args) + 1;   /* XXX requires C99 vsnprintf behavior */
736      if (size == -1) {   size = vsnprintf(&c, 1, format, args) + 1;
737   printf("ERROR: vsnprintf behavior is not C99\n");   if (size == -1) {
738   abort();   printf("ERROR: vsnprintf behavior is not C99\n");
739      }   abort();
740     }
741      va_end(args);  
742      va_start(args, format);   va_end(args);
743     va_start(args, format);
744      buf = malloc(size);  
745      if (buf == NULL)   buf = malloc(size);
746   return NULL;   if (buf == NULL)
747      vsnprintf(buf, size, format, args);   return NULL;
748      va_end (args);   vsnprintf(buf, size, format, args);
749     va_end(args);
750    
751      return buf;   return buf;
752  }  }
753    
754  static int isBracketedTitle(struct singleLine * line) {  static inline int
755      if ((*line->elements[0].item == '[') && (line->numElements == 1)) {  kwcmp(struct keywordTypes *kw, const char * label, int case_insensitive)
756          int len = strlen(line->elements[0].item);  {
757          if (*(line->elements[0].item + len - 1) == ']') {      int kwl = strlen(kw->key);
758              /* FIXME: this is a hack... */      int ll = strlen(label);
759              if (strcmp(line->elements[0].item, "[defaultboot]")) {      int rc;
760                  return 1;      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;      return 0;
779  }  }
780    
781  /* figure out if this is a entry separator */  static enum lineType_e preferredLineType(enum lineType_e type,
782  static int isEntrySeparator(struct singleLine * line,   struct configFileInfo *cfi)
783                              struct configFileInfo * cfi) {  {
784      if (line->type == LT_WHITESPACE)   if (isEfi && cfi == &grub2ConfigType) {
785   return 0;   switch (type) {
786      if (line->type == cfi->entrySeparator)   case LT_KERNEL:
787          return 1;   return isEfiOnly ? LT_KERNEL : LT_KERNEL_EFI;
788      if (line->type == LT_OTHER)   case LT_INITRD:
789          return 1;   return isEfiOnly ? LT_INITRD : LT_INITRD_EFI;
790      if (cfi->titleBracketed && isBracketedTitle(line)) {   default:
791          return 1;   return type;
792      }   }
793      return 0;  #if defined(__i386__) || defined(__x86_64__)
794     } 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     }
805     return type;
806  }  }
807    
808  /* extract the title from within brackets (for zipl) */  static struct keywordTypes *getKeywordByType(enum lineType_e type,
809  static char * extractTitle(struct singleLine * line) {       struct configFileInfo *cfi)
810      /* bracketed title... let's extract it (leaks a byte) */  {
811      char * title;   for (struct keywordTypes * kw = cfi->keywords; kw->key; kw++) {
812      title = strdup(line->elements[0].item);   if (kw->type == type)
813      title++;   return kw;
814      *(title + strlen(title) - 1) = '\0';   }
815      return title;   return NULL;
816  }  }
   
 static int readFile(int fd, char ** bufPtr) {  
     int alloced = 0, size = 0, i = 0;  
     char * buf = NULL;  
   
     do {  
  size += i;  
  if ((size + 1024) > alloced) {  
     alloced += 4096;  
     buf = realloc(buf, alloced + 1);  
  }  
     } while ((i = read(fd, buf + size, 1024)) > 0);  
   
     if (i < 0) {  
  fprintf(stderr, _("error reading input: %s\n"), strerror(errno));  
         free(buf);  
  return 1;  
     }  
817    
818      buf = realloc(buf, size + 2);  static char *getKeyByType(enum lineType_e type, struct configFileInfo *cfi)
819      if (size == 0)  {
820          buf[size++] = '\n';   struct keywordTypes *kt = getKeywordByType(type, cfi);
821      else   if (kt)
822          if (buf[size - 1] != '\n')   return kt->key;
823              buf[size++] = '\n';   return "unknown";
824      buf[size] = '\0';  }
825    
826      *bufPtr = buf;  static char *getpathbyspec(char *device)
827    {
828     if (!blkid)
829     blkid_get_cache(&blkid, NULL);
830    
831      return 0;   return blkid_get_devname(blkid, device, NULL);
832  }  }
833    
834  static void lineInit(struct singleLine * line) {  static char *getuuidbydev(char *device)
835      line->indent = NULL;  {
836      line->elements = NULL;   if (!blkid)
837      line->numElements = 0;   blkid_get_cache(&blkid, NULL);
838      line->next = NULL;  
839     return blkid_get_tag_value(blkid, "UUID", device);
840  }  }
841    
842  static void lineFree(struct singleLine * line) {  static enum lineType_e getTypeByKeyword(char *keyword,
843      int i;   struct configFileInfo *cfi)
844    {
845     for (struct keywordTypes * kw = cfi->keywords; kw->key; kw++) {
846     if (!kwcmp(kw, keyword, cfi->caseInsensitive))
847     return kw->type;
848     }
849     return LT_UNKNOWN;
850    }
851    
852      if (line->indent) free(line->indent);  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    }
866    
867      for (i = 0; i < line->numElements; i++) {  static int isBracketedTitle(struct singleLine *line)
868   free(line->elements[i].item);  {
869   free(line->elements[i].indent);   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    }
880    
881      if (line->elements) free(line->elements);  static int isEntryStart(struct singleLine *line, struct configFileInfo *cfi)
882      lineInit(line);  {
883     return line->type == cfi->entryStart || line->type == LT_OTHER ||
884        (cfi->titleBracketed && isBracketedTitle(line));
885  }  }
886    
887  static int lineWrite(FILE * out, struct singleLine * line,  /* extract the title from within brackets (for zipl) */
888       struct configFileInfo * cfi) {  static char *extractTitle(struct grubConfig *cfg, struct singleLine *line)
889      int i;  {
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    }
907    
908      if (fprintf(out, "%s", line->indent) == -1) return -1;  static int readFile(int fd, char **bufPtr)
909    {
910     int alloced = 0, size = 0, i = 0;
911     char *buf = NULL;
912    
913     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      for (i = 0; i < line->numElements; i++) {   if (i < 0) {
922   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)   fprintf(stderr, _("error reading input: %s\n"),
923      if (fputc('"', out) == EOF) return -1;   strerror(errno));
924     free(buf);
925     return 1;
926     }
927    
928   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;   buf = realloc(buf, size + 2);
929   if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;   if (size == 0)
930      }   buf[size++] = '\n';
931     else if (buf[size - 1] != '\n')
932     buf[size++] = '\n';
933     buf[size] = '\0';
934    
935      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)   *bufPtr = buf;
  if (fputc('"', out) == EOF) return -1;  
936    
937      if (fprintf(out, "\n") == -1) return -1;   return 0;
938    }
939    
940      return 0;  static void lineInit(struct singleLine *line)
941    {
942     line->type = LT_UNIDENTIFIED;
943     line->indent = NULL;
944     line->elements = NULL;
945     line->numElements = 0;
946     line->next = NULL;
947  }  }
948    
949  /* we've guaranteed that the buffer ends w/ \n\0 */  struct singleLine *lineDup(struct singleLine *line)
950  static int getNextLine(char ** bufPtr, struct singleLine * line,  {
951                         struct configFileInfo * cfi) {   struct singleLine *newLine = malloc(sizeof(*newLine));
952      char * end;  
953      char * start = *bufPtr;   newLine->indent = strdup(line->indent);
954      char * chptr;   newLine->next = NULL;
955      int elementsAlloced = 0;   newLine->type = line->type;
956      struct lineElement * element;   newLine->numElements = line->numElements;
957      struct keywordTypes * keywords = cfi->keywords;   newLine->elements = malloc(sizeof(*newLine->elements) *
958      int first = 1;     newLine->numElements);
959      int i;  
960     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    
965     return newLine;
966    }
967    
968      lineFree(line);  static void lineFree(struct singleLine *line)
969    {
970     if (line->indent)
971     free(line->indent);
972    
973      end = strchr(start, '\n');   for (int i = 0; i < line->numElements; i++) {
974      *end = '\0';   free(line->elements[i].item);
975      *bufPtr = end + 1;   free(line->elements[i].indent);
976     }
977    
978      for (chptr = start; *chptr && isspace(*chptr); chptr++) ;   if (line->elements)
979     free(line->elements);
980     lineInit(line);
981    }
982    
983      line->indent = strndup(start, chptr - start);  static int lineWrite(FILE * out, struct singleLine *line,
984      start = chptr;       struct configFileInfo *cfi)
985    {
986     if (fprintf(out, "%s", line->indent) == -1)
987     return -1;
988    
989     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      while (start < end) {   continue;
1017   /* we know !isspace(*start) */   }
1018    
1019   if (elementsAlloced == line->numElements) {   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)
1020      elementsAlloced += 5;   if (fputc('"', out) == EOF)
1021      line->elements = realloc(line->elements,   return -1;
1022   sizeof(*line->elements) * elementsAlloced);  
1023   }   if (fprintf(out, "%s", line->elements[i].item) == -1)
1024     return -1;
1025     if (i < line->numElements - 1 || line->type == LT_SET_VARIABLE)
1026     if (fprintf(out, "%s", line->elements[i].indent) == -1)
1027     return -1;
1028     }
1029    
1030     if (line->type == LT_KERNELARGS && cfi->argsInQuotes)
1031     if (fputc('"', out) == EOF)
1032     return -1;
1033    
1034   element = line->elements + line->numElements;   if (fprintf(out, "\n") == -1)
1035     return -1;
1036    
1037   chptr = start;   return 0;
1038   while (*chptr && !isspace(*chptr)) {  }
1039      if (first && *chptr == '=') break;  
1040      chptr++;  /* we've guaranteed that the buffer ends w/ \n\0 */
1041   }  static int getNextLine(char **bufPtr, struct singleLine *line,
1042   element->item = strndup(start, chptr - start);         struct configFileInfo *cfi)
1043   start = chptr;  {
1044     char *end;
1045     char *start = *bufPtr;
1046     char *chptr;
1047     int elementsAlloced = 0;
1048     struct lineElement *element;
1049     int first = 1;
1050    
1051     lineFree(line);
1052    
1053     end = strchr(start, '\n');
1054     *end = '\0';
1055     *bufPtr = end + 1;
1056    
1057          /* lilo actually accepts the pathological case of append = " foo " */   for (chptr = start; *chptr && isspace(*chptr); chptr++) ;
         if (*start == '=')  
             chptr = start + 1;  
         else  
             chptr = start;  
   
         do {  
             for (; *chptr && isspace(*chptr); chptr++);  
             if (*chptr == '=')  
                 chptr = chptr + 1;  
         } while (isspace(*chptr));  
1058    
1059   element->indent = strndup(start, chptr - start);   line->indent = strndup(start, chptr - start);
1060   start = chptr;   start = chptr;
1061    
1062   line->numElements++;   while (start < end) {
1063   first = 0;   /* we know !isspace(*start) */
     }  
1064    
1065      if (!line->numElements)   if (elementsAlloced == line->numElements) {
1066   line->type = LT_WHITESPACE;   elementsAlloced += 5;
1067      else {   line->elements = realloc(line->elements,
1068   for (i = 0; keywords[i].key; i++)   sizeof(*line->elements) *
1069      if (!strcmp(line->elements[0].item, keywords[i].key)) break;   elementsAlloced);
1070     }
  if (keywords[i].key) {  
     line->type = keywords[i].type;  
  } else {  
     line->type = LT_UNKNOWN;  
               
             /* zipl does [title] instead of something reasonable like all  
              * the other boot loaders.  kind of ugly */  
             if (cfi->titleBracketed && isBracketedTitle(line)) {  
                 line->type = LT_TITLE;  
             }  
   
     /* this is awkward, but we need to be able to handle keywords  
        that begin with a # (specifically for #boot in grub.conf),  
        but still make comments lines with no elements (everything  
        stored in the indent */  
     if (*line->elements[0].item == '#') {  
  char * fullLine;  
  int len;  
  int i;  
   
  len = strlen(line->indent);  
  for (i = 0; i < line->numElements; i++)  
     len += strlen(line->elements[i].item) +  
    strlen(line->elements[i].indent);  
1071    
1072   fullLine = malloc(len + 1);   element = line->elements + line->numElements;
  strcpy(fullLine, line->indent);  
  free(line->indent);  
  line->indent = fullLine;  
1073    
1074   for (i = 0; i < line->numElements; i++) {   chptr = start;
1075      strcat(fullLine, line->elements[i].item);   while (*chptr && !isspace(*chptr)) {
1076      strcat(fullLine, line->elements[i].indent);   if (first && *chptr == '=')
1077      free(line->elements[i].item);   break;
1078      free(line->elements[i].indent);   chptr++;
1079   }   }
1080     if (line->type == LT_UNIDENTIFIED)
1081     line->type = getTypeByKeyword(start, cfi);
1082     element->item = strndup(start, chptr - start);
1083     start = chptr;
1084    
1085     /* lilo actually accepts the pathological case of
1086     * append = " foo " */
1087     if (*start == '=')
1088     chptr = start + 1;
1089     else
1090     chptr = start;
1091    
1092     do {
1093     for (; *chptr && isspace(*chptr); chptr++) ;
1094     if (*chptr == '=')
1095     chptr = chptr + 1;
1096     } while (isspace(*chptr));
1097    
1098     element->indent = strndup(start, chptr - start);
1099     start = chptr;
1100    
1101     line->numElements++;
1102     first = 0;
1103     }
1104    
1105     if (!line->numElements)
1106   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1107   line->numElements = 0;   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    
1116     /* 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    
1125     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    
1130     fullLine = malloc(len + 1);
1131     strcpy(fullLine, line->indent);
1132     free(line->indent);
1133     line->indent = fullLine;
1134    
1135     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    
1144     line->type = LT_WHITESPACE;
1145     line->numElements = 0;
1146     }
1147     } else if (line->type == LT_INITRD) {
1148     struct keywordTypes *kw;
1149    
1150     kw = getKeywordByType(line->type, cfi);
1151    
1152     /* 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    
1162     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    
1180     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    
1188     p = line->elements[i].item;
1189     while (*p != '\0') {
1190    
1191     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     }
1208     }
1209     } 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     }
1243   }   }
     }  
1244    
1245      return 0;   return 0;
1246  }  }
1247    
1248  static struct grubConfig * readConfig(const char * inName,  static int isnumber(const char *s)
1249        struct configFileInfo * cfi) {  {
1250      int in;   int i;
1251      char * incoming = NULL, * head;   for (i = 0; s[i] != '\0'; i++)
1252      int rc;   if (s[i] < '0' || s[i] > '9')
1253      int sawEntry = 0;   return 0;
1254      int movedLine = 0;   return i;
1255      struct grubConfig * cfg;  }
1256      struct singleLine * last = NULL, * line, * defaultLine = NULL;  
1257      char * end;  static struct grubConfig *readConfig(const char *inName,
1258      struct singleEntry * entry = NULL;       struct configFileInfo *cfi)
1259      int i, len;  {
1260      char * buf;   int in;
1261     char *incoming = NULL, *head;
1262      if (!strcmp(inName, "-")) {   int rc;
1263   in = 0;   int sawEntry = 0;
1264      } else {   int movedLine = 0;
1265   if ((in = open(inName, O_RDONLY)) < 0) {   struct grubConfig *cfg;
1266      fprintf(stderr, _("error opening %s for read: %s\n"),   struct singleLine *last = NULL, *line, *defaultLine = NULL;
1267      inName, strerror(errno));   char *end;
1268      return NULL;   struct singleEntry *entry = NULL;
1269     int len;
1270     char *buf;
1271    
1272     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   }   }
     }  
1284    
1285      rc = readFile(in, &incoming);   rc = readFile(in, &incoming);
1286      close(in);   close(in);
1287      if (rc) return NULL;   if (rc)
1288     return NULL;
1289      head = incoming;  
1290      cfg = malloc(sizeof(*cfg));   head = incoming;
1291      cfg->primaryIndent = strdup("");   cfg = malloc(sizeof(*cfg));
1292      cfg->secondaryIndent = strdup("\t");   cfg->primaryIndent = strdup("");
1293      cfg->flags = GRUB_CONFIG_NO_DEFAULT;   cfg->secondaryIndent = strdup("\t");
1294      cfg->cfi = cfi;   cfg->flags = GRUB_CONFIG_NO_DEFAULT;
1295      cfg->theLines = NULL;   cfg->cfi = cfi;
1296      cfg->entries = NULL;   cfg->theLines = NULL;
1297      cfg->fallbackImage = 0;   cfg->entries = NULL;
1298     cfg->fallbackImage = 0;
1299      /* copy everything we have */  
1300      while (*head) {   /* copy everything we have */
1301   line = malloc(sizeof(*line));   while (*head) {
1302   lineInit(line);   line = malloc(sizeof(*line));
1303     lineInit(line);
1304    
1305     if (getNextLine(&head, line, cfi)) {
1306     free(line);
1307     /* XXX memory leak of everything in cfg */
1308     return NULL;
1309     }
1310    
1311   if (getNextLine(&head, line, cfi)) {   if (!sawEntry && line->numElements) {
1312      free(line);   free(cfg->primaryIndent);
1313      /* XXX memory leak of everything in cfg */   cfg->primaryIndent = strdup(line->indent);
1314      return NULL;   } else if (line->numElements) {
1315   }   free(cfg->secondaryIndent);
1316     cfg->secondaryIndent = strdup(line->indent);
1317   if (!sawEntry && line->numElements) {   }
     free(cfg->primaryIndent);  
     cfg->primaryIndent = strdup(line->indent);  
  } else if (line->numElements) {  
     free(cfg->secondaryIndent);  
     cfg->secondaryIndent = strdup(line->indent);  
  }  
   
  if (isEntrySeparator(line, cfi)) {  
     sawEntry = 1;  
     if (!entry) {  
  cfg->entries = malloc(sizeof(*entry));  
  entry = cfg->entries;  
     } else {  
  entry->next = malloc(sizeof(*entry));  
  entry = entry->next;  
     }  
1318    
1319      entry->skip = 0;   if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
1320              entry->multiboot = 0;   sawEntry = 1;
1321      entry->lines = NULL;   if (!entry) {
1322      entry->next = NULL;   cfg->entries = malloc(sizeof(*entry));
1323   }   entry = cfg->entries;
1324     } else {
1325   if (line->type == LT_DEFAULT && line->numElements == 2) {   entry->next = malloc(sizeof(*entry));
1326      cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;   entry = entry->next;
1327      defaultLine = line;   }
1328          } else if (line->type == LT_MBMODULE) {  
1329              entry->multiboot = 1;   entry->skip = 0;
1330   } else if (line->type == LT_FALLBACK && line->numElements == 2) {   entry->multiboot = 0;
1331      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);   entry->lines = NULL;
1332      if (*end) cfg->fallbackImage = -1;   entry->next = NULL;
1333   } else if (line->type == LT_TITLE && line->numElements > 1) {   }
     /* make the title a single argument (undoing our parsing) */  
     len = 0;  
     for (i = 1; i < line->numElements; i++) {  
  len += strlen(line->elements[i].item);  
  len += strlen(line->elements[i].indent);  
     }  
     buf = malloc(len + 1);  
     *buf = '\0';  
1334    
1335      for (i = 1; i < line->numElements; i++) {   if (line->type == LT_SET_VARIABLE) {
1336   strcat(buf, line->elements[i].item);   dbgPrintf("found 'set' command (%d elements): ",
1337   free(line->elements[i].item);    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        && !is_special_grub2_variable(
1348     line->elements[2].item)) {
1349     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    
1361     } 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    
1379     } else if (line->type == LT_HYPER) {
1380     entry->multiboot = 1;
1381    
1382     } 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    
1388     } 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    
1401     for (int i = 1; i < line->numElements; i++) {
1402     strcat(buf, line->elements[i].item);
1403     free(line->elements[i].item);
1404    
1405     if ((i + 1) != line->numElements) {
1406     strcat(buf, line->elements[i].indent);
1407     free(line->elements[i].indent);
1408     }
1409     }
1410    
1411     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    
1423     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    
1430     /* allocate mem for extra flags. */
1431     extras = malloc(len + 1);
1432     *extras = '\0';
1433    
1434     /* 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    
1448     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    
1458     /* 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    
1468     if (!strcmp
1469        (line->elements[i].item, "menuentry"))
1470     continue;
1471    
1472     /* 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    
1480     len = strlen(line->elements[i].item);
1481    
1482     if (line->elements[i].item[len - 1] ==
1483        quote_char)
1484     count++;
1485    
1486     /* 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    
1503     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    
1509     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    
1516   if ((i + 1) != line->numElements) {   if (line->type == LT_DEFAULT && line->numElements == 2) {
1517      strcat(buf, line->elements[i].indent);   cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1518      free(line->elements[i].indent);   defaultLine = line;
1519   }   }
     }  
   
     line->elements[1].indent =  
     line->elements[line->numElements - 1].indent;  
     line->elements[1].item = buf;  
     line->numElements = 2;  
  } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {  
     /* Strip off any " which may be present; they'll be put back  
        on write. This is one of the few (the only?) places that grubby  
        canonicalizes the output */  
   
     if (line->numElements >= 2) {  
  int last, len;  
   
  if (*line->elements[1].item == '"')  
     memcpy(line->elements[1].item, line->elements[1].item + 1,  
    strlen(line->elements[1].item + 1) + 1);  
   
  last = line->numElements - 1;  
  len = strlen(line->elements[last].item) - 1;  
  if (line->elements[last].item[len] == '"')  
     line->elements[last].item[len] = '\0';  
     }  
1520    
1521   }   /* 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    
1539   /* If we find a generic config option which should live at the   /* If a second line of whitespace happens after a generic
1540     top of the file, move it there. Old versions of grubby were   * option which was moved, drop it. */
1541     probably responsible for putting new images in the wrong   if (movedLine && line->type == LT_WHITESPACE
1542     place in front of it anyway. */      && last->type == LT_WHITESPACE) {
1543   if (sawEntry && line->type == LT_GENERIC) {   lineFree(line);
1544   struct singleLine **l = &cfg->theLines;   free(line);
1545   struct singleLine **last_nonws = &cfg->theLines;   movedLine = 0;
1546   while (*l) {   continue;
1547   if ((*l)->type != LT_WHITESPACE)   }
  last_nonws = &((*l)->next);  
  l = &((*l)->next);  
  }  
  line->next = *last_nonws;  
  *last_nonws = line;  
  movedLine = 1;  
  continue; /* without setting 'last' */  
  }  
  /* If a second line of whitespace happens after a generic option  
    which was moved, drop it. */  
  if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {  
  lineFree(line);  
  free(line);  
1548   movedLine = 0;   movedLine = 0;
1549   continue;  
1550     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    
1558     /* 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   }   }
  movedLine = 0;  
1574    
1575   if (sawEntry) {   free(incoming);
1576      if (!entry->lines)  
1577   entry->lines = line;   dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");
1578      else   if (defaultLine) {
1579   last->next = line;   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     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     } 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    
1650     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     int index = 0;
1676     if (isnumber(defTitle)) {
1677     index = atoi(defTitle);
1678     entry = findEntryByIndex(cfg, index);
1679     } else {
1680     entry = findEntryByTitle(cfg, defTitle, &index);
1681     }
1682     if (entry)
1683     cfg->defaultImage = index;
1684     }
1685   } else {   } else {
1686      if (!cfg->theLines)   cfg->defaultImage = 0;
  cfg->theLines = line;  
     else {  
  last->next = line;  
     }  
1687   }   }
1688    
1689   last = line;   return cfg;
1690      }  }
   
     free(incoming);  
1691    
1692      if (defaultLine) {  static void writeDefault(FILE * out, char *indent,
1693   if (cfi->defaultSupportSaved &&   char *separator, struct grubConfig *cfg)
1694   !strncmp(defaultLine->elements[1].item, "saved", 5)) {  {
1695      cfg->defaultImage = DEFAULT_SAVED;   struct singleEntry *entry;
1696   } else if (cfi->defaultIsIndex) {   struct singleLine *line;
1697      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);   int i;
1698      if (*end) cfg->defaultImage = -1;  
1699   } else if (defaultLine->numElements >= 2) {   if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT)
1700      i = 0;   return;
1701      while ((entry = findEntryByIndex(cfg, i))) {  
1702   for (line = entry->lines; line; line = line->next)   if (cfg->defaultImage == DEFAULT_SAVED)
1703      if (line->type == LT_TITLE) break;   fprintf(out, "%sdefault%ssaved\n", indent, separator);
1704     else if (cfg->cfi->defaultIsSaved) {
1705                  if (!cfi->titleBracketed) {   fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1706                      if (line && (line->numElements >= 2) &&   if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {
1707                          !strcmp(defaultLine->elements[1].item,   char *title;
1708                                  line->elements[1].item)) break;   entry = findEntryByIndex(cfg, cfg->defaultImage);
1709                  } else if (line) {   line = getLineByType(LT_MENUENTRY, entry->lines);
1710                      if (!strcmp(defaultLine->elements[1].item,   if (!line)
1711                                  extractTitle(line))) break;   line = getLineByType(LT_TITLE, entry->lines);
1712                  }   if (line) {
1713   i++;   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    
1731      if (entry) cfg->defaultImage = i;   entry = cfg->entries;
1732     while (entry && entry->skip)
1733     entry = entry->next;
1734    
1735     i = 0;
1736     while (entry && i < image) {
1737     entry = entry->next;
1738    
1739     while (entry && entry->skip)
1740     entry = entry->next;
1741     i++;
1742     }
1743    
1744     if (!entry)
1745     return;
1746    
1747     line = getLineByType(LT_TITLE, entry->lines);
1748    
1749     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     }
1762   }   }
     }  
   
     return cfg;  
1763  }  }
1764    
1765  static void writeDefault(FILE * out, char * indent,  static int writeConfig(struct grubConfig *cfg, char *outName,
1766   char * separator, struct grubConfig * cfg) {         const char *prefix)
1767      struct singleEntry * entry;  {
1768      struct singleLine * line;   FILE *out;
1769      int i;   struct singleLine *line;
1770     struct singleEntry *entry;
1771     char *tmpOutName;
1772     int needs = MAIN_DEFAULT;
1773     struct stat sb;
1774     int i;
1775    
1776     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    
1785     /* 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    
1796     if (rc < 0) {
1797     fprintf(stderr,
1798     _
1799     ("grubby: error readlink link %s: %s\n"),
1800     outName, strerror(errno));
1801     return 1;
1802     }
1803    
1804      if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT) return;   outName = buf;
1805     outName[rc] = '\0';
1806     }
1807    
1808      if (cfg->defaultImage == DEFAULT_SAVED)   tmpOutName = alloca(strlen(outName) + 2);
1809   fprintf(out, "%sdefault%ssaved\n", indent, separator);   sprintf(tmpOutName, "%s-", outName);
1810      else if (cfg->defaultImage > -1) {   out = fopen(tmpOutName, "w");
1811   if (cfg->cfi->defaultIsIndex) {   if (!out) {
1812      fprintf(out, "%sdefault%s%d\n", indent, separator,   fprintf(stderr, _("grubby: error creating %s: %s\n"),
1813      cfg->defaultImage);   tmpOutName, strerror(errno));
1814   } else {   return 1;
1815      int image = cfg->defaultImage;   }
1816    
1817      entry = cfg->entries;   if (!stat(outName, &sb)) {
1818      while (entry && entry->skip) entry = entry->next;   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     }
1829    
1830      i = 0;   line = cfg->theLines;
1831      while (entry && i < image) {   struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1832   entry = entry->next;   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    
1861   while (entry && entry->skip) entry = entry->next;   line = line->next;
1862   i++;   }
     }  
1863    
1864      if (!entry) return;   if (needs & MAIN_DEFAULT) {
1865     writeDefault(out, cfg->primaryIndent, "=", cfg);
1866     needs &= ~MAIN_DEFAULT;
1867     }
1868    
1869      line = entry->lines;   i = 0;
1870      while (line && line->type != LT_TITLE) line = line->next;   while ((entry = findEntryByIndex(cfg, i++))) {
1871     if (entry->skip)
1872     continue;
1873    
1874     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     }
1887    
1888      if (line && line->numElements >= 2)   if (tmpOutName) {
1889   fprintf(out, "%sdefault%s%s\n", indent, separator,   if (rename(tmpOutName, outName)) {
1890   line->elements[1].item);   fprintf(stderr,
1891              else if (line && (line->numElements == 1) &&   _("grubby: error moving %s to %s: %s\n"),
1892                       cfg->cfi->titleBracketed) {   tmpOutName, outName, strerror(errno));
1893   fprintf(out, "%sdefault%s%s\n", indent, separator,   unlink(outName);
1894                          extractTitle(line));   return 1;
1895              }   }
1896   }   }
1897      }  
1898     return 0;
1899  }  }
1900    
1901  static int writeConfig(struct grubConfig * cfg, char * outName,  static int numEntries(struct grubConfig *cfg)
1902         const char * prefix) {  {
1903      FILE * out;   int i = 0;
1904      struct singleLine * line;   struct singleEntry *entry;
1905      struct singleEntry * entry;  
1906      char * tmpOutName;   entry = cfg->entries;
1907      int needs = MAIN_DEFAULT;   while (entry) {
1908      struct stat sb;   if (!entry->skip)
1909      int i;   i++;
1910     entry = entry->next;
1911      if (!strcmp(outName, "-")) {   }
1912   out = stdout;   return i;
1913   tmpOutName = NULL;  }
     } else {  
  if (!lstat(outName, &sb) && S_ISLNK(sb.st_mode)) {  
     char * buf;  
     int len = 256;  
     int rc;  
   
     /* most likely the symlink is relative, so change our  
        directory to / */  
     rc = chdir("/");  
     do {  
  buf = alloca(len + 1);  
  rc = readlink(outName, buf, len);  
  if (rc == len) len += 256;  
     } while (rc == len);  
       
     if (rc < 0) {  
  fprintf(stderr, _("grubby: error readlink link %s: %s\n"),  
  outName, strerror(errno));  
  return 1;  
     }  
   
     outName = buf;  
     outName[rc] = '\0';  
  }  
   
  tmpOutName = alloca(strlen(outName) + 2);  
  sprintf(tmpOutName, "%s-", outName);  
  out = fopen(tmpOutName, "w");  
  if (!out) {  
     fprintf(stderr, _("grubby: error creating %s: %s\n"), tmpOutName,  
     strerror(errno));  
     return 1;  
  }  
   
  if (!stat(outName, &sb)) {  
     if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {  
  fprintf(stderr, _("grubby: error setting perms on %s: %s\n"),  
         tmpOutName, strerror(errno));  
  fclose(out);  
  unlink(tmpOutName);  
                 return 1;  
     }  
  }  
     }  
1914    
1915      line = cfg->theLines;  static char *findDiskForRoot()
1916      while (line) {  {
1917   if (line->type == LT_DEFAULT) {   int fd;
1918      writeDefault(out, line->indent, line->elements[0].indent, cfg);   char buf[65536];
1919      needs &= ~MAIN_DEFAULT;   char *devname;
1920   } else if (line->type == LT_FALLBACK) {   char *chptr;
1921      if (cfg->fallbackImage > -1)   int rc;
1922   fprintf(out, "%s%s%s%d\n", line->indent,  
1923   line->elements[0].item, line->elements[0].indent,   if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1924   cfg->fallbackImage);   fprintf(stderr, "grubby: failed to open %s: %s\n",
1925   } else {   _PATH_MOUNTED, strerror(errno));
1926      if (lineWrite(out, line, cfg->cfi) == -1) {   return NULL;
1927                  fprintf(stderr, _("grubby: error writing %s: %s\n"),   }
1928                          tmpOutName, strerror(errno));  
1929                  fclose(out);   rc = read(fd, buf, sizeof(buf) - 1);
1930                  unlink(tmpOutName);   if (rc <= 0) {
1931                  return 1;   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    
1940   line = line->next;   char *foundanswer = NULL;
     }  
1941    
1942      if (needs & MAIN_DEFAULT) {   while (chptr && chptr != buf + rc) {
1943   writeDefault(out, cfg->primaryIndent, "=", cfg);   devname = chptr;
  needs &= ~MAIN_DEFAULT;  
     }  
1944    
1945      i = 0;   /*
1946      while ((entry = findEntryByIndex(cfg, i++))) {   * The first column of a mtab entry is the device, but if the
1947   if (entry->skip) continue;   * 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    
1957   line = entry->lines;   /* Seek to the next space */
1958   while (line) {   chptr = strchr(chptr, ' ');
1959      if (lineWrite(out, line, cfg->cfi) == -1) {   if (!chptr) {
1960                  fprintf(stderr, _("grubby: error writing %s: %s\n"),   fprintf(stderr, "grubby: error parsing %s: %s\n",
1961                          tmpOutName, strerror(errno));   _PATH_MOUNTED, strerror(errno));
1962                  fclose(out);   return NULL;
1963                  unlink(tmpOutName);   }
                 return 1;  
             }  
     line = line->next;  
  }  
     }  
1964    
1965      if (tmpOutName) {   /*
1966   if (rename(tmpOutName, outName)) {   * The second column of a mtab entry is the mount point, we
1967      fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),   * are looking for '/' obviously.
1968      tmpOutName, outName, strerror(errno));   */
1969      unlink(outName);   if (*(++chptr) == '/' && *(++chptr) == ' ') {
1970              return 1;   /* remember the last / entry in mtab */
1971     foundanswer = devname;
1972     }
1973    
1974     /* Next line */
1975     chptr = strchr(chptr, '\n');
1976     if (chptr)
1977     chptr++;
1978   }   }
     }  
1979    
1980      return 0;   /* Return the last / entry found */
1981  }   if (foundanswer) {
1982     chptr = strchr(foundanswer, ' ');
1983     *chptr = '\0';
1984     return strdup(foundanswer);
1985     }
1986    
1987  static int numEntries(struct grubConfig *cfg) {   return NULL;
     int i = 0;  
     struct singleEntry * entry;  
   
     entry = cfg->entries;  
     while (entry) {  
         if (!entry->skip)  
             i++;  
         entry = entry->next;  
     }  
     return i;  
1988  }  }
1989    
1990  int suitableImage(struct singleEntry * entry, const char * bootPrefix,  void printEntry(struct singleEntry *entry, FILE * f)
1991    int skipRemoved, int flags) {  {
1992      struct singleLine * line;   int i;
1993      char * fullName;   struct singleLine *line;
     int i;  
     struct stat sb, sb2;  
     char * dev;  
     char * end;  
     char * rootspec;  
   
     line = entry->lines;  
     while (line && line->type != LT_KERNEL) line = line->next;  
       
     if (!line) return 0;  
     if (skipRemoved && entry->skip) return 0;  
     if (line->numElements < 2) return 0;  
   
     if (flags & GRUBBY_BADIMAGE_OKAY) return 1;  
   
     fullName = alloca(strlen(bootPrefix) +  
       strlen(line->elements[1].item) + 1);  
     rootspec = getRootSpecifier(line->elements[1].item);  
     sprintf(fullName, "%s%s", bootPrefix,  
             line->elements[1].item + ((rootspec != NULL) ?  
                                       strlen(rootspec) : 0));  
     if (access(fullName, R_OK)) return 0;  
   
     for (i = 2; i < line->numElements; i++)  
  if (!strncasecmp(line->elements[i].item, "root=", 5)) break;  
     if (i < line->numElements) {  
  dev = line->elements[i].item + 5;  
     } else {  
  /* look for a lilo style LT_ROOT line */  
  line = entry->lines;  
  while (line && line->type != LT_ROOT) line = line->next;  
1994    
1995   if (line && line->numElements >= 2) {   for (line = entry->lines; line; line = line->next) {
1996      dev = line->elements[1].item;   log_message(f, "DBG: %s", line->indent);
1997   } else {   for (i = 0; i < line->numElements; i++) {
1998              int type;   /* Need to handle this, because we strip the quotes from
1999      /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS */   * menuentry when read it. */
2000      line = entry->lines;   if (line->type == LT_MENUENTRY && i == 1) {
2001     if (!isquote(*line->elements[i].item))
2002              type = ((entry->multiboot) ? LT_MBMODULE : LT_KERNELARGS);   log_message(f, "\'%s\'",
2003        line->elements[i].item);
2004      while (line && line->type != type) line = line->next;   else
2005     log_message(f, "%s",
2006              /* failed to find one */      line->elements[i].item);
2007              if (!line) return 0;   log_message(f, "%s", line->elements[i].indent);
2008    
2009      for (i = 1; i < line->numElements; i++)   continue;
2010          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;   }
2011      if (i < line->numElements)  
2012          dev = line->elements[i].item + 5;   log_message(f, "%s%s",
2013      else {      line->elements[i].item,
2014   /* it failed too...  can't find root= */      line->elements[i].indent);
2015          return 0;   }
2016              }   log_message(f, "\n");
2017   }   }
2018      }  }
   
     if (!strncmp(dev, "LABEL=", 6)) {  
  dev += 6;  
   
  /* check which device has this label */  
  dev = get_spec_by_volume_label(dev, &i, &i);  
  if (!dev) return 0;  
     }  
2019    
2020      if (*dev == '/') {  void notSuitablePrintf(struct singleEntry *entry, int okay, const char *fmt,
2021   if (stat(dev, &sb))         ...)
2022      return 0;  {
2023      } else {   static int once;
2024   sb.st_rdev = strtol(dev, &end, 16);   va_list argp, argq;
2025   if (*end) return 0;  
2026      }   va_start(argp, fmt);
2027      stat("/", &sb2);  
2028     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    
2037     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     once = 1;
2055     fprintf(stderr, "DBG: Image entry failed: ");
2056     vfprintf(stderr, fmt, argp);
2057     printEntry(entry, stderr);
2058     va_end(argp);
2059    }
2060    
2061      if (sb.st_rdev != sb2.st_dev) return 0;  #define beginswith(s, c) ((s) && (s)[0] == (c))
2062    
2063      return 1;  static int endswith(const char *s, char c)
2064  }  {
2065     int slen;
2066    
2067  /* returns the first match on or after the one pointed to by index (if index   if (!s || !s[0])
2068     is not NULL) which is not marked as skip */   return 0;
2069  struct singleEntry * findEntryByPath(struct grubConfig * config,   slen = strlen(s) - 1;
      const char * kernel, const char * prefix,  
      int * index) {  
     struct singleEntry * entry = NULL;  
     struct singleLine * line;  
     int i;  
     char * chptr;  
     char * rootspec = NULL;  
     enum lineType_e checkType = LT_KERNEL;  
2070    
2071      if (isdigit(*kernel)) {   return s[slen] == c;
2072   int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));  }
2073    
2074   i = 0;  int suitableImage(struct singleEntry *entry, const char *bootPrefix,
2075   indexVars[i] = strtol(kernel, &chptr, 10);    int skipRemoved, int flags)
2076   while (*chptr == ',') {  {
2077      i++;   struct singleLine *line;
2078      kernel = chptr + 1;   char *fullName;
2079      indexVars[i] = strtol(kernel, &chptr, 10);   int i;
2080     char *dev;
2081     char *rootspec;
2082     char *rootdev;
2083    
2084     if (skipRemoved && entry->skip) {
2085     notSuitablePrintf(entry, 0, "marked to skip\n");
2086     return 0;
2087     }
2088    
2089     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    
2102   if (*chptr) {   if (flags & GRUBBY_BADIMAGE_OKAY) {
2103      /* can't parse it, bail */   notSuitablePrintf(entry, 1, "\n");
2104      return NULL;   return 1;
2105   }   }
2106    
2107   indexVars[i + 1] = -1;   fullName = alloca(strlen(bootPrefix) +
2108      strlen(line->elements[1].item) + 1);
2109   i = 0;   rootspec = getRootSpecifier(line->elements[1].item);
2110   if (index) {   int rootspec_offset = rootspec ? strlen(rootspec) : 0;
2111      while (i < *index) i++;   int hasslash = endswith(bootPrefix, '/') ||
2112      if (indexVars[i] == -1) return NULL;      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     } else {
2125     /* look for a lilo style LT_ROOT line */
2126     line = getLineByType(LT_ROOT, entry->lines);
2127    
2128     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    
2139     /* 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   }   }
2159    
2160   entry = findEntryByIndex(config, indexVars[i]);   dev = getpathbyspec(dev);
2161   if (!entry) return NULL;   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    
2168     rootdev = findDiskForRoot();
2169     if (!rootdev) {
2170     notSuitablePrintf(entry, 0, "can't find root device\n");
2171     return 0;
2172     }
2173    
2174     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    
2182     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   line = entry->lines;   free(rootdev);
2191   while (line && line->type != LT_KERNEL)   notSuitablePrintf(entry, 1, "\n");
     line = line->next;  
2192    
2193   if (!line) return NULL;   return 1;
2194    }
2195    
2196   if (index) *index = indexVars[i];  /* returns the first match on or after the one pointed to by index (if index
2197   return entry;     is not NULL) which is not marked as skip */
2198      }  struct singleEntry *findEntryByPath(struct grubConfig *config,
2199            const char *kernel, const char *prefix,
2200      if (!strcmp(kernel, "DEFAULT")) {      int *index)
2201   if (index && *index > config->defaultImage) {  {
2202      entry = NULL;   struct singleEntry *entry = NULL;
2203   } else {   struct singleLine *line;
2204      entry = findEntryByIndex(config, config->defaultImage);   int i;
2205      if (entry && entry->skip)   char *chptr;
2206   entry = NULL;   char *rootspec = NULL;
2207      else if (index)   enum lineType_e checkType = LT_KERNEL;
2208   *index = config->defaultImage;  
2209   }   if (isdigit(*kernel)) {
2210      } else if (!strcmp(kernel, "ALL")) {   int *indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
2211   if (index)  
2212      i = *index;   i = 0;
2213   else   indexVars[i] = strtol(kernel, &chptr, 10);
2214      i = 0;   while (*chptr == ',') {
2215     i++;
2216     kernel = chptr + 1;
2217     indexVars[i] = strtol(kernel, &chptr, 10);
2218     }
2219    
2220   while ((entry = findEntryByIndex(config, i))) {   if (*chptr) {
2221      if (!entry->skip) break;   /* can't parse it, bail */
2222      i++;   return NULL;
2223   }   }
2224    
2225   if (entry && index)   indexVars[i + 1] = -1;
     *index = i;  
     } else {  
  if (index)  
     i = *index;  
  else  
     i = 0;  
2226    
2227   if (!strncmp(kernel, "TITLE=", 6)) {   i = 0;
2228      prefix = "";   if (index) {
2229      checkType = LT_TITLE;   while (i < *index) {
2230      kernel += 6;   i++;
2231   }   if (indexVars[i] == -1)
2232     return NULL;
2233   while ((entry = findEntryByIndex(config, i))) {   }
2234      line = entry->lines;   }
     while (line && line->type != checkType) line=line->next;  
   
   
     if (line && line->numElements >= 2 && !entry->skip) {  
                 rootspec = getRootSpecifier(line->elements[1].item);  
         if (!strcmp(line->elements[1].item  +  
                             ((rootspec != NULL) ? strlen(rootspec) : 0),  
                             kernel + strlen(prefix)))  
                     break;  
             }  
               
             /* have to check multiboot lines too */  
             if (entry->multiboot) {  
                 while (line && line->type != LT_MBMODULE) line = line->next;  
                 if (line && line->numElements >= 2 && !entry->skip) {  
                     rootspec = getRootSpecifier(line->elements[1].item);  
                     if (!strcmp(line->elements[1].item  +  
                                 ((rootspec != NULL) ? strlen(rootspec) : 0),  
                                 kernel + strlen(prefix)))  
                         break;  
                 }  
             }  
2235    
2236      i++;   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     }
2250    
2251     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    
2267     while ((entry = findEntryByIndex(config, i))) {
2268     if (!entry->skip)
2269     break;
2270     i++;
2271     }
2272    
2273     if (entry && index)
2274     *index = i;
2275     } else {
2276     if (index)
2277     i = *index;
2278     else
2279     i = 0;
2280    
2281     if (!strncmp(kernel, "TITLE=", 6)) {
2282     prefix = "";
2283     checkType = LT_TITLE | LT_MENUENTRY;
2284     kernel += 6;
2285     }
2286    
2287     for (entry = findEntryByIndex(config, i); entry;
2288         entry = entry->next, i++) {
2289     if (entry->skip)
2290     continue;
2291    
2292     dbgPrintf("findEntryByPath looking for %d %s in %p\n",
2293      checkType, kernel, entry);
2294    
2295     /* 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    
2309     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    
2326     /* 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     }
2336    
2337     if (index)
2338     *index = i;
2339   }   }
2340    
2341   if (index) *index = i;   return entry;
2342      }  }
2343    
2344      if (!entry) return NULL;  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    
2352     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    
2367      /* make sure this entry has a kernel identifier; this skips non-Linux   if (!entry)
2368         boot entries (could find netbsd etc, though, which is unfortunate) */   return NULL;
     line = entry->lines;  
     while (line && line->type != LT_KERNEL) line = line->next;  
     if (!line) {  
  if (!index) index = &i;  
  (*index)++;  
  return findEntryByPath(config, kernel, prefix, index);  
     }  
2369    
2370      return entry;   if (index)
2371     *index = i;
2372     return entry;
2373  }  }
2374    
2375  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {  struct singleEntry *findEntryByIndex(struct grubConfig *cfg, int index)
2376      struct singleEntry * entry;  {
2377     struct singleEntry *entry;
2378    
2379      entry = cfg->entries;   entry = cfg->entries;
2380      while (index && entry) {   while (index && entry) {
2381   entry = entry->next;   entry = entry->next;
2382   index--;   index--;
2383      }   }
2384    
2385      return entry;   return entry;
2386  }  }
2387    
2388  /* Find a good template to use for the new kernel. An entry is  /* Find a good template to use for the new kernel. An entry is
# Line 1142  struct singleEntry * findEntryByIndex(st Line 2390  struct singleEntry * findEntryByIndex(st
2390   * is going to be removed). Try and use the default entry, but   * 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,   * if that doesn't work just take the first. If we can't find one,
2392   * bail. */   * bail. */
2393  struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,  struct singleEntry *findTemplate(struct grubConfig *cfg, const char *prefix,
2394   int * indexPtr, int skipRemoved, int flags) {   int *indexPtr, int skipRemoved, int flags)
2395      struct singleEntry * entry, * entry2;  {
2396      int index;   struct singleEntry *entry, *entry2;
2397     int index;
2398      if (cfg->defaultImage > -1) {  
2399   entry = findEntryByIndex(cfg, cfg->defaultImage);   if (cfg->cfi->defaultIsSaved) {
2400   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {   if (cfg->cfi->getEnv) {
2401      if (indexPtr) *indexPtr = cfg->defaultImage;   char *defTitle =
2402      return entry;      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     }
2423     } else if (cfg->defaultImage > -1) {
2424     entry = findEntryByIndex(cfg, cfg->defaultImage);
2425     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2426     if (indexPtr)
2427     *indexPtr = cfg->defaultImage;
2428     return entry;
2429     }
2430   }   }
     }  
2431    
2432      index = 0;   index = 0;
2433      while ((entry = findEntryByIndex(cfg, index))) {   while ((entry = findEntryByIndex(cfg, index))) {
2434   if (suitableImage(entry, prefix, skipRemoved, flags)) {   if (suitableImage(entry, prefix, skipRemoved, flags)) {
2435              int j;   int j;
2436              for (j = 0; j < index; j++) {   for (j = 0; j < index; j++) {
2437                  entry2 = findEntryByIndex(cfg, j);   entry2 = findEntryByIndex(cfg, j);
2438                  if (entry2->skip) index--;   if (entry2->skip)
2439              }   index--;
2440      if (indexPtr) *indexPtr = index;   }
2441     if (indexPtr)
2442     *indexPtr = index;
2443    
2444      return entry;   return entry;
2445   }   }
2446    
2447   index++;   index++;
2448      }   }
2449    
2450      fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));   fprintf(stderr,
2451     _("grubby fatal error: unable to find a suitable template\n"));
2452    
2453      return NULL;   return NULL;
2454  }  }
2455    
2456  char * findBootPrefix(void) {  char *findBootPrefix(void)
2457      struct stat sb, sb2;  {
2458     struct stat sb, sb2;
2459    
2460      stat("/", &sb);   stat("/", &sb);
2461  #ifdef __ia64__  #ifdef __ia64__
2462      stat("/boot/efi/EFI/redhat/", &sb2);   stat("/boot/efi/EFI/redhat/", &sb2);
2463  #else  #else
2464      stat("/boot", &sb2);   stat("/boot", &sb2);
2465  #endif  #endif
2466    
2467      if (sb.st_dev == sb2.st_dev)   if (sb.st_dev == sb2.st_dev)
2468   return strdup("");   return strdup("");
2469    
2470  #ifdef __ia64__  #ifdef __ia64__
2471      return strdup("/boot/efi/EFI/redhat/");   return strdup("/boot/efi/EFI/redhat/");
2472  #else  #else
2473      return strdup("/boot");   return strdup("/boot");
2474  #endif  #endif
2475  }  }
2476    
2477  void markRemovedImage(struct grubConfig * cfg, const char * image,  void markRemovedImage(struct grubConfig *cfg, const char *image,
2478        const char * prefix) {        const char *prefix)
2479      struct singleEntry * entry;  {
2480     struct singleEntry *entry;
2481      if (!image) return;  
2482     if (!image)
2483      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))   return;
2484   entry->skip = 1;  
2485  }   /* check and see if we're removing the default image */
2486     if (isdigit(*image)) {
2487  void setDefaultImage(struct grubConfig * config, int hasNew,   entry = findEntryByPath(cfg, image, prefix, NULL);
2488       const char * defaultKernelPath, int newIsDefault,   if (entry)
2489       const char * prefix, int flags) {   entry->skip = 1;
2490      struct singleEntry * entry, * entry2, * newDefault;   return;
2491      int i, j;   }
2492    
2493      if (newIsDefault) {   while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2494   config->defaultImage = 0;   entry->skip = 1;
2495   return;  }
2496      } else if (defaultKernelPath) {  
2497   i = 0;  void setDefaultImage(struct grubConfig *config, int hasNew,
2498   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {       const char *defaultKernelPath, int newIsDefault,
2499      config->defaultImage = i;       const char *prefix, int flags, int index)
2500    {
2501     struct singleEntry *entry, *entry2, *newDefault;
2502     int i, j;
2503    
2504     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     else
2533     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   } else {   } else {
2549      config->defaultImage = -1;   /* Either we just erased the default (or the default line was
2550      return;   * 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   }   }
2558      }  }
2559    
2560      /* defaultImage now points to what we'd like to use, but before any order  void setFallbackImage(struct grubConfig *config, int hasNew)
2561         changes */  {
2562      if (config->defaultImage == DEFAULT_SAVED)   struct singleEntry *entry, *entry2;
2563        /* default is set to saved, we don't want to change it */   int j;
2564        return;  
2565     if (config->fallbackImage == -1)
2566      if (config->defaultImage > -1)   return;
2567   entry = findEntryByIndex(config, config->defaultImage);  
2568      else   entry = findEntryByIndex(config, config->fallbackImage);
2569   entry = NULL;   if (!entry || entry->skip) {
2570     config->fallbackImage = -1;
2571     return;
2572     }
2573    
     if (entry && !entry->skip) {  
  /* we can preserve the default */  
2574   if (hasNew)   if (hasNew)
2575      config->defaultImage++;   config->fallbackImage++;
2576    
2577   /* count the number of entries erased before this one */   /* count the number of entries erased before this one */
2578   for (j = 0; j < config->defaultImage; j++) {   for (j = 0; j < config->fallbackImage; j++) {
2579      entry2 = findEntryByIndex(config, j);   entry2 = findEntryByIndex(config, j);
2580      if (entry2->skip) config->defaultImage--;   if (entry2->skip)
2581   }   config->fallbackImage--;
2582      } else if (hasNew) {   }
  config->defaultImage = 0;  
     } else {  
  /* Either we just erased the default (or the default line was bad  
  * to begin with) and didn't put a new one in. We'll use the first  
  * valid image. */  
  newDefault = findTemplate(config, prefix, &config->defaultImage, 1,  
   flags);  
  if (!newDefault)  
     config->defaultImage = -1;  
     }  
2583  }  }
2584    
2585  void setFallbackImage(struct grubConfig * config, int hasNew) {  void displayEntry(struct singleEntry *entry, const char *prefix, int index)
2586      struct singleEntry * entry, * entry2;  {
2587      int j;   struct singleLine *line;
2588     char *root = NULL;
2589      if (config->fallbackImage == -1) return;   int i;
2590     int j;
2591      entry = findEntryByIndex(config, config->fallbackImage);  
2592      if (!entry || entry->skip) {   printf("index=%d\n", index);
2593   config->fallbackImage = -1;  
2594   return;   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    
2602      if (hasNew)   if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2603   config->fallbackImage++;   printf("kernel=%s\n", line->elements[1].item);
2604         else
2605      /* count the number of entries erased before this one */   printf("kernel=%s%s\n", prefix, line->elements[1].item);
     for (j = 0; j < config->fallbackImage; j++) {  
  entry2 = findEntryByIndex(config, j);  
  if (entry2->skip) config->fallbackImage--;  
     }  
 }  
2606    
2607  void displayEntry(struct singleEntry * entry, const char * prefix, int index) {   if (line->numElements >= 3) {
2608      struct singleLine * line;   printf("args=\"");
2609      char * root = NULL;   i = 2;
2610      int i;   while (i < line->numElements) {
2611     if (!strncmp(line->elements[i].item, "root=", 5)) {
2612      line = entry->lines;   root = line->elements[i].item + 5;
2613      while (line && line->type != LT_KERNEL) line = line->next;   } else {
2614     printf("%s%s", line->elements[i].item,
2615      printf("index=%d\n", index);         line->elements[i].indent);
2616     }
2617      printf("kernel=%s\n", line->elements[1].item);  
2618     i++;
2619      if (line->numElements >= 3) {   }
2620   printf("args=\"");   printf("\"\n");
2621   i = 2;   } else {
2622   while (i < line->numElements) {   line = getLineByType(LT_KERNELARGS, entry->lines);
2623      if (!strncmp(line->elements[i].item, "root=", 5)) {   if (line) {
2624   root = line->elements[i].item + 5;   char *s;
2625      } else {  
2626   printf("%s%s", line->elements[i].item,   printf("args=\"");
2627         line->elements[i].indent);   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    
2635     printf("%s%s", s,
2636           line->elements[i].indent);
2637     }
2638    
2639      i++;   i++;
2640     }
2641    
2642     s = line->elements[i - 1].indent;
2643     printf("\"\n");
2644     }
2645     }
2646    
2647     if (!root) {
2648     line = getLineByType(LT_ROOT, entry->lines);
2649     if (line && line->numElements >= 2)
2650     root = line->elements[1].item;
2651     }
2652    
2653     if (root) {
2654     char *s = alloca(strlen(root) + 1);
2655    
2656     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   }   }
2662   printf("\"\n");  
2663      } else {   line =
2664   line = entry->lines;      getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
2665   while (line && line->type != LT_KERNELARGS) line=line->next;    entry->lines);
2666    
2667     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    
2673     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    
2679     line = getLineByType(LT_TITLE, entry->lines);
2680   if (line) {   if (line) {
2681      char * s;   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     }
2691    
2692     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    
2700     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     }
2707    }
2708    
2709    int isSuseSystem(void)
2710    {
2711     const char *path;
2712     const static char default_path[] = "/etc/SuSE-release";
2713    
2714     if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2715     path = default_path;
2716    
2717     if (!access(path, R_OK))
2718     return 1;
2719     return 0;
2720    }
2721    
2722    int isSuseGrubConf(const char *path)
2723    {
2724     FILE *grubConf;
2725     char *line = NULL;
2726     size_t len = 0, res = 0;
2727    
2728     grubConf = fopen(path, "r");
2729     if (!grubConf) {
2730     dbgPrintf("Could not open SuSE configuration file '%s'\n",
2731      path);
2732     return 0;
2733     }
2734    
2735     while ((res = getline(&line, &len, grubConf)) != -1) {
2736     if (!strncmp(line, "setup", 5)) {
2737     fclose(grubConf);
2738     free(line);
2739     return 1;
2740     }
2741     }
2742    
2743     dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2744      path);
2745    
2746     fclose(grubConf);
2747     free(line);
2748     return 0;
2749    }
2750    
2751    int suseGrubConfGetLba(const char *path, int *lbaPtr)
2752    {
2753     FILE *grubConf;
2754     char *line = NULL;
2755     size_t res = 0, len = 0;
2756    
2757     if (!path)
2758     return 1;
2759     if (!lbaPtr)
2760     return 1;
2761    
2762     grubConf = fopen(path, "r");
2763     if (!grubConf)
2764     return 1;
2765    
2766     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    
2776      printf("args=\"");   if (!strncmp(line, "setup", 5)) {
2777      i = 1;   if (strstr(line, "--force-lba")) {
2778      while (i < line->numElements) {   *lbaPtr = 1;
2779   if (!strncmp(line->elements[i].item, "root=", 5)) {   } else {
2780      root = line->elements[i].item + 5;   *lbaPtr = 0;
2781     }
2782     dbgPrintf("lba: %i\n", *lbaPtr);
2783     break;
2784     }
2785     }
2786    
2787     free(line);
2788     fclose(grubConf);
2789     return 0;
2790    }
2791    
2792    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    
2802     if (!path)
2803     return 1;
2804     if (!devicePtr)
2805     return 1;
2806    
2807     grubConf = fopen(path, "r");
2808     if (!grubConf)
2809     return 1;
2810    
2811     while ((res = getline(&line, &len, grubConf)) != -1) {
2812     if (strncmp(line, "setup", 5))
2813     continue;
2814    
2815     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    
2824     lastParamPtr = bounds = line + res;
2825    
2826     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2827     while (!isspace(*lastParamPtr))
2828     lastParamPtr--;
2829     lastParamPtr++;
2830    
2831     secLastParamPtr = lastParamPtr - 2;
2832     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2833    
2834     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    
2844     /*
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    
2852     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 {   } else {
2864      s = line->elements[i].item;   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     }
2874    
2875     free(line);
2876     fclose(grubConf);
2877     return 1;
2878    }
2879    
2880    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    
2890     if (!device)
2891     return 1;
2892     if (!bootPtr)
2893     return 1;
2894    
2895     if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2896     path = default_path;
2897    
2898      printf("%s%s", s, line->elements[i].indent);   dbgPrintf("opening grub device.map file from: %s\n", path);
2899     deviceMap = fopen(path, "r");
2900     if (!deviceMap)
2901     return 1;
2902    
2903     while ((res = getline(&line, &len, deviceMap)) != -1) {
2904     if (!strncmp(line, "#", 1))
2905     continue;
2906    
2907     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    
2916   i++;   devicePtr = line;
2917      }   bounds = line + res;
2918    
2919     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2920     devicePtr++;
2921     dbgPrintf("device: %s\n", devicePtr);
2922    
2923     if (!strncmp(devicePtr, device, strlen(device))) {
2924     devicePtr += strlen(device);
2925     while (isspace(*devicePtr)
2926           && ((devicePtr + 1) <= bounds))
2927     devicePtr++;
2928    
2929      s = line->elements[i - 1].indent;   *bootPtr = strdup(devicePtr);
2930      printf("\"\n");   break;
2931     }
2932   }   }
     }  
2933    
2934      if (!root) {   free(line);
2935   line = entry->lines;   fclose(deviceMap);
2936   while (line && line->type != LT_ROOT) line = line->next;   return 0;
2937    }
2938    
2939   if (line && line->numElements >= 2)  int suseGrubConfGetBoot(const char *path, char **bootPtr)
2940      root=line->elements[1].item;  {
2941      }   char *grubDevice;
2942    
2943      if (root) {   if (suseGrubConfGetInstallDevice(path, &grubDevice))
2944   char * s = alloca(strlen(root) + 1);   dbgPrintf("error looking for grub installation device\n");
2945     else
2946   strcpy(s, root);   dbgPrintf("grubby installation device: %s\n", grubDevice);
  if (s[strlen(s) - 1] == '"')  
     s[strlen(s) - 1] = '\0';  
  /* make sure the root doesn't have a trailing " */  
  printf("root=%s\n", s);  
     }  
2947    
2948      line = entry->lines;   if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2949      while (line && line->type != LT_INITRD) line = line->next;   dbgPrintf("error looking for grub boot device\n");
2950     else
2951     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2952    
2953      if (line && line->numElements >= 2) {   free(grubDevice);
2954   printf("initrd=%s", prefix);   return 0;
  for (i = 1; i < line->numElements; i++)  
     printf("%s%s", line->elements[i].item, line->elements[i].indent);  
  printf("\n");  
     }  
2955  }  }
2956    
2957  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {  int parseSuseGrubConf(int *lbaPtr, char **bootPtr)
2958      FILE * in;  {
2959      char buf[1024];   /*
2960      char * chptr;   * This SuSE grub configuration file at this location is not your
2961      char * start;   * average grub configuration file, but instead the grub commands
2962      char * param;   * used to setup grub on that system.
2963     */
2964      in = fopen("/etc/sysconfig/grub", "r");   const char *path;
2965      if (!in) return 1;   const static char default_path[] = "/etc/grub.conf";
   
     if (lbaPtr) *lbaPtr = 0;  
     if (bootPtr) *bootPtr = NULL;  
   
     while (fgets(buf, sizeof(buf), in)) {  
  start = buf;  
  while (isspace(*start)) start++;  
  if (*start == '#') continue;  
   
  chptr = strchr(start, '=');  
  if (!chptr) continue;  
  chptr--;  
  while (*chptr && isspace(*chptr)) chptr--;  
  chptr++;  
  *chptr = '\0';  
   
  param = chptr + 1;  
  while (*param && isspace(*param)) param++;  
  if (*param == '=') {  
     param++;  
     while (*param && isspace(*param)) param++;  
  }  
   
  chptr = param;  
  while (*chptr && !isspace(*chptr)) chptr++;  
  *chptr = '\0';  
   
  if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)  
     *lbaPtr = 1;  
  else if (!strcmp(start, "boot") && bootPtr)  
     *bootPtr = strdup(param);  
     }  
2966    
2967      fclose(in);   if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2968     path = default_path;
2969    
2970      return 0;   if (!isSuseGrubConf(path))
2971     return 1;
2972    
2973     if (lbaPtr) {
2974     *lbaPtr = 0;
2975     if (suseGrubConfGetLba(path, lbaPtr))
2976     return 1;
2977     }
2978    
2979     if (bootPtr) {
2980     *bootPtr = NULL;
2981     suseGrubConfGetBoot(path, bootPtr);
2982     }
2983    
2984     return 0;
2985  }  }
2986    
2987  void dumpSysconfigGrub(void) {  int parseSysconfigGrub(int *lbaPtr, char **bootPtr)
2988      char * boot;  {
2989      int lba;   FILE *in;
2990     char buf[1024];
2991      if (!parseSysconfigGrub(&lba, &boot)) {   char *chptr;
2992   if (lba) printf("lba\n");   char *start;
2993   if (boot) printf("boot=%s\n", boot);   char *param;
2994      }  
2995     in = fopen("/etc/sysconfig/grub", "r");
2996     if (!in)
2997     return 1;
2998    
2999     if (lbaPtr)
3000     *lbaPtr = 0;
3001     if (bootPtr)
3002     *bootPtr = NULL;
3003    
3004     while (fgets(buf, sizeof(buf), in)) {
3005     start = buf;
3006     while (isspace(*start))
3007     start++;
3008     if (*start == '#')
3009     continue;
3010    
3011     chptr = strchr(start, '=');
3012     if (!chptr)
3013     continue;
3014     chptr--;
3015     while (*chptr && isspace(*chptr))
3016     chptr--;
3017     chptr++;
3018     *chptr = '\0';
3019    
3020     param = chptr + 1;
3021     while (*param && isspace(*param))
3022     param++;
3023     if (*param == '=') {
3024     param++;
3025     while (*param && isspace(*param))
3026     param++;
3027     }
3028    
3029     chptr = param;
3030     while (*chptr && !isspace(*chptr))
3031     chptr++;
3032     *chptr = '\0';
3033    
3034     if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
3035     *lbaPtr = 1;
3036     else if (!strcmp(start, "boot") && bootPtr)
3037     *bootPtr = strdup(param);
3038     }
3039    
3040     fclose(in);
3041    
3042     return 0;
3043  }  }
3044    
3045  int displayInfo(struct grubConfig * config, char * kernel,  void dumpSysconfigGrub(void)
3046   const char * prefix) {  {
3047      int i = 0;   char *boot = NULL;
3048      struct singleEntry * entry;   int lba;
3049      struct singleLine * line;  
3050     if (isSuseSystem()) {
3051      entry = findEntryByPath(config, kernel, prefix, &i);   if (parseSuseGrubConf(&lba, &boot)) {
3052      if (!entry) {   free(boot);
3053   fprintf(stderr, _("grubby: kernel not found\n"));   return;
3054   return 1;   }
3055      }   } else {
3056     if (parseSysconfigGrub(&lba, &boot)) {
3057     free(boot);
3058     return;
3059     }
3060     }
3061    
3062      /* this is a horrible hack to support /etc/sysconfig/grub; there must   if (lba)
3063         be a better way */   printf("lba\n");
3064      if (config->cfi == &grubConfigType) {   if (boot) {
3065   dumpSysconfigGrub();   printf("boot=%s\n", boot);
3066      } else {   free(boot);
  line = config->theLines;  
  while (line && line->type != LT_BOOT) line = line->next;  
  if (line && line->numElements >= 1) {  
     printf("boot=%s\n", line->elements[1].item);  
3067   }   }
3068    }
3069    
3070   line = config->theLines;  int displayInfo(struct grubConfig *config, char *kernel, const char *prefix)
3071   while (line && line->type != LT_LBA) line = line->next;  {
3072   if (line) printf("lba\n");   int i = 0;
3073      }   struct singleEntry *entry;
3074     struct singleLine *line;
3075    
3076     entry = findEntryByPath(config, kernel, prefix, &i);
3077     if (!entry) {
3078     fprintf(stderr, _("grubby: kernel not found\n"));
3079     return 1;
3080     }
3081    
3082      displayEntry(entry, prefix, i);   /* 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    
3092     line = getLineByType(LT_LBA, config->theLines);
3093     if (line)
3094     printf("lba\n");
3095     }
3096    
     i++;  
     while ((entry = findEntryByPath(config, kernel, prefix, &i))) {  
3097   displayEntry(entry, prefix, i);   displayEntry(entry, prefix, i);
3098    
3099   i++;   i++;
3100      }   while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
3101     displayEntry(entry, prefix, i);
3102     i++;
3103     }
3104    
3105      return 0;   return 0;
3106    }
3107    
3108    struct singleLine *addLineTmpl(struct singleEntry *entry,
3109           struct singleLine *tmplLine,
3110           struct singleLine *prevLine,
3111           const char *val, struct configFileInfo *cfi)
3112    {
3113     struct singleLine *newLine = lineDup(tmplLine);
3114    
3115     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    
3123     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    
3131     /* 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     }
3145    
3146     dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
3147      newLine->elements[0].item : "");
3148    
3149     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    
3158     return newLine;
3159  }  }
3160    
3161  /* val may be NULL */  /* val may be NULL */
3162  struct singleLine *  addLine(struct singleEntry * entry,  struct singleLine *addLine(struct singleEntry *entry,
3163       struct configFileInfo * cfi,     struct configFileInfo *cfi,
3164       enum lineType_e type, const char * defaultIndent,     enum lineType_e type, char *defaultIndent,
3165       char * val) {     const char *val)
3166      struct singleLine * line, * prev;  {
3167      int i;   struct singleLine *line, *prev;
3168     struct keywordTypes *kw;
3169      for (i = 0; cfi->keywords[i].key; i++)   struct singleLine tmpl;
3170   if (cfi->keywords[i].type == type) break;  
3171      if (type != LT_TITLE || !cfi->titleBracketed)   /* NB: This function shouldn't allocate items on the heap, rather on
3172          if (!cfi->keywords[i].key) abort();   * the stack since it calls addLineTmpl which will make copies.
3173     */
3174      /* The last non-empty line gives us the indention to us and the line   if (type == LT_TITLE && cfi->titleBracketed) {
3175         to insert after. Note that comments are considered empty lines, which   /* we're doing a bracketed title (zipl) */
3176         may not be ideal? If there are no lines or we are looking at the   tmpl.type = type;
3177         first line, we use defaultIndent (the first line is normally indented   tmpl.numElements = 1;
3178         differently from the rest) */   tmpl.elements = alloca(sizeof(*tmpl.elements));
3179      if (entry->lines) {   tmpl.elements[0].item = alloca(strlen(val) + 3);
3180   line = entry->lines;   sprintf(tmpl.elements[0].item, "[%s]", val);
3181   prev = NULL;   tmpl.elements[0].indent = "";
3182   while (line) {   val = NULL;
3183      if (line->numElements) prev = line;   } else if (type == LT_MENUENTRY) {
3184      line = line->next;   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   }   }
  if (!prev) {  
     /* just use the last line */  
     prev = entry->lines;  
     while (prev->next) prev = prev->next;  
  }  
   
  line = prev->next;  
  prev->next = malloc(sizeof(*line));  
  prev->next->next = line;  
  line = prev->next;  
3231    
3232   if (prev == entry->lines)   /* The last non-empty line gives us the indention to us and the line
3233      line->indent = strdup(defaultIndent);   * to insert after. Note that comments are considered empty lines,
3234   else   * which may not be ideal? If there are no lines or we are looking at
3235      line->indent = strdup(prev->indent);   * the first line, we use defaultIndent (the first line is normally
3236      } else {   * indented differently from the rest) */
3237   line = malloc(sizeof(*line));   for (line = entry->lines, prev = NULL; line; line = line->next) {
3238   line->indent = strdup(defaultIndent);   if (line->numElements)
3239   line->next = NULL;   prev = line;
3240      }   /* fall back on the last line if prev isn't otherwise set */
3241     if (!line->next && !prev)
3242     prev = line;
3243     }
3244    
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     }
3260    
3261      if (type != LT_TITLE || !cfi->titleBracketed) {   return addLineTmpl(entry, &tmpl, prev, val, cfi);
3262          line->type = type;  }
3263          line->numElements = val ? 2 : 1;  
3264          line->elements = malloc(sizeof(*line->elements) * line->numElements);  void removeLine(struct singleEntry *entry, struct singleLine *line)
3265          line->elements[0].item = strdup(cfi->keywords[i].key);  {
3266          line->elements[0].indent = malloc(2);   struct singleLine *prev;
3267          line->elements[0].indent[0] = cfi->keywords[i].nextChar;   int i;
3268          line->elements[0].indent[1] = '\0';  
3269             for (i = 0; i < line->numElements; i++) {
3270          if (val) {   free(line->elements[i].item);
3271              line->elements[1].item = val;   free(line->elements[i].indent);
3272              line->elements[1].indent = strdup("");   }
3273          }   free(line->elements);
3274      } else {   free(line->indent);
         /* we're doing the title of a bracketed title (zipl) */  
         line->type = type;  
         line->numElements = 1;  
         line->elements = malloc(sizeof(*line->elements) * line->numElements);  
   
         line->elements[0].item = malloc(strlen(val) + 3);  
         sprintf(line->elements[0].item, "[%s]", val);  
         line->elements[0].indent = strdup("");  
     }  
3275    
3276      return line;   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    
3285     free(line);
3286  }  }
3287    
3288  void removeLine(struct singleEntry * entry, struct singleLine * line) {  static void requote(struct singleLine *tmplLine, struct configFileInfo *cfi)
3289      struct singleLine * prev;  {
3290      int i;   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    
3301     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    
3307     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     }
3341     }
3342     while (tmplLine->numElements)
3343     removeElement(tmplLine, 0);
3344     if (tmplLine->elements)
3345     free(tmplLine->elements);
3346    
3347      for (i = 0; i < line->numElements; i++) {   tmplLine->numElements = newLine.numElements;
3348   free(line->elements[i].item);   tmplLine->elements = newLine.elements;
3349   free(line->elements[i].indent);  }
     }  
     free(line->elements);  
     free(line->indent);  
3350    
3351      if (line == entry->lines) {  static void insertElement(struct singleLine *line,
3352   entry->lines = line->next;    const char *item, int insertHere,
3353      } else {    struct configFileInfo *cfi)
3354   prev = entry->lines;  {
3355   while (prev->next != line) prev = prev->next;   struct keywordTypes *kw;
3356   prev->next = line->next;   char indent[2] = "";
3357      }  
3358     /* 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    
3366     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    
3373     kw = getKeywordByType(line->type, cfi);
3374    
3375     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    
3385      free(line);   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    
3394     line->numElements++;
3395    
3396     dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3397      line->elements[0].item,
3398      line->elements[insertHere].item,
3399      line->elements[insertHere].indent, insertHere);
3400  }  }
3401    
3402  int argMatch(const char * one, const char * two) {  static void removeElement(struct singleLine *line, int removeHere)
3403      char * first, * second;  {
3404      char * chptr;   int i;
3405    
3406      first = strcpy(alloca(strlen(one) + 1), one);   /* sanity check */
3407      second = strcpy(alloca(strlen(two) + 1), two);   if (removeHere >= line->numElements)
3408     return;
3409      chptr = strchr(first, '=');  
3410      if (chptr) *chptr = '\0';   dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3411      removeHere, line->elements[removeHere].item);
3412      chptr = strchr(second, '=');  
3413      if (chptr) *chptr = '\0';   free(line->elements[removeHere].item);
3414    
3415      return strcmp(first, second);   if (removeHere > 1) {
3416  }   /* previous argument gets this argument's post-indentation */
3417     free(line->elements[removeHere - 1].indent);
3418  int updateActualImage(struct grubConfig * cfg, const char * image,   line->elements[removeHere - 1].indent =
3419                        const char * prefix, const char * addArgs,      line->elements[removeHere].indent;
3420                        const char * removeArgs, int multibootArgs) {   } else {
3421      struct singleEntry * entry;   free(line->elements[removeHere].indent);
     struct singleLine * line, * rootLine;  
     int index = 0;  
     int i, j, k;  
     const char ** newArgs, ** oldArgs;  
     const char ** arg;  
     const char * chptr;  
     int useKernelArgs = 0;  
     int useRoot = 0;  
     int firstElement;  
     int *usedElements, *usedArgs;  
   
     if (!image) return 0;  
   
     if (!addArgs) {  
  newArgs = malloc(sizeof(*newArgs));  
  *newArgs = NULL;  
     } else {  
  if (poptParseArgvString(addArgs, NULL, &newArgs)) {  
     fprintf(stderr,  
     _("grubby: error separating arguments '%s'\n"), addArgs);  
     return 1;  
3422   }   }
     }  
3423    
3424      if (!removeArgs) {   /* now collapse the array, but don't bother to realloc smaller */
3425   oldArgs = malloc(sizeof(*oldArgs));   for (i = removeHere; i < line->numElements - 1; i++)
3426   *oldArgs = NULL;   line->elements[i] = line->elements[i + 1];
3427      } else {  
3428   if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {   line->numElements--;
3429      fprintf(stderr,  }
3430      _("grubby: error separating arguments '%s'\n"), removeArgs);  
3431              free(newArgs);  int argMatch(const char *one, const char *two)
3432      return 1;  {
3433     char *first, *second;
3434     char *chptr;
3435    
3436     first = strcpy(alloca(strlen(one) + 1), one);
3437     second = strcpy(alloca(strlen(two) + 1), two);
3438    
3439     chptr = strchr(first, '=');
3440     if (chptr)
3441     *chptr = '\0';
3442    
3443     chptr = strchr(second, '=');
3444     if (chptr)
3445     *chptr = '\0';
3446    
3447     return strcmp(first, second);
3448    }
3449    
3450    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    
3465     if (!image)
3466     return 0;
3467    
3468     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     }
3479    
3480     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   }   }
     }  
3492    
3493      for (i = 0; cfg->cfi->keywords[i].key; i++)   useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3494   if (cfg->cfi->keywords[i].type == LT_KERNELARGS) break;   && (!multibootArgs || cfg->cfi->mbConcatArgs));
3495    
3496      if (cfg->cfi->keywords[i].key)   useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3497   useKernelArgs = 1;     && !multibootArgs);
3498    
3499      for (i = 0; cfg->cfi->keywords[i].key; i++)   for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
  if (cfg->cfi->keywords[i].type == LT_ROOT) break;  
3500    
3501      if (cfg->cfi->keywords[i].key)   if (multibootArgs && !entry->multiboot)
3502   useRoot = 1;   continue;
3503    
3504      k = 0;   /* Determine where to put the args.  If this config supports
3505      for (arg = newArgs; *arg; arg++)   * LT_KERNELARGS, use that.  Otherwise use
3506          k++;   * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3507      usedArgs = calloc(k, sizeof(int));   */
3508     if (useKernelArgs) {
3509      while ((entry = findEntryByPath(cfg, image, prefix, &index))) {   line = getLineByType(LT_KERNELARGS, entry->lines);
3510   index++;   if (!line) {
3511     /* no LT_KERNELARGS, need to add it */
3512   line = entry->lines;   line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3513   while (line && line->type != LT_KERNEL) line = line->next;         cfg->secondaryIndent, NULL);
3514   if (!line) continue;   }
3515   firstElement = 2;   firstElement = 1;
3516    
3517          if (entry->multiboot && !multibootArgs) {   } else if (multibootArgs) {
3518              /* first mb module line is the real kernel */   line = getLineByType(LT_HYPER, entry->lines);
3519              while (line && line->type != LT_MBMODULE) line = line->next;   if (!line) {
3520              firstElement = 2;   /* a multiboot entry without LT_HYPER? */
3521          } else if (useKernelArgs) {   continue;
3522      while (line && line->type != LT_KERNELARGS) line = line->next;   }
3523      firstElement = 1;   firstElement = 2;
  }  
   
  if (!line && useKernelArgs) {  
     /* no append in there, need to add it */  
     line = addLine(entry, cfg->cfi, LT_KERNELARGS, NULL, NULL);  
  }  
   
         usedElements = calloc(line->numElements, sizeof(int));  
   
         k = 0;  
  for (arg = newArgs; *arg; arg++) {  
             if (usedArgs[k]) {  
                 k++;  
                 continue;  
             }  
     for (i = firstElement; i < line->numElements; i++) {  
                 if (usedElements[i])  
                     continue;  
  if (!argMatch(line->elements[i].item, *arg)) {  
                     usedElements[i]=1;  
                     usedArgs[k]=1;  
     break;  
                 }  
             }  
     chptr = strchr(*arg, '=');  
3524    
     if (i < line->numElements) {  
  /* replace */  
  free(line->elements[i].item);  
  line->elements[i].item = strdup(*arg);  
     } else if (useRoot && !strncmp(*arg, "root=/dev/", 10) && *chptr) {  
  rootLine = entry->lines;  
  while (rootLine && rootLine->type != LT_ROOT)  
     rootLine = rootLine->next;  
  if (!rootLine) {  
     rootLine = addLine(entry, cfg->cfi, LT_ROOT, NULL, NULL);  
     rootLine->elements = realloc(rootLine->elements,  
     2 * sizeof(*rootLine->elements));  
     rootLine->numElements++;  
     rootLine->elements[1].indent = strdup("");  
     rootLine->elements[1].item = strdup("");  
  }  
   
  free(rootLine->elements[1].item);  
  rootLine->elements[1].item = strdup(chptr + 1);  
     } else {  
  /* append */  
  line->elements = realloc(line->elements,  
  (line->numElements + 1) * sizeof(*line->elements));  
  line->elements[line->numElements].item = strdup(*arg);  
  usedElements = realloc(usedElements,  
  (line->numElements + 1) * sizeof(int));  
  usedElements[line->numElements] = 1;  
   
  if (line->numElements > 1) {  
     /* add to existing list of arguments */  
     line->elements[line->numElements].indent =  
  line->elements[line->numElements - 1].indent;  
     line->elements[line->numElements - 1].indent = strdup(" ");  
3525   } else {   } else {
3526      /* First thing on this line; treat a bit differently. Note   line =
3527         this is only possible if we've added a LT_KERNELARGS      getLineByType(LT_KERNEL | LT_MBMODULE |
3528         entry */    LT_KERNEL_EFI | LT_KERNEL_16,
3529      line->elements[line->numElements].indent = strdup("");    entry->lines);
3530     if (!line) {
3531     /* no LT_KERNEL or LT_MBMODULE in this entry? */
3532     continue;
3533     }
3534     firstElement = 2;
3535   }   }
3536    
3537   line->numElements++;   /* handle the elilo case which does:
3538     *   append="hypervisor args -- kernel args"
3539     */
3540     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    
3577     usedElements = calloc(line->numElements, sizeof(*usedElements));
3578    
3579     for (k = 0, arg = newArgs; *arg; arg++, k++) {
3580    
3581   /* if we updated a root= here even though there is a   doreplace = 1;
3582     LT_ROOT available we need to remove the LT_ROOT entry   for (i = firstElement; i < line->numElements; i++) {
3583     (this will happen if we switch from a device to a label) */   if (multibootArgs && cfg->cfi->mbConcatArgs &&
3584   if (useRoot && !strncmp(*arg, "root=", 5)) {      !strcmp(line->elements[i].item, "--")) {
3585      rootLine = entry->lines;   /* reached the end of hyper args, insert here */
3586      while (rootLine && rootLine->type != LT_ROOT)   doreplace = 0;
3587   rootLine = rootLine->next;   break;
3588      if (rootLine) {   }
3589   removeLine(entry, rootLine);   if (usedElements[i])
3590      }   continue;
3591   }   if (!argMatch(line->elements[i].item, *arg)) {
3592      }   usedElements[i] = 1;
3593              k++;   break;
3594   }   }
3595     }
3596          free(usedElements);  
3597     if (i < line->numElements && doreplace) {
3598   /* no arguments to remove (i.e. no append line) */   /* direct replacement */
3599   if (!line) continue;   free(line->elements[i].item);
3600     line->elements[i].item = strdup(*arg);
3601   /* this won't remove an LT_ROOT item properly (but then again,  
3602     who cares? */   } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3603   for (arg = oldArgs; *arg; arg++) {   /* root= replacement */
3604      for (i = firstElement; i < line->numElements; i++)   rootLine = getLineByType(LT_ROOT, entry->lines);
3605   if (!argMatch(line->elements[i].item, *arg))   if (rootLine) {
3606      break;   free(rootLine->elements[1].item);
3607     rootLine->elements[1].item =
3608      if (i < line->numElements) {      strdup(*arg + 5);
3609   /* if this isn't the first argument the previous argument   } else {
3610     gets this arguments post-indention */   rootLine =
3611   if (i > firstElement) {      addLine(entry, cfg->cfi, LT_ROOT,
3612      free(line->elements[i - 1].indent);      cfg->secondaryIndent,
3613      line->elements[i - 1].indent = line->elements[i].indent;      *arg + 5);
3614     }
3615     }
3616    
3617     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    
3628     /* 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   }   }
   
  free(line->elements[i].item);  
3641    
3642   for (j = i + 1; j < line->numElements; j++)   free(usedElements);
     line->elements[j - 1] = line->elements[j];  
3643    
3644   line->numElements--;   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     }
3662    
3663   if (line->numElements == 1) {   if (line->numElements == 1) {
3664      /* don't need the line at all (note it has to be a   /* don't need the line at all (note it has to be a
3665         LT_KERNELARGS for this to happen */     LT_KERNELARGS for this to happen */
3666      removeLine(entry, line);   removeLine(entry, line);
3667     }
3668   }   }
     }  
3669    
3670      free(usedArgs);   free(newArgs);
3671      free(newArgs);   free(oldArgs);
     free(oldArgs);  
3672    
3673      return 0;   return 0;
3674  }  }
3675    
3676  int updateImage(struct grubConfig * cfg, const char * image,  int updateImage(struct grubConfig *cfg, const char *image,
3677                  const char * prefix, const char * addArgs,   const char *prefix, const char *addArgs,
3678                  const char * removeArgs,   const char *removeArgs,
3679                  const char * addMBArgs, const char * removeMBArgs) {   const char *addMBArgs, const char *removeMBArgs)
3680      int rc = 0;  {
3681     int rc = 0;
3682      if (!image) return rc;  
3683     if (!image)
3684      /* update the main args first... */   return rc;
3685      if (addArgs || removeArgs)  
3686          rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);   /* update the main args first... */
3687      if (rc) return rc;   if (addArgs || removeArgs)
3688     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs,
3689      /* and now any multiboot args */         0);
3690      if (addMBArgs || removeMBArgs)   if (rc)
3691          rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);   return rc;
3692      return rc;  
3693  }   /* and now any multiboot args */
3694     if (addMBArgs || removeMBArgs)
3695  int checkDeviceBootloader(const char * device, const unsigned char * boot) {   rc = updateActualImage(cfg, image, prefix, addMBArgs,
3696      int fd;         removeMBArgs, 1);
3697      unsigned char bootSect[512];   return rc;
3698      int offset;  }
   
     fd = open(device, O_RDONLY);  
     if (fd < 0) {  
  fprintf(stderr, _("grubby: unable to open %s: %s\n"),  
  device, strerror(errno));  
  return 1;  
     }  
3699    
3700      if (read(fd, bootSect, 512) != 512) {  int addMBInitrd(struct grubConfig *cfg, const char *newMBKernel,
3701   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   const char *image, const char *prefix, const char *initrd,
3702   device, strerror(errno));   const char *title)
3703   return 1;  {
3704      }   struct singleEntry *entry;
3705      close(fd);   struct singleLine *line, *kernelLine, *endLine = NULL;
3706     int index = 0;
3707    
3708     if (!image)
3709     return 0;
3710    
3711     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3712     kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3713     if (!kernelLine)
3714     continue;
3715    
3716     /* if title is supplied, the entry's title must match it. */
3717     if (title) {
3718     char *linetitle;
3719    
3720     line =
3721        getLineByType(LT_TITLE | LT_MENUENTRY,
3722      entry->lines);
3723     if (!line)
3724     continue;
3725    
3726     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    
3736      /* first three bytes should match, a jmp short should be in there */   if (prefix) {
3737      if (memcmp(boot, bootSect, 3))   int prefixLen = strlen(prefix);
3738   return 0;   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      if (boot[1] == 0xeb) {   break;
3757   offset = boot[2] + 2;   }
     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {  
  offset = (boot[3] << 8) + boot[2] + 2;  
     } else if (boot[0] == 0xeb) {  
  offset = boot[1] + 2;  
     } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {  
  offset = (boot[2] << 8) + boot[1] + 2;  
     } else {  
  return 0;  
     }  
3758    
     if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))  
3759   return 0;   return 0;
3760    }
3761    
3762      return 2;  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    
3769     if (!image)
3770     return 0;
3771    
3772     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    
3779     /* if title is supplied, the entry's title must match it. */
3780     if (title) {
3781     char *linetitle;
3782    
3783     line =
3784        getLineByType(LT_TITLE | LT_MENUENTRY,
3785      entry->lines);
3786     if (!line)
3787     continue;
3788    
3789     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    
3799     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     break;
3836     }
3837    
3838     return 0;
3839  }  }
3840    
3841  int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {  int checkDeviceBootloader(const char *device, const unsigned char *boot)
3842      int fd;  {
3843      char buf[65536];   int fd;
3844      char * end;   unsigned char bootSect[512];
3845      char * chptr;   int offset;
3846      char * chptr2;  
3847      int rc;   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    
3854     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    
3861      /* it's on raid; we need to parse /proc/mdstat and check all of the   /* first three bytes should match, a jmp short should be in there */
3862         *raw* devices listed in there */   if (memcmp(boot, bootSect, 3))
3863     return 0;
3864    
3865     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    
3887      if (!strncmp(mdDev, "/dev/", 5))   if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3888   mdDev += 5;   return 0;
3889    
     if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {  
  fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),  
  strerror(errno));  
3890   return 2;   return 2;
3891      }  }
3892    
3893      rc = read(fd, buf, sizeof(buf) - 1);  int checkLiloOnRaid(char *mdDev, const unsigned char *boot)
3894      if (rc < 0 || rc == (sizeof(buf) - 1)) {  {
3895   fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),   int fd;
3896   strerror(errno));   char buf[65536];
3897     char *end;
3898     char *chptr;
3899     char *chptr2;
3900     int rc;
3901    
3902     /* it's on raid; we need to parse /proc/mdstat and check all of the
3903     *raw* devices listed in there */
3904    
3905     if (!strncmp(mdDev, "/dev/", 5))
3906     mdDev += 5;
3907    
3908     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    
3914     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   close(fd);   close(fd);
3922   return 2;   buf[rc] = '\0';
     }  
     close(fd);  
     buf[rc] = '\0';  
3923    
3924      chptr = buf;   chptr = buf;
3925      while (*chptr) {   while (*chptr) {
3926   end = strchr(chptr, '\n');   end = strchr(chptr, '\n');
3927   if (!end) break;   if (!end)
3928   *end = '\0';   break;
3929     *end = '\0';
3930    
3931     if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3932        chptr[strlen(mdDev)] == ' ') {
3933    
3934     /* found the device */
3935     while (*chptr && *chptr != ':')
3936     chptr++;
3937     chptr++;
3938     while (*chptr && isspace(*chptr))
3939     chptr++;
3940    
3941     /* skip the "active" bit */
3942     while (*chptr && !isspace(*chptr))
3943     chptr++;
3944     while (*chptr && isspace(*chptr))
3945     chptr++;
3946    
3947     /* skip the raid level */
3948     while (*chptr && !isspace(*chptr))
3949     chptr++;
3950     while (*chptr && isspace(*chptr))
3951     chptr++;
3952    
3953     /* everything else is partition stuff */
3954     while (*chptr) {
3955     chptr2 = chptr;
3956     while (*chptr2 && *chptr2 != '[')
3957     chptr2++;
3958     if (!*chptr2)
3959     break;
3960    
3961     /* yank off the numbers at the end */
3962     chptr2--;
3963     while (isdigit(*chptr2) && chptr2 > chptr)
3964     chptr2--;
3965     chptr2++;
3966     *chptr2 = '\0';
3967    
3968     /* 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     }
3990    
3991   if (!strncmp(chptr, mdDev, strlen(mdDev)) &&   chptr = end + 1;
3992      chptr[strlen(mdDev)] == ' ') {   }
3993    
3994      /* found the device */   fprintf(stderr,
3995      while (*chptr && *chptr != ':') chptr++;   _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3996      chptr++;   mdDev);
3997      while (*chptr && isspace(*chptr)) chptr++;   return 0;
3998    }
     /* skip the "active" bit */  
     while (*chptr && !isspace(*chptr)) chptr++;  
     while (*chptr && isspace(*chptr)) chptr++;  
   
     /* skip the raid level */  
     while (*chptr && !isspace(*chptr)) chptr++;  
     while (*chptr && isspace(*chptr)) chptr++;  
   
     /* everything else is partition stuff */  
     while (*chptr) {  
  chptr2 = chptr;  
  while (*chptr2 && *chptr2 != '[') chptr2++;  
  if (!*chptr2) break;  
   
  /* yank off the numbers at the end */  
  chptr2--;  
  while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;  
  chptr2++;  
  *chptr2 = '\0';  
   
  /* Better, now we need the /dev/ back. We're done with  
  * everything before this point, so we can just put  
  * the /dev/ part there. There will always be room. */  
  memcpy(chptr - 5, "/dev/", 5);  
  rc = checkDeviceBootloader(chptr - 5, boot);  
  if (rc != 2) {  
     return rc;  
  }  
   
  chptr = chptr2 + 1;  
  /* skip the [11] bit */  
  while (*chptr && !isspace(*chptr)) chptr++;  
  /* and move to the next one */  
  while (*chptr && isspace(*chptr)) chptr++;  
     }  
3999    
4000      /*  we're good to go */  int checkForLilo(struct grubConfig *config)
4001      return 2;  {
4002     int fd;
4003     unsigned char boot[512];
4004     struct singleLine *line;
4005    
4006     for (line = config->theLines; line; line = line->next)
4007     if (line->type == LT_BOOT)
4008     break;
4009    
4010     if (!line) {
4011     fprintf(stderr,
4012     _
4013     ("grubby: no boot line found in lilo configuration\n"));
4014     return 1;
4015   }   }
4016    
4017   chptr = end + 1;   if (line->numElements != 2)
4018      }   return 1;
4019    
4020      fprintf(stderr,   fd = open("/boot/boot.b", O_RDONLY);
4021      _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),   if (fd < 0) {
4022      mdDev);   fprintf(stderr, _("grubby: unable to open %s: %s\n"),
4023      return 0;   "/boot/boot.b", strerror(errno));
4024  }   return 1;
4025     }
4026    
4027  int checkForLilo(struct grubConfig * config) {   if (read(fd, boot, 512) != 512) {
4028      int fd;   fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4029      unsigned char boot[512];   "/boot/boot.b", strerror(errno));
4030      struct singleLine * line;   return 1;
4031     }
4032      for (line = config->theLines; line; line = line->next)   close(fd);
  if (line->type == LT_BOOT) break;  
   
     if (!line) {  
  fprintf(stderr,  
  _("grubby: no boot line found in lilo configuration\n"));  
  return 1;  
     }  
4033    
4034      if (line->numElements != 2) return 1;   if (!strncmp("/dev/md", line->elements[1].item, 7))
4035     return checkLiloOnRaid(line->elements[1].item, boot);
4036    
4037      fd = open("/boot/boot.b", O_RDONLY);   return checkDeviceBootloader(line->elements[1].item, boot);
4038      if (fd < 0) {  }
4039   fprintf(stderr, _("grubby: unable to open %s: %s\n"),  
4040   "/boot/boot.b", strerror(errno));  int checkForGrub2(struct grubConfig *config)
4041   return 1;  {
4042      }   if (!access("/etc/grub.d/", R_OK))
4043     return 2;
4044    
     if (read(fd, boot, 512) != 512) {  
  fprintf(stderr, _("grubby: unable to read %s: %s\n"),  
  "/boot/boot.b", strerror(errno));  
4045   return 1;   return 1;
4046      }  }
     close(fd);  
4047    
4048      if (!strncmp("/dev/md", line->elements[1].item, 7))  int checkForGrub(struct grubConfig *config)
4049   return checkLiloOnRaid(line->elements[1].item, boot);  {
4050     int fd;
4051     unsigned char bootSect[512];
4052     char *boot;
4053     int onSuse = isSuseSystem();
4054    
4055     if (onSuse) {
4056     if (parseSuseGrubConf(NULL, &boot))
4057     return 0;
4058     } else {
4059     if (parseSysconfigGrub(NULL, &boot))
4060     return 0;
4061     }
4062    
4063     /* assume grub is not installed -- not an error condition */
4064     if (!boot)
4065     return 0;
4066    
4067     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    
4072     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    
4080     /* 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    
4086      return checkDeviceBootloader(line->elements[1].item, boot);   return checkDeviceBootloader(boot, bootSect);
4087  }  }
4088    
4089  int checkForGrub(struct grubConfig * config) {  int checkForExtLinux(struct grubConfig *config)
4090      int fd;  {
4091      unsigned char bootSect[512];   int fd;
4092      char * boot;   unsigned char bootSect[512];
4093     char *boot;
4094     char executable[] = "/boot/extlinux/extlinux";
4095    
4096     printf("entered: checkForExtLinux()\n");
4097    
4098     if (parseSysconfigGrub(NULL, &boot))
4099     return 0;
4100    
4101     /* assume grub is not installed -- not an error condition */
4102     if (!boot)
4103     return 0;
4104    
4105     fd = open(executable, O_RDONLY);
4106     if (fd < 0)
4107     /* this doesn't exist if grub hasn't been installed */
4108     return 0;
4109    
4110     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    
4117      if (parseSysconfigGrub(NULL, &boot))   return checkDeviceBootloader(boot, bootSect);
4118   return 0;  }
4119    
4120      /* assume grub is not installed -- not an error condition */  int checkForYaboot(struct grubConfig *config)
4121      if (!boot)  {
4122   return 0;   /*
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    
4135      fd = open("/boot/grub/stage1", O_RDONLY);   return 1;
4136      if (fd < 0)  }
4137   /* this doesn't exist if grub hasn't been installed */  
4138   return 0;  int checkForElilo(struct grubConfig *config)
4139    {
4140     if (!access("/etc/elilo.conf", R_OK))
4141     return 2;
4142    
     if (read(fd, bootSect, 512) != 512) {  
  fprintf(stderr, _("grubby: unable to read %s: %s\n"),  
  "/boot/grub/stage1", strerror(errno));  
4143   return 1;   return 1;
4144      }  }
     close(fd);  
4145    
4146      return checkDeviceBootloader(boot, bootSect);  static char *getRootSpecifier(char *str)
4147    {
4148     char *idx, *rootspec = NULL;
4149    
4150     if (*str == '(') {
4151     idx = rootspec = strdup(str);
4152     while (*idx && (*idx != ')') && (!isspace(*idx)))
4153     idx++;
4154     *(++idx) = '\0';
4155     }
4156     return rootspec;
4157  }  }
4158    
4159  static char * getRootSpecifier(char * str) {  static char *getInitrdVal(struct grubConfig *config,
4160      char * idx, * rootspec = NULL;    const char *prefix, struct singleLine *tmplLine,
4161      const char *newKernelInitrd,
4162      const char **extraInitrds, int extraInitrdCount)
4163    {
4164     char *initrdVal, *end;
4165     int i;
4166     size_t totalSize;
4167     size_t prefixLen;
4168     char separatorChar;
4169    
4170     prefixLen = strlen(prefix);
4171     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */ ;
4172    
4173     for (i = 0; i < extraInitrdCount; i++) {
4174     totalSize += sizeof(separatorChar);
4175     totalSize += strlen(extraInitrds[i]) - prefixLen;
4176     }
4177    
4178     initrdVal = end = malloc(totalSize);
4179    
4180     end = stpcpy(end, newKernelInitrd + prefixLen);
4181    
4182     separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
4183     for (i = 0; i < extraInitrdCount; i++) {
4184     const char *extraInitrd;
4185     int j;
4186    
4187     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    
4196      if (*str == '(') {   if (j != tmplLine->numElements)
4197          idx = rootspec = strdup(str);   continue;
4198          while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;   }
4199          *(++idx) = '\0';  
4200      }   *end++ = separatorChar;
4201      return rootspec;   end = stpcpy(end, extraInitrd);
4202     }
4203    
4204     return initrdVal;
4205  }  }
4206    
4207  int addNewKernel(struct grubConfig * config, struct singleEntry * template,  int addNewKernel(struct grubConfig *config, struct singleEntry *template,
4208           const char * prefix,   const char *prefix,
4209   char * newKernelPath, char * newKernelTitle,   const char *newKernelPath, const char *newKernelTitle,
4210   char * newKernelArgs, char * newKernelInitrd,   const char *newKernelArgs, const char *newKernelInitrd,
4211                   char * newMBKernel, char * newMBKernelArgs) {   const char **extraInitrds, int extraInitrdCount,
4212      struct singleEntry * new;   const char *newMBKernel, const char *newMBKernelArgs,
4213      struct singleLine * newLine = NULL, * tmplLine = NULL, * lastLine = NULL;   const char *newDevTreePath)
4214      int needs;  {
4215      char * indent = NULL;   struct singleEntry *new;
4216      char * rootspec = NULL;   struct singleLine *newLine = NULL, *tmplLine = NULL, *masterLine = NULL;
4217      char * chptr;   int needs;
4218      int i;   char *chptr;
4219      enum lineType_e type;  
4220     if (!newKernelPath)
4221      if (!newKernelPath) return 0;   return 0;
4222    
4223      /* if the newKernelTitle is too long silently munge it into something   /* 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   * we can live with. truncating is first check, then we'll just mess with
4225       * it until it looks better */   * it until it looks better */
4226      if (config->cfi->maxTitleLength &&   if (config->cfi->maxTitleLength &&
4227      (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {      (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
4228   char * buf = alloca(config->cfi->maxTitleLength + 7);   char *buf = alloca(config->cfi->maxTitleLength + 7);
4229   char * numBuf = alloca(config->cfi->maxTitleLength + 1);   char *numBuf = alloca(config->cfi->maxTitleLength + 1);
4230   int i = 1;   int i = 1;
4231    
4232     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   sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);   newKernelTitle = buf + 6;
  while (findEntryByPath(config, buf, NULL, NULL)) {  
     sprintf(numBuf, "%d", i++);  
     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);  
4240   }   }
4241    
4242   newKernelTitle = buf + 6;   new = malloc(sizeof(*new));
4243      }   new->skip = 0;
4244     new->multiboot = 0;
4245     new->next = config->entries;
4246     new->lines = NULL;
4247     config->entries = new;
4248    
4249     /* copy/update from the template */
4250     needs = NEED_KERNEL | NEED_TITLE;
4251     if (newKernelInitrd)
4252     needs |= NEED_INITRD;
4253     if (newMBKernel) {
4254     needs |= NEED_MB;
4255     new->multiboot = 1;
4256     }
4257     if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
4258     needs |= NEED_DEVTREE;
4259    
4260     if (template) {
4261     for (masterLine = template->lines;
4262         masterLine && (tmplLine = lineDup(masterLine));
4263         lineFree(tmplLine), masterLine = masterLine->next) {
4264     dbgPrintf("addNewKernel processing %d\n",
4265      tmplLine->type);
4266    
4267     /* skip comments */
4268     chptr = tmplLine->indent;
4269     while (*chptr && isspace(*chptr))
4270     chptr++;
4271     if (*chptr == '#')
4272     continue;
4273    
4274     if (iskernel(tmplLine->type)
4275        && tmplLine->numElements >= 2) {
4276     if (!template->multiboot && (needs & NEED_MB)) {
4277     /* it's not a multiboot template and
4278     * this is the kernel line.  Try to
4279     * be intelligent about inserting the
4280     * hypervisor at the same time.
4281     */
4282     if (config->cfi->mbHyperFirst) {
4283     /* insert the hypervisor first */
4284     newLine =
4285        addLine(new, config->cfi,
4286        LT_HYPER,
4287        tmplLine->indent,
4288        newMBKernel +
4289        strlen(prefix));
4290     /* set up for adding the
4291     * kernel line */
4292     free(tmplLine->indent);
4293     tmplLine->indent =
4294        strdup(config->
4295       secondaryIndent);
4296     needs &= ~NEED_MB;
4297     }
4298     if (needs & NEED_KERNEL) {
4299     /* use addLineTmpl to
4300     * preserve line elements,
4301     * otherwise we could just
4302     * call addLine.
4303     * Unfortunately this means
4304     * making some changes to the
4305     * template such as the
4306     * indent change above and
4307     * the type change below.
4308     */
4309     struct keywordTypes *mbm_kw =
4310        getKeywordByType
4311        (LT_MBMODULE, config->cfi);
4312     if (mbm_kw) {
4313     tmplLine->type =
4314        LT_MBMODULE;
4315     free(tmplLine->
4316         elements[0].item);
4317     tmplLine->elements[0].
4318        item =
4319        strdup(mbm_kw->key);
4320     }
4321     newLine =
4322        addLineTmpl(new, tmplLine,
4323     newLine,
4324     newKernelPath +
4325     strlen(prefix),
4326     config->cfi);
4327     needs &= ~NEED_KERNEL;
4328     }
4329     if (needs & NEED_MB) { /* !mbHyperFirst */
4330     newLine =
4331        addLine(new, config->cfi,
4332        LT_HYPER,
4333        config->
4334        secondaryIndent,
4335        newMBKernel +
4336        strlen(prefix));
4337     needs &= ~NEED_MB;
4338     }
4339     } else if (needs & NEED_KERNEL) {
4340     newLine =
4341        addLineTmpl(new, tmplLine, newLine,
4342     newKernelPath +
4343     strlen(prefix),
4344     config->cfi);
4345     needs &= ~NEED_KERNEL;
4346     }
4347    
4348     } else if (tmplLine->type == LT_HYPER &&
4349       tmplLine->numElements >= 2) {
4350     if (needs & NEED_MB) {
4351     newLine =
4352        addLineTmpl(new, tmplLine, newLine,
4353     newMBKernel +
4354     strlen(prefix),
4355     config->cfi);
4356     needs &= ~NEED_MB;
4357     }
4358    
4359     } else if (tmplLine->type == LT_MBMODULE &&
4360       tmplLine->numElements >= 2) {
4361     if (new->multiboot) {
4362     if (needs & NEED_KERNEL) {
4363     newLine =
4364        addLineTmpl(new, tmplLine,
4365     newLine,
4366     newKernelPath +
4367     strlen(prefix),
4368     config->cfi);
4369     needs &= ~NEED_KERNEL;
4370     } else if (config->cfi->mbInitRdIsModule
4371       && (needs & NEED_INITRD)) {
4372     char *initrdVal;
4373     initrdVal =
4374        getInitrdVal(config, prefix,
4375     tmplLine,
4376     newKernelInitrd,
4377     extraInitrds,
4378     extraInitrdCount);
4379     newLine =
4380        addLineTmpl(new, tmplLine,
4381     newLine,
4382     initrdVal,
4383     config->cfi);
4384     free(initrdVal);
4385     needs &= ~NEED_INITRD;
4386     }
4387     } else if (needs & NEED_KERNEL) {
4388     /* template is multi but new is not,
4389     * insert the kernel in the first
4390     * module slot
4391     */
4392     tmplLine->type =
4393        preferredLineType(LT_KERNEL,
4394          config->cfi);
4395     free(tmplLine->elements[0].item);
4396     tmplLine->elements[0].item =
4397        strdup(getKeywordByType
4398       (tmplLine->type,
4399        config->cfi)->key);
4400     newLine =
4401        addLineTmpl(new, tmplLine, newLine,
4402     newKernelPath +
4403     strlen(prefix),
4404     config->cfi);
4405     needs &= ~NEED_KERNEL;
4406     } else if (needs & NEED_INITRD) {
4407     char *initrdVal;
4408     /* template is multi but new is not,
4409     * insert the initrd in the second
4410     * module slot
4411     */
4412     tmplLine->type =
4413        preferredLineType(LT_INITRD,
4414          config->cfi);
4415     free(tmplLine->elements[0].item);
4416     tmplLine->elements[0].item =
4417        strdup(getKeywordByType
4418       (tmplLine->type,
4419        config->cfi)->key);
4420     initrdVal =
4421        getInitrdVal(config, prefix,
4422     tmplLine,
4423     newKernelInitrd,
4424     extraInitrds,
4425     extraInitrdCount);
4426     newLine =
4427        addLineTmpl(new, tmplLine, newLine,
4428     initrdVal, config->cfi);
4429     free(initrdVal);
4430     needs &= ~NEED_INITRD;
4431     }
4432    
4433     } else if (isinitrd(tmplLine->type)
4434       && tmplLine->numElements >= 2) {
4435     if (needs & NEED_INITRD && new->multiboot
4436        && !template->multiboot
4437        && config->cfi->mbInitRdIsModule) {
4438     /* make sure we don't insert the
4439     * module initrd before the module
4440     * kernel... if we don't do it here,
4441     * it will be inserted following the
4442     * template.
4443     */
4444     if (!needs & NEED_KERNEL) {
4445     char *initrdVal;
4446    
4447     initrdVal =
4448        getInitrdVal(config, prefix,
4449     tmplLine,
4450     newKernelInitrd,
4451     extraInitrds,
4452     extraInitrdCount);
4453     newLine =
4454        addLine(new, config->cfi,
4455        LT_MBMODULE,
4456        config->
4457        secondaryIndent,
4458        initrdVal);
4459     free(initrdVal);
4460     needs &= ~NEED_INITRD;
4461     }
4462     } else if (needs & NEED_INITRD) {
4463     char *initrdVal;
4464     initrdVal =
4465        getInitrdVal(config, prefix,
4466     tmplLine,
4467     newKernelInitrd,
4468     extraInitrds,
4469     extraInitrdCount);
4470     newLine =
4471        addLineTmpl(new, tmplLine, newLine,
4472     initrdVal, config->cfi);
4473     free(initrdVal);
4474     needs &= ~NEED_INITRD;
4475     }
4476    
4477     } else if (tmplLine->type == LT_MENUENTRY &&
4478       (needs & NEED_TITLE)) {
4479     requote(tmplLine, config->cfi);
4480     char *nkt = malloc(strlen(newKernelTitle) + 3);
4481     strcpy(nkt, "'");
4482     strcat(nkt, newKernelTitle);
4483     strcat(nkt, "'");
4484     newLine =
4485        addLineTmpl(new, tmplLine, newLine, nkt,
4486     config->cfi);
4487     free(nkt);
4488     needs &= ~NEED_TITLE;
4489     } else if (tmplLine->type == LT_TITLE &&
4490       (needs & NEED_TITLE)) {
4491     if (tmplLine->numElements >= 2) {
4492     newLine =
4493        addLineTmpl(new, tmplLine, newLine,
4494     newKernelTitle,
4495     config->cfi);
4496     needs &= ~NEED_TITLE;
4497     } else if (tmplLine->numElements == 1 &&
4498       config->cfi->titleBracketed) {
4499     /* addLineTmpl doesn't handle
4500     * titleBracketed */
4501     newLine =
4502        addLine(new, config->cfi, LT_TITLE,
4503        tmplLine->indent,
4504        newKernelTitle);
4505     needs &= ~NEED_TITLE;
4506     }
4507     } else if (tmplLine->type == LT_ECHO) {
4508     requote(tmplLine, config->cfi);
4509     static const char *prefix = "'Loading ";
4510     if (tmplLine->numElements > 1 &&
4511        strstr(tmplLine->elements[1].item, prefix)
4512        && masterLine->next
4513        && iskernel(masterLine->next->type)) {
4514     char *newTitle =
4515        malloc(strlen(prefix) +
4516       strlen(newKernelTitle) + 2);
4517    
4518     strcpy(newTitle, prefix);
4519     strcat(newTitle, newKernelTitle);
4520     strcat(newTitle, "'");
4521     newLine =
4522        addLine(new, config->cfi, LT_ECHO,
4523        tmplLine->indent, newTitle);
4524     free(newTitle);
4525     } else {
4526     /* pass through other lines from the
4527     * template */
4528     newLine =
4529        addLineTmpl(new, tmplLine, newLine,
4530     NULL, config->cfi);
4531     }
4532     } else if (tmplLine->type == LT_DEVTREE &&
4533       tmplLine->numElements == 2
4534       && newDevTreePath) {
4535     newLine =
4536        addLineTmpl(new, tmplLine, newLine,
4537     newDevTreePath + strlen(prefix),
4538     config->cfi);
4539     needs &= ~NEED_DEVTREE;
4540     } else if (tmplLine->type == LT_ENTRY_END
4541       && needs & NEED_DEVTREE) {
4542     const char *ndtp = newDevTreePath;
4543     if (!strncmp
4544        (newDevTreePath, prefix, strlen(prefix)))
4545     ndtp += strlen(prefix);
4546     newLine = addLine(new, config->cfi, LT_DEVTREE,
4547      config->secondaryIndent,
4548      ndtp);
4549     needs &= ~NEED_DEVTREE;
4550     newLine =
4551        addLineTmpl(new, tmplLine, newLine, NULL,
4552     config->cfi);
4553     } else {
4554     /* pass through other lines from the template */
4555     newLine =
4556        addLineTmpl(new, tmplLine, newLine, NULL,
4557     config->cfi);
4558     }
4559     }
4560    
4561      new = malloc(sizeof(*new));   } else {
4562      new->skip = 0;   /* don't have a template, so start the entry with the
4563      new->multiboot = 0;   * appropriate starting line
4564      new->next = config->entries;   */
4565      new->lines = NULL;   switch (config->cfi->entryStart) {
4566      config->entries = new;   case LT_KERNEL:
4567     case LT_KERNEL_EFI:
4568      /* copy/update from the template */   case LT_KERNEL_16:
4569      needs = KERNEL_KERNEL | KERNEL_INITRD | KERNEL_TITLE;   if (new->multiboot && config->cfi->mbHyperFirst) {
4570      if (newMBKernel) {   /* fall through to LT_HYPER */
4571          needs |= KERNEL_MB;   } else {
4572          new->multiboot = 1;   newLine = addLine(new, config->cfi,
4573      }    preferredLineType(LT_KERNEL,
4574        config->
4575        cfi),
4576      config->primaryIndent,
4577      newKernelPath +
4578      strlen(prefix));
4579     needs &= ~NEED_KERNEL;
4580     break;
4581     }
4582    
4583     case LT_HYPER:
4584     newLine = addLine(new, config->cfi, LT_HYPER,
4585      config->primaryIndent,
4586      newMBKernel + strlen(prefix));
4587     needs &= ~NEED_MB;
4588     break;
4589    
4590     case LT_MENUENTRY:{
4591     char *nkt = malloc(strlen(newKernelTitle) + 3);
4592     strcpy(nkt, "'");
4593     strcat(nkt, newKernelTitle);
4594     strcat(nkt, "'");
4595     newLine =
4596        addLine(new, config->cfi, LT_MENUENTRY,
4597        config->primaryIndent, nkt);
4598     free(nkt);
4599     needs &= ~NEED_TITLE;
4600     needs |= NEED_END;
4601     break;
4602     }
4603     case LT_TITLE:
4604     if (useextlinuxmenu != 0) { // We just need useextlinuxmenu to not be zero (set above)
4605     char *templabel;
4606     int x = 0, y = 0;
4607    
4608     templabel = strdup(newKernelTitle);
4609     while (templabel[x]) {
4610     if (templabel[x] == ' ') {
4611     y = x;
4612     while (templabel[y]) {
4613     templabel[y] =
4614        templabel[y + 1];
4615     y++;
4616     }
4617     }
4618     x++;
4619     }
4620     newLine = addLine(new, config->cfi, LT_TITLE,
4621      config->primaryIndent,
4622      templabel);
4623     free(templabel);
4624     } else {
4625     newLine = addLine(new, config->cfi, LT_TITLE,
4626      config->primaryIndent,
4627      newKernelTitle);
4628     }
4629     needs &= ~NEED_TITLE;
4630     break;
4631    
4632      if (template) {   default:
4633   for (tmplLine = template->lines; tmplLine; tmplLine = tmplLine->next) {   abort();
4634      /* remember the indention level; we may need it for new lines */   }
4635      if (tmplLine->numElements)   }
  indent = tmplLine->indent;  
   
     /* skip comments */  
     chptr = tmplLine->indent;  
     while (*chptr && isspace(*chptr)) chptr++;  
     if (*chptr == '#') continue;  
   
     /* we don't need an initrd here */  
     if (tmplLine->type == LT_INITRD && !newKernelInitrd) continue;  
   
             if (tmplLine->type == LT_KERNEL &&  
                 !template->multiboot && (needs & KERNEL_MB)) {  
                 struct singleLine *l;  
                 needs &= ~ KERNEL_MB;  
   
                 l = addLine(new, config->cfi, LT_KERNEL,  
                                   config->secondaryIndent,  
                                   newMBKernel + strlen(prefix));  
                   
                 tmplLine = lastLine;  
                 if (!new->lines) {  
                     new->lines = l;  
                 } else {  
                     newLine->next = l;  
                     newLine = l;  
                 }  
                 continue;  
             } else if (tmplLine->type == LT_KERNEL &&  
                        template->multiboot && !new->multiboot) {  
                 continue; /* don't need multiboot kernel here */  
             }  
   
     if (!new->lines) {  
  newLine = malloc(sizeof(*newLine));  
  new->lines = newLine;  
     } else {  
  newLine->next = malloc(sizeof(*newLine));  
  newLine = newLine->next;  
     }  
   
   
     newLine->indent = strdup(tmplLine->indent);  
     newLine->next = NULL;  
     newLine->type = tmplLine->type;  
     newLine->numElements = tmplLine->numElements;  
     newLine->elements = malloc(sizeof(*newLine->elements) *  
     newLine->numElements);  
     for (i = 0; i < newLine->numElements; i++) {  
  newLine->elements[i].item = strdup(tmplLine->elements[i].item);  
  newLine->elements[i].indent =  
  strdup(tmplLine->elements[i].indent);  
     }  
   
             lastLine = tmplLine;  
     if (tmplLine->type == LT_KERNEL && tmplLine->numElements >= 2) {  
                 char * repl;  
                 if (!template->multiboot) {  
                     needs &= ~KERNEL_KERNEL;  
                     repl = newKernelPath;  
                 } else {  
                     needs &= ~KERNEL_MB;  
                     repl = newMBKernel;  
                 }  
                 if (new->multiboot && !template->multiboot) {  
                     free(newLine->elements[0].item);  
                     newLine->elements[0].item = strdup("module");  
                     newLine->type = LT_MBMODULE;  
                 }  
  free(newLine->elements[1].item);  
                 rootspec = getRootSpecifier(tmplLine->elements[1].item);  
                 if (rootspec != NULL) {  
                     newLine->elements[1].item = sdupprintf("%s%s",  
                                                            rootspec,  
                                                            repl +  
                                                            strlen(prefix));  
                 } else {  
                     newLine->elements[1].item = strdup(repl +  
                                                        strlen(prefix));  
                 }  
             } else if (tmplLine->type == LT_MBMODULE &&  
                        tmplLine->numElements >= 2 && (needs & KERNEL_KERNEL)) {  
                 needs &= ~KERNEL_KERNEL;  
                 if (!new->multiboot && template->multiboot) {  
                     free(newLine->elements[0].item);  
                     newLine->elements[0].item = strdup("kernel");  
                     newLine->type = LT_KERNEL;  
                 }  
  free(newLine->elements[1].item);  
                 rootspec = getRootSpecifier(tmplLine->elements[1].item);  
                 if (rootspec != NULL) {  
                     newLine->elements[1].item = sdupprintf("%s%s",  
                                                            rootspec,  
                                                            newKernelPath +  
                                                            strlen(prefix));  
                 } else {  
                     newLine->elements[1].item = strdup(newKernelPath +  
                                                        strlen(prefix));  
                 }  
     } else if (tmplLine->type == LT_INITRD &&  
     tmplLine->numElements >= 2) {  
  needs &= ~KERNEL_INITRD;  
  free(newLine->elements[1].item);  
                 if (new->multiboot && !template->multiboot) {  
                     free(newLine->elements[0].item);  
                     newLine->elements[0].item = strdup("module");  
                     newLine->type = LT_MBMODULE;  
                 }  
                 rootspec = getRootSpecifier(tmplLine->elements[1].item);  
                 if (rootspec != NULL) {  
                     newLine->elements[1].item = sdupprintf("%s%s",  
                                                            rootspec,  
                                                            newKernelInitrd +  
                                                            strlen(prefix));  
                 } else {  
                     newLine->elements[1].item = strdup(newKernelInitrd +  
                                                        strlen(prefix));  
                 }  
             } else if (tmplLine->type == LT_MBMODULE &&  
                        tmplLine->numElements >= 2 && (needs & KERNEL_INITRD)) {  
  needs &= ~KERNEL_INITRD;  
                 if (!new->multiboot && template->multiboot) {  
                     free(newLine->elements[0].item);  
                     newLine->elements[0].item = strdup("initrd");  
                     newLine->type = LT_INITRD;  
                 }  
  free(newLine->elements[1].item);  
                 rootspec = getRootSpecifier(tmplLine->elements[1].item);  
                 if (rootspec != NULL) {  
                     newLine->elements[1].item = sdupprintf("%s%s",  
                                                            rootspec,  
                                                            newKernelInitrd +  
                                                            strlen(prefix));  
                 } else {  
                     newLine->elements[1].item = strdup(newKernelInitrd +  
                                                        strlen(prefix));  
                 }  
     } else if (tmplLine->type == LT_TITLE &&  
     tmplLine->numElements >= 2) {  
  needs &= ~KERNEL_TITLE;  
   
  for (i = 1; i < newLine->numElements; i++) {  
     free(newLine->elements[i].item);  
     free(newLine->elements[i].indent);  
  }  
   
  newLine->elements[1].item = strdup(newKernelTitle);  
  newLine->elements[1].indent = strdup("");  
  newLine->numElements = 2;  
     } else if (tmplLine->type == LT_TITLE &&  
                        config->cfi->titleBracketed &&  
                        tmplLine->numElements == 1) {  
                 needs &= ~KERNEL_TITLE;  
                 free(newLine->elements[0].item);  
                 free(newLine->elements[0].indent);  
                 newLine->elements = malloc(sizeof(*newLine->elements) *  
                                            newLine->numElements);  
   
                 newLine->elements[0].item = malloc(strlen(newKernelTitle) + 3);  
                 sprintf(newLine->elements[0].item, "[%s]", newKernelTitle);  
                 newLine->elements[0].indent = strdup("");  
                 newLine->numElements = 1;  
             }  
  }  
     } else {  
  for (i = 0; config->cfi->keywords[i].key; i++) {  
     if ((config->cfi->keywords[i].type == config->cfi->entrySeparator) || (config->cfi->keywords[i].type == LT_OTHER))  
  break;  
         }  
4636    
4637   switch (config->cfi->keywords[i].type) {   struct singleLine *endLine = NULL;
4638      case LT_KERNEL:  needs &= ~KERNEL_KERNEL,   endLine = getLineByType(LT_ENTRY_END, new->lines);
4639       chptr = newKernelPath + strlen(prefix);   if (endLine) {
4640       type = LT_KERNEL; break;   removeLine(new, endLine);
4641      case LT_TITLE:   needs &= ~KERNEL_TITLE, chptr = newKernelTitle;   needs |= NEED_END;
4642       type = LT_TITLE; break;   }
4643      default:      
4644                  /* zipl strikes again */   /* add the remainder of the lines, i.e. those that either
4645                  if (config->cfi->titleBracketed) {   * weren't present in the template, or in the case of no template,
4646                      needs &= ~KERNEL_TITLE;   * all the lines following the entryStart.
4647                      chptr = newKernelTitle;   */
4648                      type = LT_TITLE;   if (needs & NEED_TITLE) {
4649                      break;   newLine = addLine(new, config->cfi, LT_TITLE,
4650                  } else {    config->secondaryIndent, newKernelTitle);
4651                      abort();   needs &= ~NEED_TITLE;
4652                  }   }
4653   }   if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4654     newLine = addLine(new, config->cfi, LT_HYPER,
4655   newLine = addLine(new, config->cfi, type, config->primaryIndent, chptr);    config->secondaryIndent,
4656   new->lines = newLine;    newMBKernel + strlen(prefix));
4657      }   needs &= ~NEED_MB;
4658     }
4659      if (new->multiboot) {   if (needs & NEED_KERNEL) {
4660          if (needs & KERNEL_MB)   newLine = addLine(new, config->cfi,
4661              newLine = addLine(new, config->cfi, LT_KERNEL,    (new->multiboot
4662                                config->secondaryIndent,     && getKeywordByType(LT_MBMODULE,
4663                                newMBKernel + strlen(prefix));         config->cfi))
4664          if (needs & KERNEL_KERNEL)    ? LT_MBMODULE : preferredLineType(LT_KERNEL,
4665              newLine = addLine(new, config->cfi, LT_MBMODULE,      config->
4666                                config->secondaryIndent,      cfi),
4667                                newKernelPath + strlen(prefix));    config->secondaryIndent,
4668          /* don't need to check for title as it's guaranteed to have been    newKernelPath + strlen(prefix));
4669           * done as we only do multiboot with grub which uses title as   needs &= ~NEED_KERNEL;
4670           * a separator */   }
4671          if (needs & KERNEL_INITRD && newKernelInitrd)   if (needs & NEED_MB) {
4672              newLine = addLine(new, config->cfi, LT_MBMODULE,   newLine = addLine(new, config->cfi, LT_HYPER,
4673                                config->secondaryIndent,    config->secondaryIndent,
4674                                newKernelInitrd + strlen(prefix));    newMBKernel + strlen(prefix));
4675      } else {   needs &= ~NEED_MB;
4676          if (needs & KERNEL_KERNEL)   }
4677              newLine = addLine(new, config->cfi, LT_KERNEL,   if (needs & NEED_INITRD) {
4678                                config->secondaryIndent,   char *initrdVal;
4679                                newKernelPath + strlen(prefix));   initrdVal =
4680          if (needs & KERNEL_TITLE)      getInitrdVal(config, prefix, NULL, newKernelInitrd,
4681              newLine = addLine(new, config->cfi, LT_TITLE,   extraInitrds, extraInitrdCount);
4682                                config->secondaryIndent,   newLine =
4683                                newKernelTitle);      addLine(new, config->cfi,
4684          if (needs & KERNEL_INITRD && newKernelInitrd)      (new->multiboot
4685              newLine = addLine(new, config->cfi, LT_INITRD,       && getKeywordByType(LT_MBMODULE, config->cfi))
4686                                config->secondaryIndent,      ? LT_MBMODULE : preferredLineType(LT_INITRD,
4687                                newKernelInitrd + strlen(prefix));        config->cfi),
4688      }      config->secondaryIndent, initrdVal);
4689     free(initrdVal);
4690     needs &= ~NEED_INITRD;
4691     }
4692     if (needs & NEED_DEVTREE) {
4693     newLine = addLine(new, config->cfi, LT_DEVTREE,
4694      config->secondaryIndent, newDevTreePath);
4695     needs &= ~NEED_DEVTREE;
4696     }
4697    
4698     /* NEEDS_END must be last on bootloaders that need it... */
4699     if (needs & NEED_END) {
4700     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4701      config->secondaryIndent, NULL);
4702     needs &= ~NEED_END;
4703     }
4704    
4705     if (needs) {
4706     printf(_("grubby: needs=%d, aborting\n"), needs);
4707     abort();
4708     }
4709    
4710      if (updateImage(config, "0", prefix, newKernelArgs, NULL,   if (updateImage(config, "0", prefix, newKernelArgs, NULL,
4711                      newMBKernelArgs, NULL)) return 1;   newMBKernelArgs, NULL))
4712     return 1;
4713    
4714      return 0;   return 0;
4715  }  }
4716    
4717  int main(int argc, const char ** argv) {  int main(int argc, const char **argv)
4718      poptContext optCon;  {
4719      char * grubConfig = NULL;   poptContext optCon;
4720      char * outputFile = NULL;   const char *grubConfig = NULL;
4721      int arg = 0;   char *outputFile = NULL;
4722      int flags = 0;   int arg = 0;
4723      int badImageOkay = 0;   int flags = 0;
4724      int configureLilo = 0, configureELilo = 0, configureGrub = 0;   int badImageOkay = 0;
4725      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;   int configureGrub2 = 0;
4726      int bootloaderProbe = 0;   int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4727      char * updateKernelPath = NULL;   int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4728      char * newKernelPath = NULL;   int configureExtLinux = 0;
4729      char * removeKernelPath = NULL;   int bootloaderProbe = 0;
4730      char * newKernelArgs = NULL;   int extraInitrdCount = 0;
4731      char * newKernelInitrd = NULL;   char *updateKernelPath = NULL;
4732      char * newKernelTitle = NULL;   char *newKernelPath = NULL;
4733      char * newKernelVersion = NULL;   char *removeKernelPath = NULL;
4734      char * newMBKernel = NULL;   char *newKernelArgs = NULL;
4735      char * newMBKernelArgs = NULL;   char *newKernelInitrd = NULL;
4736      char * removeMBKernelArgs = NULL;   char *newKernelTitle = NULL;
4737      char * removeMBKernel = NULL;   char *newDevTreePath = NULL;
4738      char * bootPrefix = NULL;   char *newMBKernel = NULL;
4739      char * defaultKernel = NULL;   char *newMBKernelArgs = NULL;
4740      char * removeArgs = NULL;   char *removeMBKernelArgs = NULL;
4741      char * kernelInfo = NULL;   char *removeMBKernel = NULL;
4742      const char * chptr = NULL;   char *bootPrefix = NULL;
4743      struct configFileInfo * cfi = NULL;   char *defaultKernel = NULL;
4744      struct grubConfig * config;   char *removeArgs = NULL;
4745      struct singleEntry * template = NULL;   char *kernelInfo = NULL;
4746      int copyDefault = 0, makeDefault = 0;   char *extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4747      int displayDefault = 0;   char *envPath = NULL;
4748      struct poptOption options[] = {   const char *chptr = NULL;
4749   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,   struct configFileInfo *cfi = NULL;
4750      _("add an entry for the specified kernel"), _("kernel-path") },   struct grubConfig *config;
4751   { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,   struct singleEntry *template = NULL;
4752      _("add an entry for the specified multiboot kernel"), NULL },   int copyDefault = 0, makeDefault = 0;
4753   { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,   int displayDefault = 0;
4754      _("default arguments for the new kernel or new arguments for "   int displayDefaultIndex = 0;
4755        "kernel being updated"), _("args") },   int displayDefaultTitle = 0;
4756   { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,   int defaultIndex = -1;
4757      _("default arguments for the new multiboot kernel or "   struct poptOption options[] = {
4758                "new arguments for multiboot kernel being updated"), NULL },   {"add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4759   { "bad-image-okay", 0, 0, &badImageOkay, 0,   _("add an entry for the specified kernel"), _("kernel-path")},
4760      _("don't sanity check images in boot entries (for testing only)"),   {"add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4761      NULL },   _("add an entry for the specified multiboot kernel"), NULL},
4762   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,   {"args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4763      _("filestystem which contains /boot directory (for testing only)"),   _("default arguments for the new kernel or new arguments for "
4764      _("bootfs") },     "kernel being updated"), _("args")},
4765  #if defined(__i386__) || defined(__x86_64__)   {"mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4766   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,   _("default arguments for the new multiboot kernel or "
4767      _("check if lilo is installed on lilo.conf boot sector") },     "new arguments for multiboot kernel being updated"), NULL},
4768     {"bad-image-okay", 0, 0, &badImageOkay, 0,
4769     _
4770     ("don't sanity check images in boot entries (for testing only)"),
4771     NULL},
4772     {"boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4773     _
4774     ("filestystem which contains /boot directory (for testing only)"),
4775     _("bootfs")},
4776    #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4777     {"bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4778     _("check which bootloader is installed on boot sector")},
4779  #endif  #endif
4780   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,   {"config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4781      _("path to grub config file to update (\"-\" for stdin)"),   _("path to grub config file to update (\"-\" for stdin)"),
4782      _("path") },   _("path")},
4783   { "copy-default", 0, 0, &copyDefault, 0,   {"copy-default", 0, 0, &copyDefault, 0,
4784      _("use the default boot entry as a template for the new entry "   _("use the default boot entry as a template for the new entry "
4785        "being added; if the default is not a linux image, or if "     "being added; if the default is not a linux image, or if "
4786        "the kernel referenced by the default image does not exist, "     "the kernel referenced by the default image does not exist, "
4787        "the first linux entry whose kernel does exist is used as the "     "the first linux entry whose kernel does exist is used as the "
4788        "template"), NULL },     "template"), NULL},
4789   { "default-kernel", 0, 0, &displayDefault, 0,   {"debug", 0, 0, &debug, 0,
4790      _("display the path of the default kernel") },   _("print debugging information for failures")},
4791   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,   {"default-kernel", 0, 0, &displayDefault, 0,
4792      _("configure elilo bootloader") },   _("display the path of the default kernel")},
4793   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,   {"default-index", 0, 0, &displayDefaultIndex, 0,
4794      _("configure grub bootloader") },   _("display the index of the default kernel")},
4795   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,   {"default-title", 0, 0, &displayDefaultTitle, 0,
4796      _("display boot information for specified kernel"),   _("display the title of the default kernel")},
4797      _("kernel-path") },   {"devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4798   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,   _("device tree file for new stanza"), _("dtb-path")},
4799      _("initrd image for the new kernel"), _("initrd-path") },   {"devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4800   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,   _("device tree directory for new stanza"), _("dtb-path")},
4801      _("configure lilo bootloader") },   {"elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4802   { "make-default", 0, 0, &makeDefault, 0,   _("configure elilo bootloader")},
4803      _("make the newly added entry the default boot entry"), NULL },   {"efi", 0, POPT_ARG_NONE, &isEfi, 0,
4804   { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,   _("force grub2 stanzas to use efi")},
4805      _("path to output updated config file (\"-\" for stdout)"),   {"env", 0, POPT_ARG_STRING, &envPath, 0,
4806      _("path") },   _("path for environment data"),
4807   { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,   _("path")},
4808              _("remove kernel arguments"), NULL },   {"extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4809          { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,   _("configure extlinux bootloader (from syslinux)")},
4810      _("remove multiboot kernel arguments"), NULL },   {"grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4811   { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,   _("configure grub bootloader")},
4812      _("remove all entries for the specified kernel"),   {"grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4813      _("kernel-path") },   _("configure grub2 bootloader")},
4814   { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,   {"info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4815              _("remove all entries for the specified multiboot kernel"), NULL },   _("display boot information for specified kernel"),
4816   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,   _("kernel-path")},
4817      _("make the first entry referencing the specified kernel "   {"initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4818        "the default"), _("kernel-path") },   _("initrd image for the new kernel"), _("initrd-path")},
4819   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,   {"extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4820      _("configure silo bootloader") },   _
4821   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,   ("auxiliary initrd image for things other than the new kernel"),
4822      _("title to use for the new kernel entry"), _("entry-title") },   _("initrd-path")},
4823   { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,   {"lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4824      _("updated information for the specified kernel"),   _("configure lilo bootloader")},
4825      _("kernel-path") },   {"make-default", 0, 0, &makeDefault, 0,
4826   { "version", 'v', 0, NULL, 'v',   _("make the newly added entry the default boot entry"), NULL},
4827      _("print the version of this program and exit"), NULL },   {"output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4828   { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,   _("path to output updated config file (\"-\" for stdout)"),
4829      _("configure yaboot bootloader") },   _("path")},
4830   { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,   {"remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4831      _("configure zipl bootloader") },   _("remove kernel arguments"), NULL},
4832   POPT_AUTOHELP   {"remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4833   { 0, 0, 0, 0, 0 }   _("remove multiboot kernel arguments"), NULL},
4834      };   {"remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4835     _("remove all entries for the specified kernel"),
4836      optCon = poptGetContext("grubby", argc, argv, options, 0);   _("kernel-path")},
4837      poptReadDefaultConfig(optCon, 1);   {"remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4838     _("remove all entries for the specified multiboot kernel"),
4839      while ((arg = poptGetNextOpt(optCon)) >= 0) {   NULL},
4840   switch (arg) {   {"set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4841    case 'v':   _("make the first entry referencing the specified kernel "
4842      printf("grubby version %s\n", VERSION);     "the default"), _("kernel-path")},
4843      exit(0);   {"set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4844      break;   _("make the given entry index the default entry"),
4845     _("entry-index")},
4846     {"silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4847     _("configure silo bootloader")},
4848     {"title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
4849     _("title to use for the new kernel entry"), _("entry-title")},
4850     {"update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
4851     _("updated information for the specified kernel"),
4852     _("kernel-path")},
4853     {"version", 'v', 0, NULL, 'v',
4854     _("print the version of this program and exit"), NULL},
4855     {"yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
4856     _("configure yaboot bootloader")},
4857     {"zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
4858     _("configure zipl bootloader")},
4859     POPT_AUTOHELP {0, 0, 0, 0, 0}
4860     };
4861    
4862     useextlinuxmenu = 0;
4863    
4864     int i = 0;
4865     for (int j = 1; j < argc; j++)
4866     i += strlen(argv[j]) + 1;
4867     saved_command_line = malloc(i);
4868     if (!saved_command_line) {
4869     fprintf(stderr, "grubby: %m\n");
4870     exit(1);
4871     }
4872     saved_command_line[0] = '\0';
4873     for (int j = 1; j < argc; j++) {
4874     strcat(saved_command_line, argv[j]);
4875     strncat(saved_command_line, j == argc - 1 ? "" : " ", 1);
4876     }
4877    
4878     optCon = poptGetContext("grubby", argc, argv, options, 0);
4879     poptReadDefaultConfig(optCon, 1);
4880    
4881     while ((arg = poptGetNextOpt(optCon)) >= 0) {
4882     switch (arg) {
4883     case 'v':
4884     printf("grubby version %s\n", VERSION);
4885     exit(0);
4886     break;
4887     case 'i':
4888     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4889     extraInitrds[extraInitrdCount++] =
4890        strdup(poptGetOptArg(optCon));
4891     } else {
4892     fprintf(stderr,
4893     _
4894     ("grubby: extra initrd maximum is %d\n"),
4895     extraInitrdCount);
4896     return 1;
4897     }
4898     break;
4899     }
4900   }   }
     }  
   
     if (arg < -1) {  
  fprintf(stderr, _("grubby: bad argument %s: %s\n"),  
  poptBadOption(optCon, POPT_BADOPTION_NOALIAS),  
  poptStrerror(arg));  
  return 1;  
     }  
4901    
4902      if ((chptr = poptGetArg(optCon))) {   if (arg < -1) {
4903   fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);   fprintf(stderr, _("grubby: bad argument %s: %s\n"),
4904   return 1;   poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
4905      }   poptStrerror(arg));
4906     return 1;
4907     }
4908    
4909      if ((configureLilo + configureGrub + configureELilo +   if ((chptr = poptGetArg(optCon))) {
4910   configureYaboot + configureSilo + configureZipl) > 1) {   fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
4911   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));   return 1;
4912   return 1;   }
     } else if (bootloaderProbe && grubConfig) {  
  fprintf(stderr,  
     _("grubby: cannot specify config file with --bootloader-probe\n"));  
  return 1;  
     } else if (configureLilo) {  
  cfi = &liloConfigType;  
     } else if (configureGrub) {  
  cfi = &grubConfigType;  
     } else if (configureELilo) {  
  cfi = &eliloConfigType;  
     } else if (configureYaboot) {  
  cfi = &yabootConfigType;  
     } else if (configureSilo) {  
         cfi = &siloConfigType;  
     } else if (configureZipl) {  
         cfi = &ziplConfigType;  
     }  
4913    
4914      if (!cfi) {   if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4915        #ifdef __ia64__       configureYaboot + configureSilo + configureZipl +
4916   cfi = &eliloConfigType;       configureExtLinux) > 1) {
4917        #elif __powerpc__   fprintf(stderr,
4918   cfi = &yabootConfigType;   _("grubby: cannot specify multiple bootloaders\n"));
4919        #elif __sparc__   return 1;
4920          cfi = &siloConfigType;   } else if (bootloaderProbe && grubConfig) {
4921        #elif __s390__   fprintf(stderr,
4922          cfi = &ziplConfigType;   _
4923        #elif __s390x__   ("grubby: cannot specify config file with --bootloader-probe\n"));
4924          cfi = &ziplConfigtype;   return 1;
4925        #else   } else if (configureGrub2) {
4926   cfi = &grubConfigType;   cfi = &grub2ConfigType;
4927        #endif   if (envPath)
4928      }   cfi->envFile = envPath;
4929     } else if (configureLilo) {
4930     cfi = &liloConfigType;
4931     } else if (configureGrub) {
4932     cfi = &grubConfigType;
4933     } else if (configureELilo) {
4934     cfi = &eliloConfigType;
4935     } else if (configureYaboot) {
4936     cfi = &yabootConfigType;
4937     } else if (configureSilo) {
4938     cfi = &siloConfigType;
4939     } else if (configureZipl) {
4940     cfi = &ziplConfigType;
4941     } else if (configureExtLinux) {
4942     cfi = &extlinuxConfigType;
4943     useextlinuxmenu = 1;
4944     }
4945    
4946     if (!cfi) {
4947     if (grub2FindConfig(&grub2ConfigType)) {
4948     cfi = &grub2ConfigType;
4949     if (envPath)
4950     cfi->envFile = envPath;
4951     } else
4952    #ifdef __ia64__
4953     cfi = &eliloConfigType;
4954    #elif __powerpc__
4955     cfi = &yabootConfigType;
4956    #elif __sparc__
4957     cfi = &siloConfigType;
4958    #elif __s390__
4959     cfi = &ziplConfigType;
4960    #elif __s390x__
4961     cfi = &ziplConfigtype;
4962    #else
4963     cfi = &grubConfigType;
4964    #endif
4965     }
4966    
4967      if (!grubConfig)   if (!grubConfig) {
4968   grubConfig = cfi->defaultConfig;   if (cfi->findConfig)
4969     grubConfig = cfi->findConfig(cfi);
4970     if (!grubConfig)
4971     grubConfig = cfi->defaultConfig;
4972     }
4973    
4974      if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||   if (bootloaderProbe && (displayDefault || kernelInfo ||
4975    newKernelPath || removeKernelPath || makeDefault ||   newKernelPath || removeKernelPath || makeDefault
4976    defaultKernel)) {   || defaultKernel || displayDefaultIndex
4977   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "   || displayDefaultTitle
4978     || (defaultIndex >= 0))) {
4979     fprintf(stderr,
4980     _("grubby: --bootloader-probe may not be used with "
4981    "specified option"));    "specified option"));
4982   return 1;   return 1;
4983      }   }
4984    
4985      if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||   if ((displayDefault || kernelInfo) && (newKernelPath ||
4986     removeKernelPath)) {         removeKernelPath)) {
4987   fprintf(stderr, _("grubby: --default-kernel and --info may not "   fprintf(stderr, _("grubby: --default-kernel and --info may not "
4988    "be used when adding or removing kernels\n"));    "be used when adding or removing kernels\n"));
4989   return 1;   return 1;
4990      }   }
4991    
4992      if (newKernelPath && !newKernelTitle) {   if (newKernelPath && !newKernelTitle) {
4993   fprintf(stderr, _("grubby: kernel title must be specified\n"));   fprintf(stderr, _("grubby: kernel title must be specified\n"));
4994   return 1;   return 1;
4995      } else if (!newKernelPath && (newKernelTitle  || newKernelInitrd ||   } else if (!newKernelPath && (copyDefault ||
4996    newKernelInitrd || copyDefault     ||        (newKernelInitrd && !updateKernelPath) ||
4997    makeDefault)) {        makeDefault || extraInitrdCount > 0)) {
4998   fprintf(stderr, _("grubby: kernel path expected\n"));   fprintf(stderr, _("grubby: kernel path expected\n"));
4999   return 1;   return 1;
5000      }   }
5001    
5002      if (newKernelPath && updateKernelPath) {   if (newKernelPath && updateKernelPath) {
5003   fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"   fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
5004            "not be used together"));    "not be used together"));
5005   return 1;   return 1;
5006      }   }
5007    
5008      if (makeDefault && defaultKernel) {   if (makeDefault && defaultKernel) {
5009   fprintf(stderr, _("grubby: --make-default and --default-kernel "   fprintf(stderr, _("grubby: --make-default and --default-kernel "
5010      "may not be used together\n"));
5011     return 1;
5012     } else if (defaultKernel && removeKernelPath &&
5013       !strcmp(defaultKernel, removeKernelPath)) {
5014     fprintf(stderr,
5015     _("grubby: cannot make removed kernel the default\n"));
5016     return 1;
5017     } else if (defaultKernel && newKernelPath &&
5018       !strcmp(defaultKernel, newKernelPath)) {
5019     makeDefault = 1;
5020     defaultKernel = NULL;
5021     } else if (defaultKernel && (defaultIndex >= 0)) {
5022     fprintf(stderr,
5023     _("grubby: --set-default and --set-default-index "
5024    "may not be used together\n"));    "may not be used together\n"));
5025   return 1;   return 1;
5026      } else if (defaultKernel && removeKernelPath &&   }
  !strcmp(defaultKernel, removeKernelPath)) {  
  fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));  
  return 1;  
     } else if (defaultKernel && newKernelPath &&  
  !strcmp(defaultKernel, newKernelPath)) {  
  makeDefault = 1;  
  defaultKernel = NULL;  
     }  
5027    
5028      if (!strcmp(grubConfig, "-") && !outputFile) {   if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
5029   fprintf(stderr, _("grubby: output file must be specified if stdin "   fprintf(stderr,
5030   "is used\n"));   _("grubby: output file must be specified if stdin "
5031   return 1;    "is used\n"));
5032      }   return 1;
5033     }
5034    
5035      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel   if (!removeKernelPath && !newKernelPath && !displayDefault
5036   && !kernelInfo && !bootloaderProbe && !updateKernelPath      && !defaultKernel && !kernelInfo && !bootloaderProbe
5037          && !removeMBKernel) {      && !updateKernelPath && !removeMBKernel && !displayDefaultIndex
5038   fprintf(stderr, _("grubby: no action specified\n"));      && !displayDefaultTitle && (defaultIndex == -1)) {
5039   return 1;   fprintf(stderr, _("grubby: no action specified\n"));
5040      }   return 1;
5041     }
5042    
5043      flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;   flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
5044    
5045      if (cfi->needsBootPrefix) {   if (cfi->needsBootPrefix) {
5046   if (!bootPrefix) {   if (!bootPrefix) {
5047      bootPrefix = findBootPrefix();   bootPrefix = findBootPrefix();
5048      if (!bootPrefix) return 1;   if (!bootPrefix)
5049   } else {   return 1;
5050      /* this shouldn't end with a / */   } else {
5051      if (bootPrefix[strlen(bootPrefix) - 1] == '/')   /* this shouldn't end with a / */
5052   bootPrefix[strlen(bootPrefix) - 1] = '\0';   if (bootPrefix[strlen(bootPrefix) - 1] == '/')
5053     bootPrefix[strlen(bootPrefix) - 1] = '\0';
5054     }
5055     } else {
5056     bootPrefix = "";
5057   }   }
     } else {  
  bootPrefix = "";  
     }  
5058    
5059      if (bootloaderProbe) {   if (!cfi->mbAllowExtraInitRds && extraInitrdCount > 0) {
5060   int lrc = 0, grc = 0;   fprintf(stderr,
5061   struct grubConfig * lconfig, * gconfig;   _("grubby: %s doesn't allow multiple initrds\n"),
5062     cfi->defaultConfig);
5063   if (!access(grubConfigType.defaultConfig, F_OK)) {   return 1;
5064      gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType);   }
     if (!gconfig)  
  grc = 1;  
     else  
  grc = checkForGrub(gconfig);  
  }  
   
  if (!access(liloConfigType.defaultConfig, F_OK)) {  
     lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);  
     if (!lconfig)  
  lrc = 1;  
     else  
  lrc = checkForLilo(lconfig);  
  }  
5065    
5066   if (lrc == 1 || grc == 1) return 1;   if (bootloaderProbe) {
5067     int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
5068     struct grubConfig *lconfig, *gconfig, *yconfig, *econfig;
5069    
5070     const char *grub2config = grub2FindConfig(&grub2ConfigType);
5071     if (grub2config) {
5072     gconfig = readConfig(grub2config, &grub2ConfigType);
5073     if (!gconfig)
5074     gr2c = 1;
5075     else
5076     gr2c = checkForGrub2(gconfig);
5077     }
5078    
5079   if (lrc == 2) printf("lilo\n");   const char *grubconfig = grubFindConfig(&grubConfigType);
5080   if (grc == 2) printf("grub\n");   if (!access(grubconfig, F_OK)) {
5081     gconfig = readConfig(grubconfig, &grubConfigType);
5082     if (!gconfig)
5083     grc = 1;
5084     else
5085     grc = checkForGrub(gconfig);
5086     }
5087    
5088   return 0;   if (!access(liloConfigType.defaultConfig, F_OK)) {
5089      }   lconfig =
5090        readConfig(liloConfigType.defaultConfig,
5091           &liloConfigType);
5092     if (!lconfig)
5093     lrc = 1;
5094     else
5095     lrc = checkForLilo(lconfig);
5096     }
5097    
5098      config = readConfig(grubConfig, cfi);   if (!access(eliloConfigType.defaultConfig, F_OK)) {
5099      if (!config) return 1;   econfig = readConfig(eliloConfigType.defaultConfig,
5100         &eliloConfigType);
5101     if (!econfig)
5102     erc = 1;
5103     else
5104     erc = checkForElilo(econfig);
5105     }
5106    
5107      if (displayDefault) {   if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
5108   struct singleLine * line;   lconfig =
5109   struct singleEntry * entry;      readConfig(extlinuxConfigType.defaultConfig,
5110          char * rootspec;         &extlinuxConfigType);
5111     if (!lconfig)
5112   if (config->defaultImage == -1) return 0;   extrc = 1;
5113   entry = findEntryByIndex(config, config->defaultImage);   else
5114   if (!entry) return 0;   extrc = checkForExtLinux(lconfig);
5115   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;   }
   
  line = entry->lines;  
  while (line && line->type != LT_KERNEL) line = line->next;  
  if (!line) return 0;  
   
         rootspec = getRootSpecifier(line->elements[1].item);  
         printf("%s%s\n", bootPrefix, line->elements[1].item +  
                ((rootspec != NULL) ? strlen(rootspec) : 0));  
5116    
5117   return 0;   if (!access(yabootConfigType.defaultConfig, F_OK)) {
5118      } else if (kernelInfo)   yconfig = readConfig(yabootConfigType.defaultConfig,
5119   return displayInfo(config, kernelInfo, bootPrefix);       &yabootConfigType);
5120     if (!yconfig)
5121     yrc = 1;
5122     else
5123     yrc = checkForYaboot(yconfig);
5124     }
5125    
5126      if (copyDefault) {   if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1
5127   template = findTemplate(config, bootPrefix, NULL, 0, flags);      || erc == 1)
5128   if (!template) return 1;   return 1;
5129      }  
5130     if (lrc == 2)
5131     printf("lilo\n");
5132     if (gr2c == 2)
5133     printf("grub2\n");
5134     if (grc == 2)
5135     printf("grub\n");
5136     if (extrc == 2)
5137     printf("extlinux\n");
5138     if (yrc == 2)
5139     printf("yaboot\n");
5140     if (erc == 2)
5141     printf("elilo\n");
5142    
5143     return 0;
5144     }
5145    
5146     if (grubConfig == NULL) {
5147     printf("Could not find bootloader configuration file.\n");
5148     exit(1);
5149     }
5150    
5151      markRemovedImage(config, removeKernelPath, bootPrefix);   config = readConfig(grubConfig, cfi);
5152      markRemovedImage(config, removeMBKernel, bootPrefix);   if (!config)
5153      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,   return 1;
5154      bootPrefix, flags);  
5155      setFallbackImage(config, newKernelPath != NULL);   if (displayDefault) {
5156      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,   struct singleLine *line;
5157                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;   struct singleEntry *entry;
5158      if (addNewKernel(config, template, bootPrefix, newKernelPath,   char *rootspec;
5159                       newKernelTitle, newKernelArgs, newKernelInitrd,  
5160                       newMBKernel, newMBKernelArgs)) return 1;   if (config->defaultImage == -1)
5161         return 0;
5162     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5163      if (numEntries(config) == 0) {      cfi->defaultIsSaved)
5164          fprintf(stderr, _("grubby: doing this would leave no kernel entries. "   config->defaultImage = 0;
5165                            "Not writing out new config.\n"));   entry = findEntryByIndex(config, config->defaultImage);
5166          return 1;   if (!entry)
5167      }   return 0;
5168     if (!suitableImage(entry, bootPrefix, 0, flags))
5169     return 0;
5170    
5171     line =
5172        getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI |
5173      LT_KERNEL_16, entry->lines);
5174     if (!line)
5175     return 0;
5176    
5177     rootspec = getRootSpecifier(line->elements[1].item);
5178     printf("%s%s\n", bootPrefix, line->elements[1].item +
5179           ((rootspec != NULL) ? strlen(rootspec) : 0));
5180    
5181     return 0;
5182    
5183     } else if (displayDefaultTitle) {
5184     struct singleLine *line;
5185     struct singleEntry *entry;
5186    
5187     if (config->defaultImage == -1)
5188     return 0;
5189     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5190        cfi->defaultIsSaved)
5191     config->defaultImage = 0;
5192     entry = findEntryByIndex(config, config->defaultImage);
5193     if (!entry)
5194     return 0;
5195    
5196     if (!configureGrub2) {
5197     char *title;
5198     line = getLineByType(LT_TITLE, entry->lines);
5199     if (!line)
5200     return 0;
5201     title = extractTitle(config, line);
5202     if (!title)
5203     return 0;
5204     printf("%s\n", title);
5205     free(title);
5206     } else {
5207     char *title;
5208    
5209     dbgPrintf
5210        ("This is GRUB2, default title is embeded in menuentry\n");
5211     line = getLineByType(LT_MENUENTRY, entry->lines);
5212     if (!line)
5213     return 0;
5214     title = grub2ExtractTitle(line);
5215     if (title)
5216     printf("%s\n", title);
5217     }
5218     return 0;
5219    
5220     } else if (displayDefaultIndex) {
5221     if (config->defaultImage == -1)
5222     return 0;
5223     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5224        cfi->defaultIsSaved)
5225     config->defaultImage = 0;
5226     printf("%i\n", config->defaultImage);
5227     return 0;
5228    
5229     } else if (kernelInfo)
5230     return displayInfo(config, kernelInfo, bootPrefix);
5231    
5232     if (copyDefault) {
5233     template = findTemplate(config, bootPrefix, NULL, 0, flags);
5234     if (!template)
5235     return 1;
5236     }
5237    
5238     markRemovedImage(config, removeKernelPath, bootPrefix);
5239     markRemovedImage(config, removeMBKernel, bootPrefix);
5240     setDefaultImage(config, newKernelPath != NULL, defaultKernel,
5241     makeDefault, bootPrefix, flags, defaultIndex);
5242     setFallbackImage(config, newKernelPath != NULL);
5243     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
5244     removeArgs, newMBKernelArgs, removeMBKernelArgs))
5245     return 1;
5246     if (updateKernelPath && newKernelInitrd) {
5247     if (newMBKernel) {
5248     if (addMBInitrd(config, newMBKernel, updateKernelPath,
5249     bootPrefix, newKernelInitrd,
5250     newKernelTitle))
5251     return 1;
5252     } else {
5253     if (updateInitrd(config, updateKernelPath, bootPrefix,
5254     newKernelInitrd, newKernelTitle))
5255     return 1;
5256     }
5257     }
5258     if (addNewKernel(config, template, bootPrefix, newKernelPath,
5259     newKernelTitle, newKernelArgs, newKernelInitrd,
5260     (const char **)extraInitrds, extraInitrdCount,
5261     newMBKernel, newMBKernelArgs, newDevTreePath))
5262     return 1;
5263    
5264     if (numEntries(config) == 0) {
5265     fprintf(stderr,
5266     _("grubby: doing this would leave no kernel entries. "
5267      "Not writing out new config.\n"));
5268     return 1;
5269     }
5270    
5271      if (!outputFile)   if (!outputFile)
5272   outputFile = grubConfig;   outputFile = (char *)grubConfig;
5273    
5274      return writeConfig(config, outputFile, bootPrefix);   return writeConfig(config, outputFile, bootPrefix);
5275  }  }

Legend:
Removed from v.532  
changed lines
  Added in v.3003