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 2980 by niro, Thu Jun 30 10:26:25 2016 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_WHITESPACE   = 1 << 0,
79         LT_BOOTROOT, LT_LBA, LT_MBMODULE, LT_OTHER, LT_GENERIC };      LT_TITLE        = 1 << 1,
80        LT_KERNEL       = 1 << 2,
81        LT_INITRD       = 1 << 3,
82        LT_HYPER        = 1 << 4,
83        LT_DEFAULT      = 1 << 5,
84        LT_MBMODULE     = 1 << 6,
85        LT_ROOT         = 1 << 7,
86        LT_FALLBACK     = 1 << 8,
87        LT_KERNELARGS   = 1 << 9,
88        LT_BOOT         = 1 << 10,
89        LT_BOOTROOT     = 1 << 11,
90        LT_LBA          = 1 << 12,
91        LT_OTHER        = 1 << 13,
92        LT_GENERIC      = 1 << 14,
93        LT_ECHO    = 1 << 16,
94        LT_MENUENTRY    = 1 << 17,
95        LT_ENTRY_END    = 1 << 18,
96        LT_SET_VARIABLE = 1 << 19,
97        LT_KERNEL_EFI   = 1 << 20,
98        LT_INITRD_EFI   = 1 << 21,
99        LT_KERNEL_16    = 1 << 22,
100        LT_INITRD_16    = 1 << 23,
101        LT_DEVTREE      = 1 << 24,
102        LT_UNKNOWN      = 1 << 25,
103    };
104    
105  struct singleLine {  struct singleLine {
106      char * indent;      char * indent;
# Line 61  struct singleEntry { Line 121  struct singleEntry {
121    
122  #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 */
123    
124  #define KERNEL_KERNEL    (1 << 0)  /* These defines are (only) used in addNewKernel() */
125  #define KERNEL_INITRD    (1 << 2)  #define NEED_KERNEL  (1 << 0)
126  #define KERNEL_TITLE    (1 << 3)  #define NEED_INITRD  (1 << 1)
127  #define KERNEL_ARGS    (1 << 4)  #define NEED_TITLE   (1 << 2)
128  #define KERNEL_MB           (1 << 5)  #define NEED_ARGS    (1 << 3)
129    #define NEED_MB      (1 << 4)
130    #define NEED_END     (1 << 5)
131    #define NEED_DEVTREE (1 << 6)
132    
133  #define MAIN_DEFAULT    (1 << 0)  #define MAIN_DEFAULT    (1 << 0)
134  #define DEFAULT_SAVED       -2  #define DEFAULT_SAVED       -2
135    #define DEFAULT_SAVED_GRUB2 -3
136    
137  struct keywordTypes {  struct keywordTypes {
138      char * key;      char * key;
139      enum lineType_e type;      enum lineType_e type;
140      char nextChar;      char nextChar;
141  } ;      char separatorChar;
142    };
143    
144    struct configFileInfo;
145    
146    typedef const char *(*findConfigFunc)(struct configFileInfo *);
147    typedef const int (*writeLineFunc)(struct configFileInfo *,
148     struct singleLine *line);
149    typedef char *(*getEnvFunc)(struct configFileInfo *, char *name);
150    typedef int (*setEnvFunc)(struct configFileInfo *, char *name, char *value);
151    
152  struct configFileInfo {  struct configFileInfo {
153      char * defaultConfig;      char * defaultConfig;
154        findConfigFunc findConfig;
155        writeLineFunc writeLine;
156        getEnvFunc getEnv;
157        setEnvFunc setEnv;
158      struct keywordTypes * keywords;      struct keywordTypes * keywords;
159        int caseInsensitive;
160      int defaultIsIndex;      int defaultIsIndex;
161        int defaultIsVariable;
162      int defaultSupportSaved;      int defaultSupportSaved;
163      enum lineType_e entrySeparator;      int defaultIsSaved;
164        int defaultIsUnquoted;
165        enum lineType_e entryStart;
166        enum lineType_e entryEnd;
167      int needsBootPrefix;      int needsBootPrefix;
168      int argsInQuotes;      int argsInQuotes;
169      int maxTitleLength;      int maxTitleLength;
170      int titleBracketed;      int titleBracketed;
171        int titlePosition;
172        int mbHyperFirst;
173        int mbInitRdIsModule;
174        int mbConcatArgs;
175        int mbAllowExtraInitRds;
176        char *envFile;
177  };  };
178    
179  struct keywordTypes grubKeywords[] = {  struct keywordTypes grubKeywords[] = {
# Line 94  struct keywordTypes grubKeywords[] = { Line 182  struct keywordTypes grubKeywords[] = {
182      { "default",    LT_DEFAULT,    ' ' },      { "default",    LT_DEFAULT,    ' ' },
183      { "fallback",   LT_FALLBACK,    ' ' },      { "fallback",   LT_FALLBACK,    ' ' },
184      { "kernel",    LT_KERNEL,    ' ' },      { "kernel",    LT_KERNEL,    ' ' },
185      { "initrd",    LT_INITRD,    ' ' },      { "initrd",    LT_INITRD,    ' ', ' ' },
186      { "module",     LT_MBMODULE,    ' ' },      { "module",     LT_MBMODULE,    ' ' },
187        { "kernel",     LT_HYPER,       ' ' },
188      { NULL,    0, 0 },      { NULL,    0, 0 },
189  };  };
190    
191    const char *grubFindConfig(struct configFileInfo *cfi) {
192        static const char *configFiles[] = {
193     "/boot/grub/grub.conf",
194     "/boot/grub/menu.lst",
195     "/etc/grub.conf",
196     "/boot/grub2/grub.cfg",
197     "/boot/grub2-efi/grub.cfg",
198     NULL
199        };
200        static int i = -1;
201    
202        if (i == -1) {
203     for (i = 0; configFiles[i] != NULL; i++) {
204        dbgPrintf("Checking \"%s\": ", configFiles[i]);
205        if (!access(configFiles[i], R_OK)) {
206     dbgPrintf("found\n");
207     return configFiles[i];
208        }
209        dbgPrintf("not found\n");
210     }
211        }
212        return configFiles[i];
213    }
214    
215  struct configFileInfo grubConfigType = {  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        static const char *configFiles[] = {
250     "/boot/grub/grub-efi.cfg",
251     "/boot/grub/grub.cfg",
252     NULL
253        };
254        static int i = -1;
255        static const char *grub_cfg = "/boot/grub/grub.cfg";
256        int rc = -1;
257    
258        if (i == -1) {
259     for (i = 0; configFiles[i] != NULL; i++) {
260        dbgPrintf("Checking \"%s\": ", configFiles[i]);
261        if ((rc = access(configFiles[i], R_OK))) {
262     if (errno == EACCES) {
263        printf("Unable to access bootloader configuration file "
264           "\"%s\": %m\n", configFiles[i]);
265        exit(1);
266     }
267     continue;
268        } else {
269     dbgPrintf("found\n");
270     return configFiles[i];
271        }
272     }
273        }
274    
275        /* Ubuntu renames grub2 to grub, so check for the grub.d directory
276         * that isn't in grub1, and if it exists, return the config file path
277         * that they use. */
278        if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) {
279     dbgPrintf("found\n");
280     return grub_cfg;
281        }
282    
283        dbgPrintf("not found\n");
284        return configFiles[i];
285    }
286    
287    /* kind of hacky.  It'll give the first 1024 bytes, ish. */
288    static char *grub2GetEnv(struct configFileInfo *info, char *name)
289    {
290        static char buf[1025];
291        char *s = NULL;
292        char *ret = NULL;
293        char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
294        int rc = asprintf(&s, "grub-editenv %s list | grep '^%s='", envFile, name);
295    
296        if (rc < 0)
297     return NULL;
298    
299        FILE *f = popen(s, "r");
300        if (!f)
301     goto out;
302    
303        memset(buf, '\0', sizeof (buf));
304        ret = fgets(buf, 1024, f);
305        pclose(f);
306    
307        if (ret) {
308     ret += strlen(name) + 1;
309     ret[strlen(ret) - 1] = '\0';
310        }
311        dbgPrintf("grub2GetEnv(%s): %s\n", name, ret);
312    out:
313        free(s);
314        return ret;
315    }
316    
317    static int sPopCount(const char *s, const char *c)
318    {
319        int ret = 0;
320        if (!s)
321     return -1;
322        for (int i = 0; s[i] != '\0'; i++)
323     for (int j = 0; c[j] != '\0'; j++)
324        if (s[i] == c[j])
325     ret++;
326        return ret;
327    }
328    
329    static char *shellEscape(const char *s)
330    {
331        int l = strlen(s) + sPopCount(s, "'") * 2;
332    
333        char *ret = calloc(l+1, sizeof (*ret));
334        if (!ret)
335     return NULL;
336        for (int i = 0, j = 0; s[i] != '\0'; i++, j++) {
337     if (s[i] == '\'')
338        ret[j++] = '\\';
339     ret[j] = s[i];
340        }
341        return ret;
342    }
343    
344    static void unquote(char *s)
345    {
346        int l = strlen(s);
347    
348        if ((s[l-1] == '\'' && s[0] == '\'') || (s[l-1] == '"' && s[0] == '"')) {
349     memmove(s, s+1, l-2);
350     s[l-2] = '\0';
351        }
352    }
353    
354    static int grub2SetEnv(struct configFileInfo *info, char *name, char *value)
355    {
356        char *s = NULL;
357        int rc = 0;
358        char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
359    
360        unquote(value);
361        value = shellEscape(value);
362        if (!value)
363        return -1;
364    
365        rc = asprintf(&s, "grub-editenv %s set '%s=%s'", envFile, name, value);
366        free(value);
367        if (rc <0)
368     return -1;
369    
370        dbgPrintf("grub2SetEnv(%s): %s\n", name, s);
371        rc = system(s);
372        free(s);
373        return rc;
374    }
375    
376    /* this is a gigantic hack to avoid clobbering grub2 variables... */
377    static int is_special_grub2_variable(const char *name)
378    {
379        if (!strcmp(name,"\"${next_entry}\""))
380     return 1;
381        if (!strcmp(name,"\"${prev_saved_entry}\""))
382     return 1;
383        return 0;
384    }
385    
386    int sizeOfSingleLine(struct singleLine * line) {
387      int count = 0;
388    
389      for (int i = 0; i < line->numElements; i++) {
390        int indentSize = 0;
391    
392        count = count + strlen(line->elements[i].item);
393    
394        indentSize = strlen(line->elements[i].indent);
395        if (indentSize > 0)
396          count = count + indentSize;
397        else
398          /* be extra safe and add room for whitespaces */
399          count = count + 1;
400      }
401    
402      /* room for trailing terminator */
403      count = count + 1;
404    
405      return count;
406    }
407    
408    static int isquote(char q)
409    {
410        if (q == '\'' || q == '\"')
411     return 1;
412        return 0;
413    }
414    
415    static int iskernel(enum lineType_e type) {
416        return (type == LT_KERNEL || type == LT_KERNEL_EFI || type == LT_KERNEL_16);
417    }
418    
419    static int isinitrd(enum lineType_e type) {
420        return (type == LT_INITRD || type == LT_INITRD_EFI || type == LT_INITRD_16);
421    }
422    
423    char *grub2ExtractTitle(struct singleLine * line) {
424        char * current;
425        char * current_indent;
426        int current_len;
427        int current_indent_len;
428        int i;
429    
430        /* bail out if line does not start with menuentry */
431        if (strcmp(line->elements[0].item, "menuentry"))
432          return NULL;
433    
434        i = 1;
435        current = line->elements[i].item;
436        current_len = strlen(current);
437    
438        /* if second word is quoted, strip the quotes and return single word */
439        if (isquote(*current) && isquote(current[current_len - 1])) {
440     char *tmp;
441    
442     tmp = strdup(current);
443     *(tmp + current_len - 1) = '\0';
444     return ++tmp;
445        }
446    
447        /* if no quotes, return second word verbatim */
448        if (!isquote(*current))
449     return current;
450    
451        /* second element start with a quote, so we have to find the element
452         * whose last character is also quote (assuming it's the closing one) */
453        int resultMaxSize;
454        char * result;
455        
456        resultMaxSize = sizeOfSingleLine(line);
457        result = malloc(resultMaxSize);
458        snprintf(result, resultMaxSize, "%s", ++current);
459        
460        i++;
461        for (; i < line->numElements; ++i) {
462     current = line->elements[i].item;
463     current_len = strlen(current);
464     current_indent = line->elements[i].indent;
465     current_indent_len = strlen(current_indent);
466    
467     strncat(result, current_indent, current_indent_len);
468     if (!isquote(current[current_len-1])) {
469        strncat(result, current, current_len);
470     } else {
471        strncat(result, current, current_len - 1);
472        break;
473     }
474        }
475        return result;
476    }
477    
478    struct configFileInfo grub2ConfigType = {
479        .findConfig = grub2FindConfig,
480        .getEnv = grub2GetEnv,
481        .setEnv = grub2SetEnv,
482        .keywords = grub2Keywords,
483        .defaultIsIndex = 1,
484        .defaultSupportSaved = 1,
485        .defaultIsVariable = 1,
486        .entryStart = LT_MENUENTRY,
487        .entryEnd = LT_ENTRY_END,
488        .titlePosition = 1,
489        .needsBootPrefix = 1,
490        .mbHyperFirst = 1,
491        .mbInitRdIsModule = 1,
492        .mbAllowExtraInitRds = 1,
493  };  };
494    
495  struct keywordTypes yabootKeywords[] = {  struct keywordTypes yabootKeywords[] = {
# Line 142  struct keywordTypes yabootKeywords[] = { Line 523  struct keywordTypes yabootKeywords[] = {
523      { "partition",  LT_GENERIC,    '=' },      { "partition",  LT_GENERIC,    '=' },
524      { "device",    LT_GENERIC,    '=' },      { "device",    LT_GENERIC,    '=' },
525      { "fstype",    LT_GENERIC,    '=' },      { "fstype",    LT_GENERIC,    '=' },
526      { "initrd",    LT_INITRD,    '=' },      { "initrd",    LT_INITRD,    '=', ';' },
527      { "append",    LT_KERNELARGS,  '=' },      { "append",    LT_KERNELARGS,  '=' },
528      { "boot",    LT_BOOT,    '=' },      { "boot",    LT_BOOT,    '=' },
529      { "lba",    LT_LBA,    ' ' },      { "lba",    LT_LBA,    ' ' },
# Line 162  struct keywordTypes liloKeywords[] = { Line 543  struct keywordTypes liloKeywords[] = {
543      { NULL,    0, 0 },      { NULL,    0, 0 },
544  };  };
545    
546    struct keywordTypes eliloKeywords[] = {
547        { "label",    LT_TITLE,    '=' },
548        { "root",    LT_ROOT,    '=' },
549        { "default",    LT_DEFAULT,    '=' },
550        { "image",    LT_KERNEL,    '=' },
551        { "initrd",    LT_INITRD,    '=' },
552        { "append",    LT_KERNELARGS,  '=' },
553        { "vmm",    LT_HYPER,       '=' },
554        { NULL,    0, 0 },
555    };
556    
557  struct keywordTypes siloKeywords[] = {  struct keywordTypes siloKeywords[] = {
558      { "label",    LT_TITLE,    '=' },      { "label",    LT_TITLE,    '=' },
559      { "root",    LT_ROOT,    '=' },      { "root",    LT_ROOT,    '=' },
# Line 183  struct keywordTypes ziplKeywords[] = { Line 575  struct keywordTypes ziplKeywords[] = {
575      { NULL,         0, 0 },      { NULL,         0, 0 },
576  };  };
577    
578    struct keywordTypes extlinuxKeywords[] = {
579        { "label",    LT_TITLE,    ' ' },
580        { "root",    LT_ROOT,    ' ' },
581        { "default",    LT_DEFAULT,    ' ' },
582        { "kernel",    LT_KERNEL,    ' ' },
583        { "initrd",    LT_INITRD,      ' ', ',' },
584        { "append",    LT_KERNELARGS,  ' ' },
585        { "prompt",     LT_UNKNOWN,     ' ' },
586        { "fdt",        LT_DEVTREE,     ' ' },
587        { "fdtdir",     LT_DEVTREE,     ' ' },
588        { NULL,    0, 0 },
589    };
590    int useextlinuxmenu;
591  struct configFileInfo eliloConfigType = {  struct configFileInfo eliloConfigType = {
592      "/boot/efi/EFI/redhat/elilo.conf",    /* defaultConfig */      .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf",
593      liloKeywords,    /* keywords */      .keywords = eliloKeywords,
594      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
595      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
596      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
597      1,                    /* needsBootPrefix */      .mbConcatArgs = 1,
598      1,    /* argsInQuotes */      .titlePosition = 1,
     0,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
599  };  };
600    
601  struct configFileInfo liloConfigType = {  struct configFileInfo liloConfigType = {
602      "/etc/lilo.conf",    /* defaultConfig */      .defaultConfig = "/etc/lilo.conf",
603      liloKeywords,    /* keywords */      .keywords = liloKeywords,
604      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
605      0,    /* defaultSupportSaved */      .argsInQuotes = 1,
606      LT_KERNEL,    /* entrySeparator */      .maxTitleLength = 15,
607      0,    /* needsBootPrefix */      .titlePosition = 1,
     1,    /* argsInQuotes */  
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
608  };  };
609    
610  struct configFileInfo yabootConfigType = {  struct configFileInfo yabootConfigType = {
611      "/etc/yaboot.conf",    /* defaultConfig */      .defaultConfig = "/etc/yaboot.conf",
612      yabootKeywords,    /* keywords */      .keywords = yabootKeywords,
613      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
614      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
615      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
616      1,    /* needsBootPrefix */      .maxTitleLength = 15,
617      1,    /* argsInQuotes */      .mbAllowExtraInitRds = 1,
618      15,    /* maxTitleLength */      .titlePosition = 1,
     0,                                      /* titleBracketed */  
619  };  };
620    
621  struct configFileInfo siloConfigType = {  struct configFileInfo siloConfigType = {
622      "/etc/silo.conf",    /* defaultConfig */      .defaultConfig = "/etc/silo.conf",
623      siloKeywords,    /* keywords */      .keywords = siloKeywords,
624      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
625      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
626      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
627      1,    /* needsBootPrefix */      .maxTitleLength = 15,
628      1,    /* argsInQuotes */      .titlePosition = 1,
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
629  };  };
630    
631  struct configFileInfo ziplConfigType = {  struct configFileInfo ziplConfigType = {
632      "/etc/zipl.conf",    /* defaultConfig */      .defaultConfig = "/etc/zipl.conf",
633      ziplKeywords,    /* keywords */      .keywords = ziplKeywords,
634      0,    /* defaultIsIndex */      .entryStart = LT_TITLE,
635      0,    /* defaultSupportSaved */      .argsInQuotes = 1,
636      LT_TITLE,    /* entrySeparator */      .titleBracketed = 1,
637      0,    /* needsBootPrefix */  };
638      1,    /* argsInQuotes */  
639      15,    /* maxTitleLength */  struct configFileInfo extlinuxConfigType = {
640      1,                                      /* titleBracketed */      .defaultConfig = "/boot/extlinux/extlinux.conf",
641        .keywords = extlinuxKeywords,
642        .caseInsensitive = 1,
643        .entryStart = LT_TITLE,
644        .needsBootPrefix = 1,
645        .maxTitleLength = 255,
646        .mbAllowExtraInitRds = 1,
647        .defaultIsUnquoted = 1,
648        .titlePosition = 1,
649  };  };
650    
651  struct grubConfig {  struct grubConfig {
# Line 255  struct grubConfig { Line 660  struct grubConfig {
660      struct configFileInfo * cfi;      struct configFileInfo * cfi;
661  };  };
662    
663    blkid_cache blkid;
664    
665  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);
666  struct singleEntry * findEntryByPath(struct grubConfig * cfg,  struct singleEntry * findEntryByPath(struct grubConfig * cfg,
667       const char * path, const char * prefix,       const char * path, const char * prefix,
668       int * index);       int * index);
669  static char * strndup(char * from, int len);  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
670          int * index);
671  static int readFile(int fd, char ** bufPtr);  static int readFile(int fd, char ** bufPtr);
672  static void lineInit(struct singleLine * line);  static void lineInit(struct singleLine * line);
673    struct singleLine * lineDup(struct singleLine * line);
674  static void lineFree(struct singleLine * line);  static void lineFree(struct singleLine * line);
675  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine * line,
676       struct configFileInfo * cfi);       struct configFileInfo * cfi);
677  static int getNextLine(char ** bufPtr, struct singleLine * line,  static int getNextLine(char ** bufPtr, struct singleLine * line,
678         struct configFileInfo * cfi);         struct configFileInfo * cfi);
679  static char * getRootSpecifier(char * str);  static char * getRootSpecifier(char * str);
680    static void requote(struct singleLine *line, struct configFileInfo * cfi);
681  static char * strndup(char * from, int len) {  static void insertElement(struct singleLine * line,
682      char * to;    const char * item, int insertHere,
683      struct configFileInfo * cfi);
684      to = malloc(len + 1);  static void removeElement(struct singleLine * line, int removeHere);
685      strncpy(to, from, len);  static struct keywordTypes * getKeywordByType(enum lineType_e type,
686      to[len] = '\0';        struct configFileInfo * cfi);
687    static enum lineType_e getTypeByKeyword(char * keyword,
688      return to;   struct configFileInfo * cfi);
689  }  static struct singleLine * getLineByType(enum lineType_e type,
690     struct singleLine * line);
691    static int checkForExtLinux(struct grubConfig * config);
692    struct singleLine * addLineTmpl(struct singleEntry * entry,
693                                    struct singleLine * tmplLine,
694                                    struct singleLine * prevLine,
695                                    const char * val,
696     struct configFileInfo * cfi);
697    struct singleLine *  addLine(struct singleEntry * entry,
698                                 struct configFileInfo * cfi,
699                                 enum lineType_e type, char * defaultIndent,
700                                 const char * val);
701    
702  static char * sdupprintf(const char *format, ...)  static char * sdupprintf(const char *format, ...)
703  #ifdef __GNUC__  #ifdef __GNUC__
# Line 313  static char * sdupprintf(const char *for Line 732  static char * sdupprintf(const char *for
732      return buf;      return buf;
733  }  }
734    
735    static enum lineType_e preferredLineType(enum lineType_e type,
736     struct configFileInfo *cfi) {
737        if (isEfi && cfi == &grub2ConfigType) {
738     switch (type) {
739     case LT_KERNEL:
740        return isEfiOnly ? LT_KERNEL : LT_KERNEL_EFI;
741     case LT_INITRD:
742        return isEfiOnly ? LT_INITRD : LT_INITRD_EFI;
743     default:
744        return type;
745     }
746    #if defined(__i386__) || defined(__x86_64__)
747        } else if (cfi == &grub2ConfigType) {
748     switch (type) {
749     case LT_KERNEL:
750        return LT_KERNEL_16;
751     case LT_INITRD:
752        return LT_INITRD_16;
753     default:
754        return type;
755     }
756    #endif
757        }
758        return type;
759    }
760    
761    static struct keywordTypes * getKeywordByType(enum lineType_e type,
762          struct configFileInfo * cfi) {
763        for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
764     if (kw->type == type)
765        return kw;
766        }
767        return NULL;
768    }
769    
770    static char *getKeyByType(enum lineType_e type, struct configFileInfo * cfi) {
771        struct keywordTypes *kt = getKeywordByType(type, cfi);
772        if (kt)
773     return kt->key;
774        return "unknown";
775    }
776    
777    static char * getpathbyspec(char *device) {
778        if (!blkid)
779            blkid_get_cache(&blkid, NULL);
780    
781        return blkid_get_devname(blkid, device, NULL);
782    }
783    
784    static char * getuuidbydev(char *device) {
785        if (!blkid)
786     blkid_get_cache(&blkid, NULL);
787    
788        return blkid_get_tag_value(blkid, "UUID", device);
789    }
790    
791    static enum lineType_e getTypeByKeyword(char * keyword,
792     struct configFileInfo * cfi) {
793        for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
794     if (cfi->caseInsensitive) {
795        if (!strcasecmp(keyword, kw->key))
796                    return kw->type;
797     } else {
798        if (!strcmp(keyword, kw->key))
799            return kw->type;
800     }
801        }
802        return LT_UNKNOWN;
803    }
804    
805    static struct singleLine * getLineByType(enum lineType_e type,
806     struct singleLine * line) {
807        dbgPrintf("getLineByType(%d): ", type);
808        for (; line; line = line->next) {
809     dbgPrintf("%d:%s ", line->type,
810      line->numElements ? line->elements[0].item : "(empty)");
811     if (line->type & type) break;
812        }
813        dbgPrintf(line ? "\n" : " (failed)\n");
814        return line;
815    }
816    
817  static int isBracketedTitle(struct singleLine * line) {  static int isBracketedTitle(struct singleLine * line) {
818      if ((*line->elements[0].item == '[') && (line->numElements == 1)) {      if (line->numElements == 1 && *line->elements[0].item == '[') {
819          int len = strlen(line->elements[0].item);          int len = strlen(line->elements[0].item);
820          if (*(line->elements[0].item + len - 1) == ']') {          if (*(line->elements[0].item + len - 1) == ']') {
821              /* FIXME: this is a hack... */              /* FIXME: this is a hack... */
# Line 326  static int isBracketedTitle(struct singl Line 827  static int isBracketedTitle(struct singl
827      return 0;      return 0;
828  }  }
829    
830  /* figure out if this is a entry separator */  static int isEntryStart(struct singleLine * line,
 static int isEntrySeparator(struct singleLine * line,  
831                              struct configFileInfo * cfi) {                              struct configFileInfo * cfi) {
832      if (line->type == LT_WHITESPACE)      return line->type == cfi->entryStart || line->type == LT_OTHER ||
833   return 0;   (cfi->titleBracketed && isBracketedTitle(line));
     if (line->type == cfi->entrySeparator)  
         return 1;  
     if (line->type == LT_OTHER)  
         return 1;  
     if (cfi->titleBracketed && isBracketedTitle(line)) {  
         return 1;  
     }  
     return 0;  
834  }  }
835    
836  /* extract the title from within brackets (for zipl) */  /* extract the title from within brackets (for zipl) */
837  static char * extractTitle(struct singleLine * line) {  static char * extractTitle(struct grubConfig *cfg, struct singleLine * line) {
838      /* bracketed title... let's extract it (leaks a byte) */      /* bracketed title... let's extract it */
839      char * title;      char * title = NULL;
840      title = strdup(line->elements[0].item);      if (cfg->cfi == &grub2ConfigType)
841      title++;   return grub2ExtractTitle(line);
842      *(title + strlen(title) - 1) = '\0';      if (line->type == LT_TITLE) {
843     char *tmp = line->elements[cfg->cfi->titlePosition].item;
844     if (cfg->cfi->titleBracketed) {
845        tmp++;
846        title = strdup(tmp);
847        *(title + strlen(title) - 1) = '\0';
848     } else {
849        title = strdup(tmp);
850     }
851        } else if (line->type == LT_MENUENTRY)
852     title = strdup(line->elements[1].item);
853        else
854     return NULL;
855      return title;      return title;
856  }  }
857    
# Line 389  static void lineInit(struct singleLine * Line 893  static void lineInit(struct singleLine *
893      line->next = NULL;      line->next = NULL;
894  }  }
895    
896  static void lineFree(struct singleLine * line) {  struct singleLine * lineDup(struct singleLine * line) {
897      int i;      struct singleLine * newLine = malloc(sizeof(*newLine));
898    
899        newLine->indent = strdup(line->indent);
900        newLine->next = NULL;
901        newLine->type = line->type;
902        newLine->numElements = line->numElements;
903        newLine->elements = malloc(sizeof(*newLine->elements) *
904           newLine->numElements);
905    
906        for (int i = 0; i < newLine->numElements; i++) {
907     newLine->elements[i].indent = strdup(line->elements[i].indent);
908     newLine->elements[i].item = strdup(line->elements[i].item);
909        }
910    
911        return newLine;
912    }
913    
914    static void lineFree(struct singleLine * line) {
915      if (line->indent) free(line->indent);      if (line->indent) free(line->indent);
916    
917      for (i = 0; i < line->numElements; i++) {      for (int i = 0; i < line->numElements; i++) {
918   free(line->elements[i].item);   free(line->elements[i].item);
919   free(line->elements[i].indent);   free(line->elements[i].indent);
920      }      }
# Line 405  static void lineFree(struct singleLine * Line 925  static void lineFree(struct singleLine *
925    
926  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine * line,
927       struct configFileInfo * cfi) {       struct configFileInfo * cfi) {
     int i;  
   
928      if (fprintf(out, "%s", line->indent) == -1) return -1;      if (fprintf(out, "%s", line->indent) == -1) return -1;
929    
930      for (i = 0; i < line->numElements; i++) {      for (int i = 0; i < line->numElements; i++) {
931     /* Need to handle this, because we strip the quotes from
932     * menuentry when read it. */
933     if (line->type == LT_MENUENTRY && i == 1) {
934        if(!isquote(*line->elements[i].item))
935     fprintf(out, "\'%s\'", line->elements[i].item);
936        else
937     fprintf(out, "%s", line->elements[i].item);
938        fprintf(out, "%s", line->elements[i].indent);
939    
940        continue;
941     }
942    
943   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)
944      if (fputc('"', out) == EOF) return -1;      if (fputc('"', out) == EOF) return -1;
945    
946   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;
947   if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;   if (i < line->numElements - 1)
948        if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;
949      }      }
950    
951      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)
# Line 433  static int getNextLine(char ** bufPtr, s Line 964  static int getNextLine(char ** bufPtr, s
964      char * chptr;      char * chptr;
965      int elementsAlloced = 0;      int elementsAlloced = 0;
966      struct lineElement * element;      struct lineElement * element;
     struct keywordTypes * keywords = cfi->keywords;  
967      int first = 1;      int first = 1;
     int i;  
968    
969      lineFree(line);      lineFree(line);
970    
# Line 489  static int getNextLine(char ** bufPtr, s Line 1018  static int getNextLine(char ** bufPtr, s
1018      if (!line->numElements)      if (!line->numElements)
1019   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1020      else {      else {
1021   for (i = 0; keywords[i].key; i++)   line->type = getTypeByKeyword(line->elements[0].item, cfi);
1022      if (!strcmp(line->elements[0].item, keywords[i].key)) break;   if (line->type == LT_UNKNOWN) {
   
  if (keywords[i].key) {  
     line->type = keywords[i].type;  
  } else {  
     line->type = LT_UNKNOWN;  
               
1023              /* zipl does [title] instead of something reasonable like all              /* zipl does [title] instead of something reasonable like all
1024               * the other boot loaders.  kind of ugly */               * the other boot loaders.  kind of ugly */
1025              if (cfi->titleBracketed && isBracketedTitle(line)) {              if (cfi->titleBracketed && isBracketedTitle(line)) {
# Line 510  static int getNextLine(char ** bufPtr, s Line 1033  static int getNextLine(char ** bufPtr, s
1033      if (*line->elements[0].item == '#') {      if (*line->elements[0].item == '#') {
1034   char * fullLine;   char * fullLine;
1035   int len;   int len;
  int i;  
1036    
1037   len = strlen(line->indent);   len = strlen(line->indent);
1038   for (i = 0; i < line->numElements; i++)   for (int i = 0; i < line->numElements; i++)
1039      len += strlen(line->elements[i].item) +      len += strlen(line->elements[i].item) +
1040     strlen(line->elements[i].indent);     strlen(line->elements[i].indent);
1041    
# Line 522  static int getNextLine(char ** bufPtr, s Line 1044  static int getNextLine(char ** bufPtr, s
1044   free(line->indent);   free(line->indent);
1045   line->indent = fullLine;   line->indent = fullLine;
1046    
1047   for (i = 0; i < line->numElements; i++) {   for (int i = 0; i < line->numElements; i++) {
1048      strcat(fullLine, line->elements[i].item);      strcat(fullLine, line->elements[i].item);
1049      strcat(fullLine, line->elements[i].indent);      strcat(fullLine, line->elements[i].indent);
1050      free(line->elements[i].item);      free(line->elements[i].item);
# Line 532  static int getNextLine(char ** bufPtr, s Line 1054  static int getNextLine(char ** bufPtr, s
1054   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1055   line->numElements = 0;   line->numElements = 0;
1056      }      }
1057     } else {
1058     struct keywordTypes *kw;
1059    
1060     kw = getKeywordByType(line->type, cfi);
1061    
1062     /* space isn't the only separator, we need to split
1063     * elements up more
1064     */
1065     if (!isspace(kw->separatorChar)) {
1066        char indent[2] = "";
1067        indent[0] = kw->separatorChar;
1068        for (int i = 1; i < line->numElements; i++) {
1069     char *p;
1070     int numNewElements;
1071    
1072     numNewElements = 0;
1073     p = line->elements[i].item;
1074     while (*p != '\0') {
1075     if (*p == kw->separatorChar)
1076     numNewElements++;
1077     p++;
1078     }
1079     if (line->numElements + numNewElements >= elementsAlloced) {
1080     elementsAlloced += numNewElements + 5;
1081     line->elements = realloc(line->elements,
1082        sizeof(*line->elements) * elementsAlloced);
1083     }
1084    
1085     for (int j = line->numElements; j > i; j--) {
1086     line->elements[j + numNewElements] = line->elements[j];
1087     }
1088     line->numElements += numNewElements;
1089    
1090     p = line->elements[i].item;
1091     while (*p != '\0') {
1092    
1093     while (*p != kw->separatorChar && *p != '\0') p++;
1094     if (*p == '\0') {
1095     break;
1096     }
1097    
1098     line->elements[i + 1].indent = line->elements[i].indent;
1099     line->elements[i].indent = strdup(indent);
1100     *p++ = '\0';
1101     i++;
1102     line->elements[i].item = strdup(p);
1103     }
1104        }
1105     }
1106   }   }
1107      }      }
1108    
1109      return 0;      return 0;
1110  }  }
1111    
1112    static int isnumber(const char *s)
1113    {
1114        int i;
1115        for (i = 0; s[i] != '\0'; i++)
1116     if (s[i] < '0' || s[i] > '9')
1117        return 0;
1118        return i;
1119    }
1120    
1121  static struct grubConfig * readConfig(const char * inName,  static struct grubConfig * readConfig(const char * inName,
1122        struct configFileInfo * cfi) {        struct configFileInfo * cfi) {
1123      int in;      int in;
# Line 549  static struct grubConfig * readConfig(co Line 1129  static struct grubConfig * readConfig(co
1129      struct singleLine * last = NULL, * line, * defaultLine = NULL;      struct singleLine * last = NULL, * line, * defaultLine = NULL;
1130      char * end;      char * end;
1131      struct singleEntry * entry = NULL;      struct singleEntry * entry = NULL;
1132      int i, len;      int len;
1133      char * buf;      char * buf;
1134    
1135      if (!strcmp(inName, "-")) {      if (inName == NULL) {
1136            printf("Could not find bootloader configuration\n");
1137            exit(1);
1138        } else if (!strcmp(inName, "-")) {
1139   in = 0;   in = 0;
1140      } else {      } else {
1141   if ((in = open(inName, O_RDONLY)) < 0) {   if ((in = open(inName, O_RDONLY)) < 0) {
# Line 595  static struct grubConfig * readConfig(co Line 1178  static struct grubConfig * readConfig(co
1178      cfg->secondaryIndent = strdup(line->indent);      cfg->secondaryIndent = strdup(line->indent);
1179   }   }
1180    
1181   if (isEntrySeparator(line, cfi)) {   if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
1182      sawEntry = 1;      sawEntry = 1;
1183      if (!entry) {      if (!entry) {
1184   cfg->entries = malloc(sizeof(*entry));   cfg->entries = malloc(sizeof(*entry));
# Line 611  static struct grubConfig * readConfig(co Line 1194  static struct grubConfig * readConfig(co
1194      entry->next = NULL;      entry->next = NULL;
1195   }   }
1196    
1197   if (line->type == LT_DEFAULT && line->numElements == 2) {   if (line->type == LT_SET_VARIABLE) {
1198      cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;      dbgPrintf("found 'set' command (%d elements): ", line->numElements);
1199      defaultLine = line;      dbgPrintf("%s", line->indent);
1200        for (int i = 0; i < line->numElements; i++)
1201     dbgPrintf("\"%s\"%s", line->elements[i].item, line->elements[i].indent);
1202        dbgPrintf("\n");
1203        struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi);
1204        if (kwType && line->numElements == 3 &&
1205        !strcmp(line->elements[1].item, kwType->key) &&
1206        !is_special_grub2_variable(line->elements[2].item)) {
1207     dbgPrintf("Line sets default config\n");
1208     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1209     defaultLine = line;
1210        }
1211    
1212            } else if (iskernel(line->type)) {
1213        /* if by some freak chance this is multiboot and the "module"
1214         * lines came earlier in the template, make sure to use LT_HYPER
1215         * instead of LT_KERNEL now
1216         */
1217        if (entry && entry->multiboot)
1218     line->type = LT_HYPER;
1219    
1220          } else if (line->type == LT_MBMODULE) {          } else if (line->type == LT_MBMODULE) {
1221        /* go back and fix the LT_KERNEL line to indicate LT_HYPER
1222         * instead, now that we know this is a multiboot entry.
1223         * This only applies to grub, but that's the only place we
1224         * should find LT_MBMODULE lines anyway.
1225         */
1226        for (struct singleLine *l = entry->lines; l; l = l->next) {
1227     if (l->type == LT_HYPER)
1228        break;
1229     else if (iskernel(l->type)) {
1230        l->type = LT_HYPER;
1231        break;
1232     }
1233        }
1234              entry->multiboot = 1;              entry->multiboot = 1;
1235    
1236     } else if (line->type == LT_HYPER) {
1237        entry->multiboot = 1;
1238    
1239   } else if (line->type == LT_FALLBACK && line->numElements == 2) {   } else if (line->type == LT_FALLBACK && line->numElements == 2) {
1240      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);
1241      if (*end) cfg->fallbackImage = -1;      if (*end) cfg->fallbackImage = -1;
1242   } else if (line->type == LT_TITLE && line->numElements > 1) {  
1243      /* make the title a single argument (undoing our parsing) */   } else if ((line->type == LT_DEFAULT && cfi->defaultIsUnquoted) ||
1244                    (line->type == LT_TITLE && line->numElements > 1)) {
1245        /* make the title/default a single argument (undoing our parsing) */
1246      len = 0;      len = 0;
1247      for (i = 1; i < line->numElements; i++) {      for (int i = 1; i < line->numElements; i++) {
1248   len += strlen(line->elements[i].item);   len += strlen(line->elements[i].item);
1249   len += strlen(line->elements[i].indent);   len += strlen(line->elements[i].indent);
1250      }      }
1251      buf = malloc(len + 1);      buf = malloc(len + 1);
1252      *buf = '\0';      *buf = '\0';
1253    
1254      for (i = 1; i < line->numElements; i++) {      for (int i = 1; i < line->numElements; i++) {
1255   strcat(buf, line->elements[i].item);   strcat(buf, line->elements[i].item);
1256   free(line->elements[i].item);   free(line->elements[i].item);
1257    
# Line 643  static struct grubConfig * readConfig(co Line 1265  static struct grubConfig * readConfig(co
1265      line->elements[line->numElements - 1].indent;      line->elements[line->numElements - 1].indent;
1266      line->elements[1].item = buf;      line->elements[1].item = buf;
1267      line->numElements = 2;      line->numElements = 2;
1268     } else if (line->type == LT_MENUENTRY && line->numElements > 3) {
1269        /* let --remove-kernel="TITLE=what" work */
1270        len = 0;
1271        char *extras;
1272        char *title;
1273    
1274        for (int i = 1; i < line->numElements; i++) {
1275     len += strlen(line->elements[i].item);
1276     len += strlen(line->elements[i].indent);
1277        }
1278        buf = malloc(len + 1);
1279        *buf = '\0';
1280    
1281        /* allocate mem for extra flags. */
1282        extras = malloc(len + 1);
1283        *extras = '\0';
1284    
1285        /* get title. */
1286        for (int i = 0; i < line->numElements; i++) {
1287     if (!strcmp(line->elements[i].item, "menuentry"))
1288        continue;
1289     if (isquote(*line->elements[i].item))
1290        title = line->elements[i].item + 1;
1291     else
1292        title = line->elements[i].item;
1293    
1294     len = strlen(title);
1295            if (isquote(title[len-1])) {
1296        strncat(buf, title,len-1);
1297        break;
1298     } else {
1299        strcat(buf, title);
1300        strcat(buf, line->elements[i].indent);
1301     }
1302        }
1303    
1304        /* get extras */
1305        int count = 0;
1306        for (int i = 0; i < line->numElements; i++) {
1307     if (count >= 2) {
1308        strcat(extras, line->elements[i].item);
1309        strcat(extras, line->elements[i].indent);
1310     }
1311    
1312     if (!strcmp(line->elements[i].item, "menuentry"))
1313        continue;
1314    
1315     /* count ' or ", there should be two in menuentry line. */
1316     if (isquote(*line->elements[i].item))
1317        count++;
1318    
1319     len = strlen(line->elements[i].item);
1320    
1321     if (isquote(line->elements[i].item[len -1]))
1322        count++;
1323    
1324     /* ok, we get the final ' or ", others are extras. */
1325                }
1326        line->elements[1].indent =
1327     line->elements[line->numElements - 2].indent;
1328        line->elements[1].item = buf;
1329        line->elements[2].indent =
1330     line->elements[line->numElements - 2].indent;
1331        line->elements[2].item = extras;
1332        line->numElements = 3;
1333   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {
1334      /* Strip off any " which may be present; they'll be put back      /* Strip off any " which may be present; they'll be put back
1335         on write. This is one of the few (the only?) places that grubby         on write. This is one of the few (the only?) places that grubby
# Line 651  static struct grubConfig * readConfig(co Line 1338  static struct grubConfig * readConfig(co
1338      if (line->numElements >= 2) {      if (line->numElements >= 2) {
1339   int last, len;   int last, len;
1340    
1341   if (*line->elements[1].item == '"')   if (isquote(*line->elements[1].item))
1342      memcpy(line->elements[1].item, line->elements[1].item + 1,      memmove(line->elements[1].item, line->elements[1].item + 1,
1343     strlen(line->elements[1].item + 1) + 1);      strlen(line->elements[1].item + 1) + 1);
1344    
1345   last = line->numElements - 1;   last = line->numElements - 1;
1346   len = strlen(line->elements[last].item) - 1;   len = strlen(line->elements[last].item) - 1;
1347   if (line->elements[last].item[len] == '"')   if (isquote(line->elements[last].item[len]))
1348      line->elements[last].item[len] = '\0';      line->elements[last].item[len] = '\0';
1349      }      }
1350     }
1351    
1352     if (line->type == LT_DEFAULT && line->numElements == 2) {
1353        cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1354        defaultLine = line;
1355   }   }
1356    
1357   /* If we find a generic config option which should live at the   /* If we find a generic config option which should live at the
# Line 680  static struct grubConfig * readConfig(co Line 1371  static struct grubConfig * readConfig(co
1371   movedLine = 1;   movedLine = 1;
1372   continue; /* without setting 'last' */   continue; /* without setting 'last' */
1373   }   }
1374    
1375   /* If a second line of whitespace happens after a generic option   /* If a second line of whitespace happens after a generic option
1376     which was moved, drop it. */     which was moved, drop it. */
1377   if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {   if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {
# Line 695  static struct grubConfig * readConfig(co Line 1387  static struct grubConfig * readConfig(co
1387   entry->lines = line;   entry->lines = line;
1388      else      else
1389   last->next = line;   last->next = line;
1390        dbgPrintf("readConfig added %s to %p\n", getKeyByType(line->type, cfi), entry);
1391    
1392        /* we could have seen this outside of an entry... if so, we
1393         * ignore it like any other line we don't grok */
1394        if (line->type == LT_ENTRY_END && sawEntry)
1395     sawEntry = 0;
1396   } else {   } else {
1397      if (!cfg->theLines)      if (!cfg->theLines)
1398   cfg->theLines = line;   cfg->theLines = line;
1399      else {      else
1400   last->next = line;   last->next = line;
1401      }      dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi));
1402   }   }
1403    
1404   last = line;   last = line;
# Line 708  static struct grubConfig * readConfig(co Line 1406  static struct grubConfig * readConfig(co
1406    
1407      free(incoming);      free(incoming);
1408    
1409        dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");
1410      if (defaultLine) {      if (defaultLine) {
1411   if (cfi->defaultSupportSaved &&          if (defaultLine->numElements > 2 &&
1412        cfi->defaultSupportSaved &&
1413        !strncmp(defaultLine->elements[2].item,"\"${saved_entry}\"", 16)) {
1414     cfg->cfi->defaultIsSaved = 1;
1415     cfg->defaultImage = DEFAULT_SAVED_GRUB2;
1416     if (cfg->cfi->getEnv) {
1417        char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1418        if (defTitle) {
1419     int index = 0;
1420     if (isnumber(defTitle)) {
1421        index = atoi(defTitle);
1422        entry = findEntryByIndex(cfg, index);
1423     } else {
1424        entry = findEntryByTitle(cfg, defTitle, &index);
1425     }
1426     if (entry)
1427        cfg->defaultImage = index;
1428        }
1429     }
1430     } else if (cfi->defaultIsVariable) {
1431        char *value = defaultLine->elements[2].item;
1432        while (*value && (*value == '"' || *value == '\'' ||
1433        *value == ' ' || *value == '\t'))
1434     value++;
1435        cfg->defaultImage = strtol(value, &end, 10);
1436        while (*end && (*end == '"' || *end == '\'' ||
1437        *end == ' ' || *end == '\t'))
1438     end++;
1439        if (*end) cfg->defaultImage = -1;
1440     } else if (cfi->defaultSupportSaved &&
1441   !strncmp(defaultLine->elements[1].item, "saved", 5)) {   !strncmp(defaultLine->elements[1].item, "saved", 5)) {
1442      cfg->defaultImage = DEFAULT_SAVED;      cfg->defaultImage = DEFAULT_SAVED;
1443   } else if (cfi->defaultIsIndex) {   } else if (cfi->defaultIsIndex) {
1444      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);
1445      if (*end) cfg->defaultImage = -1;      if (*end) cfg->defaultImage = -1;
1446   } else if (defaultLine->numElements >= 2) {   } else if (defaultLine->numElements >= 2) {
1447      i = 0;      int i = 0;
1448      while ((entry = findEntryByIndex(cfg, i))) {      while ((entry = findEntryByIndex(cfg, i))) {
1449   for (line = entry->lines; line; line = line->next)   for (line = entry->lines; line; line = line->next)
1450      if (line->type == LT_TITLE) break;      if (line->type == LT_TITLE) break;
# Line 727  static struct grubConfig * readConfig(co Line 1455  static struct grubConfig * readConfig(co
1455                                  line->elements[1].item)) break;                                  line->elements[1].item)) break;
1456                  } else if (line) {                  } else if (line) {
1457                      if (!strcmp(defaultLine->elements[1].item,                      if (!strcmp(defaultLine->elements[1].item,
1458                                  extractTitle(line))) break;                                  extractTitle(cfg, line))) break;
1459                  }                  }
1460   i++;   i++;
1461     entry = NULL;
1462      }      }
1463    
1464      if (entry) cfg->defaultImage = i;      if (entry){
1465            cfg->defaultImage = i;
1466        }else{
1467            cfg->defaultImage = -1;
1468        }
1469     }
1470        } else if (cfg->cfi->defaultIsSaved && cfg->cfi->getEnv) {
1471     char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1472     if (defTitle) {
1473        int index = 0;
1474        if (isnumber(defTitle)) {
1475     index = atoi(defTitle);
1476     entry = findEntryByIndex(cfg, index);
1477        } else {
1478     entry = findEntryByTitle(cfg, defTitle, &index);
1479        }
1480        if (entry)
1481     cfg->defaultImage = index;
1482   }   }
1483        } else {
1484            cfg->defaultImage = 0;
1485      }      }
1486    
1487      return cfg;      return cfg;
# Line 749  static void writeDefault(FILE * out, cha Line 1497  static void writeDefault(FILE * out, cha
1497    
1498      if (cfg->defaultImage == DEFAULT_SAVED)      if (cfg->defaultImage == DEFAULT_SAVED)
1499   fprintf(out, "%sdefault%ssaved\n", indent, separator);   fprintf(out, "%sdefault%ssaved\n", indent, separator);
1500      else if (cfg->defaultImage > -1) {      else if (cfg->cfi->defaultIsSaved) {
1501     fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1502     if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {
1503        char *title;
1504        entry = findEntryByIndex(cfg, cfg->defaultImage);
1505        line = getLineByType(LT_MENUENTRY, entry->lines);
1506        if (!line)
1507     line = getLineByType(LT_TITLE, entry->lines);
1508        if (line) {
1509     title = extractTitle(cfg, line);
1510     if (title)
1511        cfg->cfi->setEnv(cfg->cfi, "saved_entry", title);
1512        }
1513     }
1514        } else if (cfg->defaultImage > -1) {
1515   if (cfg->cfi->defaultIsIndex) {   if (cfg->cfi->defaultIsIndex) {
1516      fprintf(out, "%sdefault%s%d\n", indent, separator,      if (cfg->cfi->defaultIsVariable) {
1517      cfg->defaultImage);          fprintf(out, "%sset default=\"%d\"\n", indent,
1518     cfg->defaultImage);
1519        } else {
1520     fprintf(out, "%sdefault%s%d\n", indent, separator,
1521     cfg->defaultImage);
1522        }
1523   } else {   } else {
1524      int image = cfg->defaultImage;      int image = cfg->defaultImage;
1525    
# Line 769  static void writeDefault(FILE * out, cha Line 1536  static void writeDefault(FILE * out, cha
1536    
1537      if (!entry) return;      if (!entry) return;
1538    
1539      line = entry->lines;      line = getLineByType(LT_TITLE, entry->lines);
     while (line && line->type != LT_TITLE) line = line->next;  
1540    
1541      if (line && line->numElements >= 2)      if (line && line->numElements >= 2)
1542   fprintf(out, "%sdefault%s%s\n", indent, separator,   fprintf(out, "%sdefault%s%s\n", indent, separator,
# Line 778  static void writeDefault(FILE * out, cha Line 1544  static void writeDefault(FILE * out, cha
1544              else if (line && (line->numElements == 1) &&              else if (line && (line->numElements == 1) &&
1545                       cfg->cfi->titleBracketed) {                       cfg->cfi->titleBracketed) {
1546   fprintf(out, "%sdefault%s%s\n", indent, separator,   fprintf(out, "%sdefault%s%s\n", indent, separator,
1547                          extractTitle(line));                          extractTitle(cfg, line));
1548              }              }
1549   }   }
1550      }      }
# Line 804  static int writeConfig(struct grubConfig Line 1570  static int writeConfig(struct grubConfig
1570      int rc;      int rc;
1571    
1572      /* most likely the symlink is relative, so change our      /* most likely the symlink is relative, so change our
1573         directory to / */         directory to the dir of the symlink */
1574      rc = chdir("/");      char *dir = strdupa(outName);
1575        rc = chdir(dirname(dir));
1576      do {      do {
1577   buf = alloca(len + 1);   buf = alloca(len + 1);
1578   rc = readlink(outName, buf, len);   rc = readlink(basename(outName), buf, len);
1579   if (rc == len) len += 256;   if (rc == len) len += 256;
1580      } while (rc == len);      } while (rc == len);
1581            
# Line 843  static int writeConfig(struct grubConfig Line 1610  static int writeConfig(struct grubConfig
1610      }      }
1611    
1612      line = cfg->theLines;      line = cfg->theLines;
1613        struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1614      while (line) {      while (line) {
1615   if (line->type == LT_DEFAULT) {          if (line->type == LT_SET_VARIABLE && defaultKw &&
1616     line->numElements == 3 &&
1617     !strcmp(line->elements[1].item, defaultKw->key) &&
1618     !is_special_grub2_variable(line->elements[2].item)) {
1619        writeDefault(out, line->indent, line->elements[0].indent, cfg);
1620        needs &= ~MAIN_DEFAULT;
1621     } else if (line->type == LT_DEFAULT) {
1622      writeDefault(out, line->indent, line->elements[0].indent, cfg);      writeDefault(out, line->indent, line->elements[0].indent, cfg);
1623      needs &= ~MAIN_DEFAULT;      needs &= ~MAIN_DEFAULT;
1624   } else if (line->type == LT_FALLBACK) {   } else if (line->type == LT_FALLBACK) {
# Line 912  static int numEntries(struct grubConfig Line 1686  static int numEntries(struct grubConfig
1686      return i;      return i;
1687  }  }
1688    
1689  int suitableImage(struct singleEntry * entry, const char * bootPrefix,  static char *findDiskForRoot()
1690    int skipRemoved, int flags) {  {
1691      struct singleLine * line;      int fd;
1692      char * fullName;      char buf[65536];
1693      int i;      char *devname;
1694      struct stat sb, sb2;      char *chptr;
1695      char * dev;      int rc;
     char * end;  
     char * rootspec;  
1696    
1697      line = entry->lines;      if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1698      while (line && line->type != LT_KERNEL) line = line->next;          fprintf(stderr, "grubby: failed to open %s: %s\n",
1699                        _PATH_MOUNTED, strerror(errno));
1700      if (!line) return 0;          return NULL;
1701      if (skipRemoved && entry->skip) return 0;      }
1702      if (line->numElements < 2) return 0;  
1703        rc = read(fd, buf, sizeof(buf) - 1);
1704        if (rc <= 0) {
1705            fprintf(stderr, "grubby: failed to read %s: %s\n",
1706                    _PATH_MOUNTED, strerror(errno));
1707            close(fd);
1708            return NULL;
1709        }
1710        close(fd);
1711        buf[rc] = '\0';
1712        chptr = buf;
1713    
1714        char *foundanswer = NULL;
1715    
1716        while (chptr && chptr != buf+rc) {
1717            devname = chptr;
1718    
1719            /*
1720             * The first column of a mtab entry is the device, but if the entry is a
1721             * special device it won't start with /, so move on to the next line.
1722             */
1723            if (*devname != '/') {
1724                chptr = strchr(chptr, '\n');
1725                if (chptr)
1726                    chptr++;
1727                continue;
1728            }
1729    
1730            /* Seek to the next space */
1731            chptr = strchr(chptr, ' ');
1732            if (!chptr) {
1733                fprintf(stderr, "grubby: error parsing %s: %s\n",
1734                        _PATH_MOUNTED, strerror(errno));
1735                return NULL;
1736            }
1737    
1738            /*
1739             * The second column of a mtab entry is the mount point, we are looking
1740             * for '/' obviously.
1741             */
1742            if (*(++chptr) == '/' && *(++chptr) == ' ') {
1743                /* remember the last / entry in mtab */
1744               foundanswer = devname;
1745            }
1746    
1747            /* Next line */
1748            chptr = strchr(chptr, '\n');
1749            if (chptr)
1750                chptr++;
1751        }
1752    
1753        /* Return the last / entry found */
1754        if (foundanswer) {
1755            chptr = strchr(foundanswer, ' ');
1756            *chptr = '\0';
1757            return strdup(foundanswer);
1758        }
1759    
1760        return NULL;
1761    }
1762    
1763    void printEntry(struct singleEntry * entry, FILE *f) {
1764        int i;
1765        struct singleLine * line;
1766    
1767        for (line = entry->lines; line; line = line->next) {
1768     log_message(f, "DBG: %s", line->indent);
1769     for (i = 0; i < line->numElements; i++) {
1770        /* Need to handle this, because we strip the quotes from
1771         * menuentry when read it. */
1772        if (line->type == LT_MENUENTRY && i == 1) {
1773     if(!isquote(*line->elements[i].item))
1774        log_message(f, "\'%s\'", line->elements[i].item);
1775     else
1776        log_message(f, "%s", line->elements[i].item);
1777     log_message(f, "%s", line->elements[i].indent);
1778    
1779     continue;
1780        }
1781        
1782        log_message(f, "%s%s",
1783        line->elements[i].item, line->elements[i].indent);
1784     }
1785     log_message(f, "\n");
1786        }
1787    }
1788    
1789    void notSuitablePrintf(struct singleEntry * entry, int okay, const char *fmt, ...)
1790    {
1791        static int once;
1792        va_list argp, argq;
1793    
1794        va_start(argp, fmt);
1795    
1796        va_copy(argq, argp);
1797        if (!once) {
1798     log_time(NULL);
1799     log_message(NULL, "command line: %s\n", saved_command_line);
1800        }
1801        log_message(NULL, "DBG: Image entry %s: ", okay ? "succeeded" : "failed");
1802        log_vmessage(NULL, fmt, argq);
1803    
1804        printEntry(entry, NULL);
1805        va_end(argq);
1806    
1807        if (!debug) {
1808     once = 1;
1809         va_end(argp);
1810     return;
1811        }
1812    
1813        if (okay) {
1814     va_end(argp);
1815     return;
1816        }
1817    
1818        if (!once)
1819     log_message(stderr, "DBG: command line: %s\n", saved_command_line);
1820        once = 1;
1821        fprintf(stderr, "DBG: Image entry failed: ");
1822        vfprintf(stderr, fmt, argp);
1823        printEntry(entry, stderr);
1824        va_end(argp);
1825    }
1826    
1827    #define beginswith(s, c) ((s) && (s)[0] == (c))
1828    
1829    static int endswith(const char *s, char c)
1830    {
1831     int slen;
1832    
1833     if (!s || !s[0])
1834     return 0;
1835     slen = strlen(s) - 1;
1836    
1837     return s[slen] == c;
1838    }
1839    
1840    int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1841      int skipRemoved, int flags) {
1842        struct singleLine * line;
1843        char * fullName;
1844        int i;
1845        char * dev;
1846        char * rootspec;
1847        char * rootdev;
1848    
1849      if (flags & GRUBBY_BADIMAGE_OKAY) return 1;      if (skipRemoved && entry->skip) {
1850     notSuitablePrintf(entry, 0, "marked to skip\n");
1851     return 0;
1852        }
1853    
1854        line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
1855        if (!line) {
1856     notSuitablePrintf(entry, 0, "no line found\n");
1857     return 0;
1858        }
1859        if (line->numElements < 2) {
1860     notSuitablePrintf(entry, 0, "line has only %d elements\n",
1861        line->numElements);
1862     return 0;
1863        }
1864    
1865        if (flags & GRUBBY_BADIMAGE_OKAY) {
1866        notSuitablePrintf(entry, 1, "\n");
1867        return 1;
1868        }
1869    
1870      fullName = alloca(strlen(bootPrefix) +      fullName = alloca(strlen(bootPrefix) +
1871        strlen(line->elements[1].item) + 1);        strlen(line->elements[1].item) + 1);
1872      rootspec = getRootSpecifier(line->elements[1].item);      rootspec = getRootSpecifier(line->elements[1].item);
1873      sprintf(fullName, "%s%s", bootPrefix,      int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1874              line->elements[1].item + ((rootspec != NULL) ?      int hasslash = endswith(bootPrefix, '/') ||
1875                                        strlen(rootspec) : 0));       beginswith(line->elements[1].item + rootspec_offset, '/');
1876      if (access(fullName, R_OK)) return 0;      sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1877                line->elements[1].item + rootspec_offset);
1878        if (access(fullName, R_OK)) {
1879     notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
1880     return 0;
1881        }
1882      for (i = 2; i < line->numElements; i++)      for (i = 2; i < line->numElements; i++)
1883   if (!strncasecmp(line->elements[i].item, "root=", 5)) break;   if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1884      if (i < line->numElements) {      if (i < line->numElements) {
1885   dev = line->elements[i].item + 5;   dev = line->elements[i].item + 5;
1886      } else {      } else {
1887   /* look for a lilo style LT_ROOT line */   /* look for a lilo style LT_ROOT line */
1888   line = entry->lines;   line = getLineByType(LT_ROOT, entry->lines);
  while (line && line->type != LT_ROOT) line = line->next;  
1889    
1890   if (line && line->numElements >= 2) {   if (line && line->numElements >= 2) {
1891      dev = line->elements[1].item;      dev = line->elements[1].item;
1892   } else {   } else {
1893              int type;      /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1894      /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS */       * grub+multiboot uses LT_MBMODULE for the args, so check that too.
1895      line = entry->lines;       */
1896        line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
             type = ((entry->multiboot) ? LT_MBMODULE : LT_KERNELARGS);  
   
     while (line && line->type != type) line = line->next;  
1897    
1898              /* failed to find one */              /* failed to find one */
1899              if (!line) return 0;              if (!line) {
1900     notSuitablePrintf(entry, 0, "no line found\n");
1901     return 0;
1902                }
1903    
1904      for (i = 1; i < line->numElements; i++)      for (i = 1; i < line->numElements; i++)
1905          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1906      if (i < line->numElements)      if (i < line->numElements)
1907          dev = line->elements[i].item + 5;          dev = line->elements[i].item + 5;
1908      else {      else {
1909     notSuitablePrintf(entry, 0, "no root= entry found\n");
1910   /* it failed too...  can't find root= */   /* it failed too...  can't find root= */
1911          return 0;          return 0;
1912              }              }
1913   }   }
1914      }      }
1915    
1916      if (!strncmp(dev, "LABEL=", 6)) {      dev = getpathbyspec(dev);
1917   dev += 6;      if (!getpathbyspec(dev)) {
1918            notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n", dev);
1919   /* check which device has this label */          return 0;
1920   dev = get_spec_by_volume_label(dev, &i, &i);      } else
1921   if (!dev) return 0;   dev = getpathbyspec(dev);
1922    
1923        rootdev = findDiskForRoot();
1924        if (!rootdev) {
1925            notSuitablePrintf(entry, 0, "can't find root device\n");
1926     return 0;
1927      }      }
1928    
1929      if (*dev == '/') {      if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1930   if (stat(dev, &sb))          notSuitablePrintf(entry, 0, "uuid missing: rootdev %s, dev %s\n",
1931      return 0;   getuuidbydev(rootdev), getuuidbydev(dev));
1932      } else {          free(rootdev);
1933   sb.st_rdev = strtol(dev, &end, 16);          return 0;
1934   if (*end) return 0;      }
1935    
1936        if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1937            notSuitablePrintf(entry, 0, "uuid mismatch: rootdev %s, dev %s\n",
1938     getuuidbydev(rootdev), getuuidbydev(dev));
1939     free(rootdev);
1940            return 0;
1941      }      }
     stat("/", &sb2);  
1942    
1943      if (sb.st_rdev != sb2.st_dev) return 0;      free(rootdev);
1944        notSuitablePrintf(entry, 1, "\n");
1945    
1946      return 1;      return 1;
1947  }  }
# Line 1024  struct singleEntry * findEntryByPath(str Line 1975  struct singleEntry * findEntryByPath(str
1975   }   }
1976    
1977   indexVars[i + 1] = -1;   indexVars[i + 1] = -1;
1978    
1979   i = 0;   i = 0;
1980   if (index) {   if (index) {
1981      while (i < *index) i++;      while (i < *index) {
1982      if (indexVars[i] == -1) return NULL;   i++;
1983     if (indexVars[i] == -1) return NULL;
1984        }
1985   }   }
1986    
1987   entry = findEntryByIndex(config, indexVars[i]);   entry = findEntryByIndex(config, indexVars[i]);
1988   if (!entry) return NULL;   if (!entry) return NULL;
1989    
1990   line = entry->lines;   line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
  while (line && line->type != LT_KERNEL)  
     line = line->next;  
   
1991   if (!line) return NULL;   if (!line) return NULL;
1992    
1993   if (index) *index = indexVars[i];   if (index) *index = indexVars[i];
# Line 1075  struct singleEntry * findEntryByPath(str Line 2025  struct singleEntry * findEntryByPath(str
2025    
2026   if (!strncmp(kernel, "TITLE=", 6)) {   if (!strncmp(kernel, "TITLE=", 6)) {
2027      prefix = "";      prefix = "";
2028      checkType = LT_TITLE;      checkType = LT_TITLE|LT_MENUENTRY;
2029      kernel += 6;      kernel += 6;
2030   }   }
2031    
2032   while ((entry = findEntryByIndex(config, i))) {   for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
2033      line = entry->lines;      if (entry->skip) continue;
     while (line && line->type != checkType) line=line->next;  
2034    
2035        dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
2036    
2037      if (line && line->numElements >= 2 && !entry->skip) {      /* check all the lines matching checkType */
2038                  rootspec = getRootSpecifier(line->elements[1].item);      for (line = entry->lines; line; line = line->next) {
2039          if (!strcmp(line->elements[1].item  +   enum lineType_e ct = checkType;
2040                              ((rootspec != NULL) ? strlen(rootspec) : 0),   if (entry->multiboot && checkType == LT_KERNEL)
2041                              kernel + strlen(prefix)))      ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER|LT_KERNEL_16;
2042                      break;   else if (checkType & LT_KERNEL)
2043              }      ct = checkType | LT_KERNEL_EFI | LT_KERNEL_16;
2044                 line = getLineByType(ct, line);
2045              /* have to check multiboot lines too */   if (!line)
2046              if (entry->multiboot) {      break;  /* not found in this entry */
2047                  while (line && line->type != LT_MBMODULE) line = line->next;  
2048                  if (line && line->numElements >= 2 && !entry->skip) {   if (line && line->type != LT_MENUENTRY &&
2049                      rootspec = getRootSpecifier(line->elements[1].item);   line->numElements >= 2) {
2050                      if (!strcmp(line->elements[1].item  +      rootspec = getRootSpecifier(line->elements[1].item);
2051                                  ((rootspec != NULL) ? strlen(rootspec) : 0),      if (!strcmp(line->elements[1].item +
2052                                  kernel + strlen(prefix)))   ((rootspec != NULL) ? strlen(rootspec) : 0),
2053                          break;   kernel + strlen(prefix)))
2054                  }   break;
2055              }   }
2056     if(line->type == LT_MENUENTRY &&
2057     !strcmp(line->elements[1].item, kernel))
2058        break;
2059        }
2060    
2061      i++;      /* make sure this entry has a kernel identifier; this skips
2062         * non-Linux boot entries (could find netbsd etc, though, which is
2063         * unfortunate)
2064         */
2065        if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines))
2066     break; /* found 'im! */
2067   }   }
2068    
2069   if (index) *index = i;   if (index) *index = i;
2070      }      }
2071    
2072      if (!entry) return NULL;      return entry;
2073    }
2074    
2075      /* make sure this entry has a kernel identifier; this skips non-Linux  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
2076         boot entries (could find netbsd etc, though, which is unfortunate) */        int * index) {
2077      line = entry->lines;      struct singleEntry * entry;
2078      while (line && line->type != LT_KERNEL) line = line->next;      struct singleLine * line;
2079      if (!line) {      int i;
2080   if (!index) index = &i;      char * newtitle;
2081   (*index)++;  
2082   return findEntryByPath(config, kernel, prefix, index);      for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2083     if (index && i < *index)
2084        continue;
2085     line = getLineByType(LT_TITLE, entry->lines);
2086     if (!line)
2087        line = getLineByType(LT_MENUENTRY, entry->lines);
2088     if (!line)
2089        continue;
2090     newtitle = grub2ExtractTitle(line);
2091     if (!newtitle)
2092        continue;
2093     if (!strcmp(title, newtitle))
2094        break;
2095      }      }
2096    
2097        if (!entry)
2098     return NULL;
2099    
2100        if (index)
2101     *index = i;
2102      return entry;      return entry;
2103  }  }
2104    
# Line 1147  struct singleEntry * findTemplate(struct Line 2124  struct singleEntry * findTemplate(struct
2124      struct singleEntry * entry, * entry2;      struct singleEntry * entry, * entry2;
2125      int index;      int index;
2126    
2127      if (cfg->defaultImage > -1) {      if (cfg->cfi->defaultIsSaved) {
2128     if (cfg->cfi->getEnv) {
2129        char *defTitle = cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2130        if (defTitle) {
2131     int index = 0;
2132     if (isnumber(defTitle)) {
2133        index = atoi(defTitle);
2134        entry = findEntryByIndex(cfg, index);
2135     } else {
2136        entry = findEntryByTitle(cfg, defTitle, &index);
2137     }
2138     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2139        cfg->defaultImage = index;
2140        if (indexPtr)
2141     *indexPtr = index;
2142        return entry;
2143     }
2144        }
2145     }
2146        } else if (cfg->defaultImage > -1) {
2147   entry = findEntryByIndex(cfg, cfg->defaultImage);   entry = findEntryByIndex(cfg, cfg->defaultImage);
2148   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2149      if (indexPtr) *indexPtr = cfg->defaultImage;      if (indexPtr) *indexPtr = cfg->defaultImage;
# Line 1200  void markRemovedImage(struct grubConfig Line 2196  void markRemovedImage(struct grubConfig
2196        const char * prefix) {        const char * prefix) {
2197      struct singleEntry * entry;      struct singleEntry * entry;
2198    
2199      if (!image) return;      if (!image)
2200     return;
2201    
2202        /* check and see if we're removing the default image */
2203        if (isdigit(*image)) {
2204     entry = findEntryByPath(cfg, image, prefix, NULL);
2205     if(entry)
2206        entry->skip = 1;
2207     return;
2208        }
2209    
2210      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2211   entry->skip = 1;   entry->skip = 1;
# Line 1208  void markRemovedImage(struct grubConfig Line 2213  void markRemovedImage(struct grubConfig
2213    
2214  void setDefaultImage(struct grubConfig * config, int hasNew,  void setDefaultImage(struct grubConfig * config, int hasNew,
2215       const char * defaultKernelPath, int newIsDefault,       const char * defaultKernelPath, int newIsDefault,
2216       const char * prefix, int flags) {       const char * prefix, int flags, int index) {
2217      struct singleEntry * entry, * entry2, * newDefault;      struct singleEntry * entry, * entry2, * newDefault;
2218      int i, j;      int i, j;
2219    
2220      if (newIsDefault) {      if (newIsDefault) {
2221   config->defaultImage = 0;   config->defaultImage = 0;
2222   return;   return;
2223        } else if ((index >= 0) && config->cfi->defaultIsIndex) {
2224     if (findEntryByIndex(config, index))
2225        config->defaultImage = index;
2226     else
2227        config->defaultImage = -1;
2228     return;
2229      } else if (defaultKernelPath) {      } else if (defaultKernelPath) {
2230   i = 0;   i = 0;
2231   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
# Line 1227  void setDefaultImage(struct grubConfig * Line 2238  void setDefaultImage(struct grubConfig *
2238    
2239      /* defaultImage now points to what we'd like to use, but before any order      /* defaultImage now points to what we'd like to use, but before any order
2240         changes */         changes */
2241      if (config->defaultImage == DEFAULT_SAVED)      if ((config->defaultImage == DEFAULT_SAVED) ||
2242     (config->defaultImage == DEFAULT_SAVED_GRUB2))
2243        /* default is set to saved, we don't want to change it */        /* default is set to saved, we don't want to change it */
2244        return;        return;
2245    
# Line 1285  void displayEntry(struct singleEntry * e Line 2297  void displayEntry(struct singleEntry * e
2297      struct singleLine * line;      struct singleLine * line;
2298      char * root = NULL;      char * root = NULL;
2299      int i;      int i;
2300        int j;
     line = entry->lines;  
     while (line && line->type != LT_KERNEL) line = line->next;  
2301    
2302      printf("index=%d\n", index);      printf("index=%d\n", index);
2303    
2304      printf("kernel=%s\n", line->elements[1].item);      line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
2305        if (!line) {
2306            printf("non linux entry\n");
2307            return;
2308        }
2309    
2310        if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2311     printf("kernel=%s\n", line->elements[1].item);
2312        else
2313     printf("kernel=%s%s\n", prefix, line->elements[1].item);
2314    
2315      if (line->numElements >= 3) {      if (line->numElements >= 3) {
2316   printf("args=\"");   printf("args=\"");
# Line 1308  void displayEntry(struct singleEntry * e Line 2327  void displayEntry(struct singleEntry * e
2327   }   }
2328   printf("\"\n");   printf("\"\n");
2329      } else {      } else {
2330   line = entry->lines;   line = getLineByType(LT_KERNELARGS, entry->lines);
  while (line && line->type != LT_KERNELARGS) line=line->next;  
   
2331   if (line) {   if (line) {
2332      char * s;      char * s;
2333    
# Line 1334  void displayEntry(struct singleEntry * e Line 2351  void displayEntry(struct singleEntry * e
2351      }      }
2352    
2353      if (!root) {      if (!root) {
2354   line = entry->lines;   line = getLineByType(LT_ROOT, entry->lines);
  while (line && line->type != LT_ROOT) line = line->next;  
   
2355   if (line && line->numElements >= 2)   if (line && line->numElements >= 2)
2356      root=line->elements[1].item;      root=line->elements[1].item;
2357      }      }
# Line 1351  void displayEntry(struct singleEntry * e Line 2366  void displayEntry(struct singleEntry * e
2366   printf("root=%s\n", s);   printf("root=%s\n", s);
2367      }      }
2368    
2369      line = entry->lines;      line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
     while (line && line->type != LT_INITRD) line = line->next;  
2370    
2371      if (line && line->numElements >= 2) {      if (line && line->numElements >= 2) {
2372   printf("initrd=%s", prefix);   if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2373        printf("initrd=");
2374     else
2375        printf("initrd=%s", prefix);
2376    
2377   for (i = 1; i < line->numElements; i++)   for (i = 1; i < line->numElements; i++)
2378      printf("%s%s", line->elements[i].item, line->elements[i].indent);      printf("%s%s", line->elements[i].item, line->elements[i].indent);
2379   printf("\n");   printf("\n");
2380      }      }
2381    
2382        line = getLineByType(LT_TITLE, entry->lines);
2383        if (line) {
2384     printf("title=%s\n", line->elements[1].item);
2385        } else {
2386     char * title;
2387     line = getLineByType(LT_MENUENTRY, entry->lines);
2388     if (line) {
2389        title = grub2ExtractTitle(line);
2390        if (title)
2391     printf("title=%s\n", title);
2392     }
2393        }
2394    
2395        for (j = 0, line = entry->lines; line; line = line->next) {
2396     if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2397        if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2398     printf("mbmodule%d=", j);
2399        else
2400     printf("mbmodule%d=%s", j, prefix);
2401    
2402        for (i = 1; i < line->numElements; i++)
2403     printf("%s%s", line->elements[i].item, line->elements[i].indent);
2404        printf("\n");
2405        j++;
2406     }
2407        }
2408    }
2409    
2410    int isSuseSystem(void) {
2411        const char * path;
2412        const static char default_path[] = "/etc/SuSE-release";
2413    
2414        if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2415     path = default_path;
2416    
2417        if (!access(path, R_OK))
2418     return 1;
2419        return 0;
2420    }
2421    
2422    int isSuseGrubConf(const char * path) {
2423        FILE * grubConf;
2424        char * line = NULL;
2425        size_t len = 0, res = 0;
2426    
2427        grubConf = fopen(path, "r");
2428        if (!grubConf) {
2429            dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2430     return 0;
2431        }
2432    
2433        while ((res = getline(&line, &len, grubConf)) != -1) {
2434     if (!strncmp(line, "setup", 5)) {
2435        fclose(grubConf);
2436        free(line);
2437        return 1;
2438     }
2439        }
2440    
2441        dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2442          path);
2443    
2444        fclose(grubConf);
2445        free(line);
2446        return 0;
2447    }
2448    
2449    int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2450        FILE * grubConf;
2451        char * line = NULL;
2452        size_t res = 0, len = 0;
2453    
2454        if (!path) return 1;
2455        if (!lbaPtr) return 1;
2456    
2457        grubConf = fopen(path, "r");
2458        if (!grubConf) return 1;
2459    
2460        while ((res = getline(&line, &len, grubConf)) != -1) {
2461     if (line[res - 1] == '\n')
2462        line[res - 1] = '\0';
2463     else if (len > res)
2464        line[res] = '\0';
2465     else {
2466        line = realloc(line, res + 1);
2467        line[res] = '\0';
2468     }
2469    
2470     if (!strncmp(line, "setup", 5)) {
2471        if (strstr(line, "--force-lba")) {
2472            *lbaPtr = 1;
2473        } else {
2474            *lbaPtr = 0;
2475        }
2476        dbgPrintf("lba: %i\n", *lbaPtr);
2477        break;
2478     }
2479        }
2480    
2481        free(line);
2482        fclose(grubConf);
2483        return 0;
2484    }
2485    
2486    int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2487        FILE * grubConf;
2488        char * line = NULL;
2489        size_t res = 0, len = 0;
2490        char * lastParamPtr = NULL;
2491        char * secLastParamPtr = NULL;
2492        char installDeviceNumber = '\0';
2493        char * bounds = NULL;
2494    
2495        if (!path) return 1;
2496        if (!devicePtr) return 1;
2497    
2498        grubConf = fopen(path, "r");
2499        if (!grubConf) return 1;
2500    
2501        while ((res = getline(&line, &len, grubConf)) != -1) {
2502     if (strncmp(line, "setup", 5))
2503        continue;
2504    
2505     if (line[res - 1] == '\n')
2506        line[res - 1] = '\0';
2507     else if (len > res)
2508        line[res] = '\0';
2509     else {
2510        line = realloc(line, res + 1);
2511        line[res] = '\0';
2512     }
2513    
2514     lastParamPtr = bounds = line + res;
2515    
2516     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2517     while (!isspace(*lastParamPtr))
2518        lastParamPtr--;
2519     lastParamPtr++;
2520    
2521     secLastParamPtr = lastParamPtr - 2;
2522     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2523    
2524     if (lastParamPtr + 3 > bounds) {
2525        dbgPrintf("lastParamPtr going over boundary");
2526        fclose(grubConf);
2527        free(line);
2528        return 1;
2529     }
2530     if (!strncmp(lastParamPtr, "(hd", 3))
2531        lastParamPtr += 3;
2532     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2533    
2534     /*
2535     * Second last parameter will decide wether last parameter is
2536     * an IMAGE_DEVICE or INSTALL_DEVICE
2537     */
2538     while (!isspace(*secLastParamPtr))
2539        secLastParamPtr--;
2540     secLastParamPtr++;
2541    
2542     if (secLastParamPtr + 3 > bounds) {
2543        dbgPrintf("secLastParamPtr going over boundary");
2544        fclose(grubConf);
2545        free(line);
2546        return 1;
2547     }
2548     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2549     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2550        secLastParamPtr += 3;
2551        dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2552        installDeviceNumber = *secLastParamPtr;
2553     } else {
2554        installDeviceNumber = *lastParamPtr;
2555     }
2556    
2557     *devicePtr = malloc(6);
2558     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2559     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2560     fclose(grubConf);
2561     free(line);
2562     return 0;
2563        }
2564    
2565        free(line);
2566        fclose(grubConf);
2567        return 1;
2568    }
2569    
2570    int grubGetBootFromDeviceMap(const char * device,
2571         char ** bootPtr) {
2572        FILE * deviceMap;
2573        char * line = NULL;
2574        size_t res = 0, len = 0;
2575        char * devicePtr;
2576        char * bounds = NULL;
2577        const char * path;
2578        const static char default_path[] = "/boot/grub/device.map";
2579    
2580        if (!device) return 1;
2581        if (!bootPtr) return 1;
2582    
2583        if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2584     path = default_path;
2585    
2586        dbgPrintf("opening grub device.map file from: %s\n", path);
2587        deviceMap = fopen(path, "r");
2588        if (!deviceMap)
2589     return 1;
2590    
2591        while ((res = getline(&line, &len, deviceMap)) != -1) {
2592            if (!strncmp(line, "#", 1))
2593        continue;
2594    
2595     if (line[res - 1] == '\n')
2596        line[res - 1] = '\0';
2597     else if (len > res)
2598        line[res] = '\0';
2599     else {
2600        line = realloc(line, res + 1);
2601        line[res] = '\0';
2602     }
2603    
2604     devicePtr = line;
2605     bounds = line + res;
2606    
2607     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2608        devicePtr++;
2609     dbgPrintf("device: %s\n", devicePtr);
2610    
2611     if (!strncmp(devicePtr, device, strlen(device))) {
2612        devicePtr += strlen(device);
2613        while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2614            devicePtr++;
2615    
2616        *bootPtr = strdup(devicePtr);
2617        break;
2618     }
2619        }
2620    
2621        free(line);
2622        fclose(deviceMap);
2623        return 0;
2624    }
2625    
2626    int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2627        char * grubDevice;
2628    
2629        if (suseGrubConfGetInstallDevice(path, &grubDevice))
2630     dbgPrintf("error looking for grub installation device\n");
2631        else
2632     dbgPrintf("grubby installation device: %s\n", grubDevice);
2633    
2634        if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2635     dbgPrintf("error looking for grub boot device\n");
2636        else
2637     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2638    
2639        free(grubDevice);
2640        return 0;
2641    }
2642    
2643    int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2644        /*
2645         * This SuSE grub configuration file at this location is not your average
2646         * grub configuration file, but instead the grub commands used to setup
2647         * grub on that system.
2648         */
2649        const char * path;
2650        const static char default_path[] = "/etc/grub.conf";
2651    
2652        if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2653     path = default_path;
2654    
2655        if (!isSuseGrubConf(path)) return 1;
2656    
2657        if (lbaPtr) {
2658            *lbaPtr = 0;
2659            if (suseGrubConfGetLba(path, lbaPtr))
2660                return 1;
2661        }
2662    
2663        if (bootPtr) {
2664            *bootPtr = NULL;
2665            suseGrubConfGetBoot(path, bootPtr);
2666        }
2667    
2668        return 0;
2669  }  }
2670    
2671  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
# Line 1369  int parseSysconfigGrub(int * lbaPtr, cha Line 2675  int parseSysconfigGrub(int * lbaPtr, cha
2675      char * start;      char * start;
2676      char * param;      char * param;
2677    
2678      in = fopen("/etc/sysconfig/grub", "r");      in = fopen("/etc/conf.d/grub", "r");
2679      if (!in) return 1;      if (!in) return 1;
2680    
2681      if (lbaPtr) *lbaPtr = 0;      if (lbaPtr) *lbaPtr = 0;
# Line 1410  int parseSysconfigGrub(int * lbaPtr, cha Line 2716  int parseSysconfigGrub(int * lbaPtr, cha
2716  }  }
2717    
2718  void dumpSysconfigGrub(void) {  void dumpSysconfigGrub(void) {
2719      char * boot;      char * boot = NULL;
2720      int lba;      int lba;
2721    
2722      if (!parseSysconfigGrub(&lba, &boot)) {      if (isSuseSystem()) {
2723   if (lba) printf("lba\n");          if (parseSuseGrubConf(&lba, &boot)) {
2724   if (boot) printf("boot=%s\n", boot);      free(boot);
2725        return;
2726     }
2727        } else {
2728            if (parseSysconfigGrub(&lba, &boot)) {
2729        free(boot);
2730        return;
2731     }
2732        }
2733    
2734        if (lba) printf("lba\n");
2735        if (boot) {
2736     printf("boot=%s\n", boot);
2737     free(boot);
2738      }      }
2739  }  }
2740    
# Line 1431  int displayInfo(struct grubConfig * conf Line 2750  int displayInfo(struct grubConfig * conf
2750   return 1;   return 1;
2751      }      }
2752    
2753      /* this is a horrible hack to support /etc/sysconfig/grub; there must      /* this is a horrible hack to support /etc/conf.d/grub; there must
2754         be a better way */         be a better way */
2755      if (config->cfi == &grubConfigType) {      if (config->cfi == &grubConfigType) {
2756   dumpSysconfigGrub();   dumpSysconfigGrub();
2757      } else {      } else {
2758   line = config->theLines;   line = getLineByType(LT_BOOT, config->theLines);
  while (line && line->type != LT_BOOT) line = line->next;  
2759   if (line && line->numElements >= 1) {   if (line && line->numElements >= 1) {
2760      printf("boot=%s\n", line->elements[1].item);      printf("boot=%s\n", line->elements[1].item);
2761   }   }
2762    
2763   line = config->theLines;   line = getLineByType(LT_LBA, config->theLines);
  while (line && line->type != LT_LBA) line = line->next;  
2764   if (line) printf("lba\n");   if (line) printf("lba\n");
2765      }      }
2766    
# Line 1458  int displayInfo(struct grubConfig * conf Line 2775  int displayInfo(struct grubConfig * conf
2775      return 0;      return 0;
2776  }  }
2777    
2778    struct singleLine * addLineTmpl(struct singleEntry * entry,
2779     struct singleLine * tmplLine,
2780     struct singleLine * prevLine,
2781     const char * val,
2782     struct configFileInfo * cfi)
2783    {
2784        struct singleLine * newLine = lineDup(tmplLine);
2785    
2786        if (isEfi && cfi == &grub2ConfigType) {
2787     enum lineType_e old = newLine->type;
2788     newLine->type = preferredLineType(newLine->type, cfi);
2789     if (old != newLine->type)
2790        newLine->elements[0].item = getKeyByType(newLine->type, cfi);
2791        }
2792    
2793        if (val) {
2794     /* override the inherited value with our own.
2795     * This is a little weak because it only applies to elements[1]
2796     */
2797     if (newLine->numElements > 1)
2798        removeElement(newLine, 1);
2799     insertElement(newLine, val, 1, cfi);
2800    
2801     /* but try to keep the rootspec from the template... sigh */
2802     if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) {
2803        char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2804        if (rootspec != NULL) {
2805     free(newLine->elements[1].item);
2806     newLine->elements[1].item =
2807        sdupprintf("%s%s", rootspec, val);
2808        }
2809     }
2810        }
2811    
2812        dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2813          newLine->elements[0].item : "");
2814    
2815        if (!entry->lines) {
2816     /* first one on the list */
2817     entry->lines = newLine;
2818        } else if (prevLine) {
2819     /* add after prevLine */
2820     newLine->next = prevLine->next;
2821     prevLine->next = newLine;
2822        }
2823    
2824        return newLine;
2825    }
2826    
2827  /* val may be NULL */  /* val may be NULL */
2828  struct singleLine *  addLine(struct singleEntry * entry,  struct singleLine *  addLine(struct singleEntry * entry,
2829       struct configFileInfo * cfi,       struct configFileInfo * cfi,
2830       enum lineType_e type, const char * defaultIndent,       enum lineType_e type, char * defaultIndent,
2831       char * val) {       const char * val) {
2832      struct singleLine * line, * prev;      struct singleLine * line, * prev;
2833      int i;      struct keywordTypes * kw;
2834        struct singleLine tmpl;
2835    
2836      for (i = 0; cfi->keywords[i].key; i++)      /* NB: This function shouldn't allocate items on the heap, rather on the
2837   if (cfi->keywords[i].type == type) break;       * stack since it calls addLineTmpl which will make copies.
2838      if (type != LT_TITLE || !cfi->titleBracketed)       */
2839          if (!cfi->keywords[i].key) abort();      if (type == LT_TITLE && cfi->titleBracketed) {
2840     /* we're doing a bracketed title (zipl) */
2841     tmpl.type = type;
2842     tmpl.numElements = 1;
2843     tmpl.elements = alloca(sizeof(*tmpl.elements));
2844     tmpl.elements[0].item = alloca(strlen(val)+3);
2845     sprintf(tmpl.elements[0].item, "[%s]", val);
2846     tmpl.elements[0].indent = "";
2847     val = NULL;
2848        } else if (type == LT_MENUENTRY) {
2849     char *lineend = "--class gnu-linux --class gnu --class os {";
2850     if (!val) {
2851        fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2852        abort();
2853     }
2854     kw = getKeywordByType(type, cfi);
2855     if (!kw) {
2856        fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2857        abort();
2858     }
2859     tmpl.indent = "";
2860     tmpl.type = type;
2861     tmpl.numElements = 3;
2862     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2863     tmpl.elements[0].item = kw->key;
2864     tmpl.elements[0].indent = alloca(2);
2865     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2866     tmpl.elements[1].item = (char *)val;
2867     tmpl.elements[1].indent = alloca(2);
2868     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2869     tmpl.elements[2].item = alloca(strlen(lineend)+1);
2870     strcpy(tmpl.elements[2].item, lineend);
2871     tmpl.elements[2].indent = "";
2872        } else {
2873     kw = getKeywordByType(type, cfi);
2874     if (!kw) {
2875        fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2876        abort();
2877     }
2878     tmpl.type = type;
2879     tmpl.numElements = val ? 2 : 1;
2880     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2881     tmpl.elements[0].item = kw->key;
2882     tmpl.elements[0].indent = alloca(2);
2883     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2884     if (val) {
2885        tmpl.elements[1].item = (char *)val;
2886        tmpl.elements[1].indent = "";
2887     }
2888        }
2889    
2890      /* The last non-empty line gives us the indention to us and the line      /* The last non-empty line gives us the indention to us and the line
2891         to insert after. Note that comments are considered empty lines, which         to insert after. Note that comments are considered empty lines, which
2892         may not be ideal? If there are no lines or we are looking at the         may not be ideal? If there are no lines or we are looking at the
2893         first line, we use defaultIndent (the first line is normally indented         first line, we use defaultIndent (the first line is normally indented
2894         differently from the rest) */         differently from the rest) */
2895      if (entry->lines) {      for (line = entry->lines, prev = NULL; line; line = line->next) {
2896   line = entry->lines;   if (line->numElements) prev = line;
2897   prev = NULL;   /* fall back on the last line if prev isn't otherwise set */
2898   while (line) {   if (!line->next && !prev) prev = line;
     if (line->numElements) prev = line;  
     line = line->next;  
  }  
  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;  
   
  if (prev == entry->lines)  
     line->indent = strdup(defaultIndent);  
  else  
     line->indent = strdup(prev->indent);  
     } else {  
  line = malloc(sizeof(*line));  
  line->indent = strdup(defaultIndent);  
  line->next = NULL;  
2899      }      }
2900    
2901      if (type != LT_TITLE || !cfi->titleBracketed) {      struct singleLine *menuEntry;
2902          line->type = type;      menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2903          line->numElements = val ? 2 : 1;      if (tmpl.type == LT_ENTRY_END) {
2904          line->elements = malloc(sizeof(*line->elements) * line->numElements);   if (menuEntry)
2905          line->elements[0].item = strdup(cfi->keywords[i].key);      tmpl.indent = menuEntry->indent;
2906          line->elements[0].indent = malloc(2);   else
2907          line->elements[0].indent[0] = cfi->keywords[i].nextChar;      tmpl.indent = defaultIndent ?: "";
2908          line->elements[0].indent[1] = '\0';      } else if (tmpl.type != LT_MENUENTRY) {
2909             if (menuEntry)
2910          if (val) {      tmpl.indent = "\t";
2911              line->elements[1].item = val;   else if (prev == entry->lines)
2912              line->elements[1].indent = strdup("");      tmpl.indent = defaultIndent ?: "";
2913          }   else
2914      } else {      tmpl.indent = prev->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("");  
2915      }      }
2916    
2917      return line;      return addLineTmpl(entry, &tmpl, prev, val, cfi);
2918  }  }
2919    
2920  void removeLine(struct singleEntry * entry, struct singleLine * line) {  void removeLine(struct singleEntry * entry, struct singleLine * line) {
# Line 1553  void removeLine(struct singleEntry * ent Line 2939  void removeLine(struct singleEntry * ent
2939      free(line);      free(line);
2940  }  }
2941    
2942    static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2943    {
2944        struct singleLine newLine = {
2945     .indent = tmplLine->indent,
2946     .type = tmplLine->type,
2947     .next = tmplLine->next,
2948        };
2949        int firstQuotedItem = -1;
2950        int quoteLen = 0;
2951        int j;
2952        int element = 0;
2953        char *c;
2954    
2955        c = malloc(strlen(tmplLine->elements[0].item) + 1);
2956        strcpy(c, tmplLine->elements[0].item);
2957        insertElement(&newLine, c, element++, cfi);
2958        free(c);
2959        c = NULL;
2960    
2961        for (j = 1; j < tmplLine->numElements; j++) {
2962     if (firstQuotedItem == -1) {
2963        quoteLen += strlen(tmplLine->elements[j].item);
2964        
2965        if (isquote(tmplLine->elements[j].item[0])) {
2966     firstQuotedItem = j;
2967            quoteLen += strlen(tmplLine->elements[j].indent);
2968        } else {
2969     c = malloc(quoteLen + 1);
2970     strcpy(c, tmplLine->elements[j].item);
2971     insertElement(&newLine, c, element++, cfi);
2972     free(c);
2973     quoteLen = 0;
2974        }
2975     } else {
2976        int itemlen = strlen(tmplLine->elements[j].item);
2977        quoteLen += itemlen;
2978        quoteLen += strlen(tmplLine->elements[j].indent);
2979        
2980        if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2981     c = malloc(quoteLen + 1);
2982     c[0] = '\0';
2983     for (int i = firstQuotedItem; i < j+1; i++) {
2984        strcat(c, tmplLine->elements[i].item);
2985        strcat(c, tmplLine->elements[i].indent);
2986     }
2987     insertElement(&newLine, c, element++, cfi);
2988     free(c);
2989    
2990     firstQuotedItem = -1;
2991     quoteLen = 0;
2992        }
2993     }
2994        }
2995        while (tmplLine->numElements)
2996     removeElement(tmplLine, 0);
2997        if (tmplLine->elements)
2998     free(tmplLine->elements);
2999    
3000        tmplLine->numElements = newLine.numElements;
3001        tmplLine->elements = newLine.elements;
3002    }
3003    
3004    static void insertElement(struct singleLine * line,
3005      const char * item, int insertHere,
3006      struct configFileInfo * cfi)
3007    {
3008        struct keywordTypes * kw;
3009        char indent[2] = "";
3010    
3011        /* sanity check */
3012        if (insertHere > line->numElements) {
3013     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
3014      insertHere, line->numElements);
3015     insertHere = line->numElements;
3016        }
3017    
3018        line->elements = realloc(line->elements, (line->numElements + 1) *
3019         sizeof(*line->elements));
3020        memmove(&line->elements[insertHere+1],
3021        &line->elements[insertHere],
3022        (line->numElements - insertHere) *
3023        sizeof(*line->elements));
3024        line->elements[insertHere].item = strdup(item);
3025    
3026        kw = getKeywordByType(line->type, cfi);
3027    
3028        if (line->numElements == 0) {
3029     indent[0] = '\0';
3030        } else if (insertHere == 0) {
3031     indent[0] = kw->nextChar;
3032        } else if (kw->separatorChar != '\0') {
3033     indent[0] = kw->separatorChar;
3034        } else {
3035     indent[0] = ' ';
3036        }
3037    
3038        if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
3039     /* move the end-of-line forward */
3040     line->elements[insertHere].indent =
3041        line->elements[insertHere-1].indent;
3042     line->elements[insertHere-1].indent = strdup(indent);
3043        } else {
3044     line->elements[insertHere].indent = strdup(indent);
3045        }
3046    
3047        line->numElements++;
3048    
3049        dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3050          line->elements[0].item,
3051          line->elements[insertHere].item,
3052          line->elements[insertHere].indent,
3053          insertHere);
3054    }
3055    
3056    static void removeElement(struct singleLine * line, int removeHere) {
3057        int i;
3058    
3059        /* sanity check */
3060        if (removeHere >= line->numElements) return;
3061    
3062        dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3063          removeHere, line->elements[removeHere].item);
3064    
3065        free(line->elements[removeHere].item);
3066    
3067        if (removeHere > 1) {
3068     /* previous argument gets this argument's post-indentation */
3069     free(line->elements[removeHere-1].indent);
3070     line->elements[removeHere-1].indent =
3071        line->elements[removeHere].indent;
3072        } else {
3073     free(line->elements[removeHere].indent);
3074        }
3075    
3076        /* now collapse the array, but don't bother to realloc smaller */
3077        for (i = removeHere; i < line->numElements - 1; i++)
3078     line->elements[i] = line->elements[i + 1];
3079    
3080        line->numElements--;
3081    }
3082    
3083  int argMatch(const char * one, const char * two) {  int argMatch(const char * one, const char * two) {
3084      char * first, * second;      char * first, * second;
3085      char * chptr;      char * chptr;
# Line 1575  int updateActualImage(struct grubConfig Line 3102  int updateActualImage(struct grubConfig
3102      struct singleEntry * entry;      struct singleEntry * entry;
3103      struct singleLine * line, * rootLine;      struct singleLine * line, * rootLine;
3104      int index = 0;      int index = 0;
3105      int i, j, k;      int i, k;
3106      const char ** newArgs, ** oldArgs;      const char ** newArgs, ** oldArgs;
3107      const char ** arg;      const char ** arg;
3108      const char * chptr;      int useKernelArgs, useRoot;
     int useKernelArgs = 0;  
     int useRoot = 0;  
3109      int firstElement;      int firstElement;
3110      int *usedElements, *usedArgs;      int *usedElements;
3111        int doreplace;
3112    
3113      if (!image) return 0;      if (!image) return 0;
3114    
# Line 1609  int updateActualImage(struct grubConfig Line 3135  int updateActualImage(struct grubConfig
3135   }   }
3136      }      }
3137    
     for (i = 0; cfg->cfi->keywords[i].key; i++)  
  if (cfg->cfi->keywords[i].type == LT_KERNELARGS) break;  
3138    
3139      if (cfg->cfi->keywords[i].key)      useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3140   useKernelArgs = 1;       && (!multibootArgs || cfg->cfi->mbConcatArgs));
3141    
3142      for (i = 0; cfg->cfi->keywords[i].key; i++)      useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3143   if (cfg->cfi->keywords[i].type == LT_ROOT) break;         && !multibootArgs);
3144    
3145      if (cfg->cfi->keywords[i].key)      for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
  useRoot = 1;  
3146    
3147      k = 0;   if (multibootArgs && !entry->multiboot)
3148      for (arg = newArgs; *arg; arg++)      continue;
3149          k++;  
3150      usedArgs = calloc(k, sizeof(int));   /* Determine where to put the args.  If this config supports
3151     * LT_KERNELARGS, use that.  Otherwise use
3152     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3153     */
3154     if (useKernelArgs) {
3155        line = getLineByType(LT_KERNELARGS, entry->lines);
3156        if (!line) {
3157     /* no LT_KERNELARGS, need to add it */
3158     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3159           cfg->secondaryIndent, NULL);
3160        }
3161        firstElement = 1;
3162    
3163      while ((entry = findEntryByPath(cfg, image, prefix, &index))) {   } else if (multibootArgs) {
3164   index++;      line = getLineByType(LT_HYPER, entry->lines);
3165        if (!line) {
3166     /* a multiboot entry without LT_HYPER? */
3167     continue;
3168        }
3169        firstElement = 2;
3170    
3171   line = entry->lines;   } else {
3172   while (line && line->type != LT_KERNEL) line = line->next;      line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3173   if (!line) continue;      if (!line) {
3174   firstElement = 2;   /* no LT_KERNEL or LT_MBMODULE in this entry? */
3175     continue;
3176          if (entry->multiboot && !multibootArgs) {      }
3177              /* first mb module line is the real kernel */      firstElement = 2;
3178              while (line && line->type != LT_MBMODULE) line = line->next;   }
3179              firstElement = 2;  
3180          } else if (useKernelArgs) {   /* handle the elilo case which does:
3181      while (line && line->type != LT_KERNELARGS) line = line->next;   *   append="hypervisor args -- kernel args"
3182      firstElement = 1;   */
3183     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3184        /* this is a multiboot entry, make sure there's
3185         * -- on the args line
3186         */
3187        for (i = firstElement; i < line->numElements; i++) {
3188     if (!strcmp(line->elements[i].item, "--"))
3189        break;
3190        }
3191        if (i == line->numElements) {
3192     /* assume all existing args are kernel args,
3193     * prepend -- to make it official
3194     */
3195     insertElement(line, "--", firstElement, cfg->cfi);
3196     i = firstElement;
3197        }
3198        if (!multibootArgs) {
3199     /* kernel args start after the -- */
3200     firstElement = i + 1;
3201        }
3202     } else if (cfg->cfi->mbConcatArgs) {
3203        /* this is a non-multiboot entry, remove hyper args */
3204        for (i = firstElement; i < line->numElements; i++) {
3205     if (!strcmp(line->elements[i].item, "--"))
3206        break;
3207        }
3208        if (i < line->numElements) {
3209     /* remove args up to -- */
3210     while (strcmp(line->elements[firstElement].item, "--"))
3211        removeElement(line, firstElement);
3212     /* remove -- */
3213     removeElement(line, firstElement);
3214        }
3215   }   }
3216    
3217   if (!line && useKernelArgs) {          usedElements = calloc(line->numElements, sizeof(*usedElements));
     /* no append in there, need to add it */  
     line = addLine(entry, cfg->cfi, LT_KERNELARGS, NULL, NULL);  
  }  
3218    
3219          usedElements = calloc(line->numElements, sizeof(int));   for (k = 0, arg = newArgs; *arg; arg++, k++) {
3220    
3221          k = 0;      doreplace = 1;
  for (arg = newArgs; *arg; arg++) {  
             if (usedArgs[k]) {  
                 k++;  
                 continue;  
             }  
3222      for (i = firstElement; i < line->numElements; i++) {      for (i = firstElement; i < line->numElements; i++) {
3223     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3224        !strcmp(line->elements[i].item, "--"))
3225     {
3226        /* reached the end of hyper args, insert here */
3227        doreplace = 0;
3228        break;  
3229     }
3230                  if (usedElements[i])                  if (usedElements[i])
3231                      continue;                      continue;
3232   if (!argMatch(line->elements[i].item, *arg)) {   if (!argMatch(line->elements[i].item, *arg)) {
3233                      usedElements[i]=1;                      usedElements[i]=1;
                     usedArgs[k]=1;  
3234      break;      break;
3235                  }                  }
3236              }              }
     chptr = strchr(*arg, '=');  
3237    
3238      if (i < line->numElements) {      if (i < line->numElements && doreplace) {
3239   /* replace */   /* direct replacement */
3240   free(line->elements[i].item);   free(line->elements[i].item);
3241   line->elements[i].item = strdup(*arg);   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("");  
  }  
3242    
3243   free(rootLine->elements[1].item);      } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3244   rootLine->elements[1].item = strdup(chptr + 1);   /* root= replacement */
3245      } else {   rootLine = getLineByType(LT_ROOT, entry->lines);
3246   /* append */   if (rootLine) {
3247   line->elements = realloc(line->elements,      free(rootLine->elements[1].item);
3248   (line->numElements + 1) * sizeof(*line->elements));      rootLine->elements[1].item = strdup(*arg + 5);
  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(" ");  
3249   } else {   } else {
3250      /* First thing on this line; treat a bit differently. Note      rootLine = addLine(entry, cfg->cfi, LT_ROOT,
3251         this is only possible if we've added a LT_KERNELARGS         cfg->secondaryIndent, *arg + 5);
        entry */  
     line->elements[line->numElements].indent = strdup("");  
3252   }   }
3253        }
3254    
3255   line->numElements++;      else {
3256     /* insert/append */
3257     insertElement(line, *arg, i, cfg->cfi);
3258     usedElements = realloc(usedElements, line->numElements *
3259           sizeof(*usedElements));
3260     memmove(&usedElements[i + 1], &usedElements[i],
3261     line->numElements - i - 1);
3262     usedElements[i] = 1;
3263    
3264   /* if we updated a root= here even though there is a   /* if we updated a root= here even though there is a
3265     LT_ROOT available we need to remove the LT_ROOT entry     LT_ROOT available we need to remove the LT_ROOT entry
3266     (this will happen if we switch from a device to a label) */     (this will happen if we switch from a device to a label) */
3267   if (useRoot && !strncmp(*arg, "root=", 5)) {   if (useRoot && !strncmp(*arg, "root=", 5)) {
3268      rootLine = entry->lines;      rootLine = getLineByType(LT_ROOT, entry->lines);
3269      while (rootLine && rootLine->type != LT_ROOT)      if (rootLine)
  rootLine = rootLine->next;  
     if (rootLine) {  
3270   removeLine(entry, rootLine);   removeLine(entry, rootLine);
     }  
3271   }   }
3272      }      }
             k++;  
3273   }   }
3274    
3275          free(usedElements);          free(usedElements);
3276    
  /* no arguments to remove (i.e. no append line) */  
  if (!line) continue;  
   
  /* this won't remove an LT_ROOT item properly (but then again,  
    who cares? */  
3277   for (arg = oldArgs; *arg; arg++) {   for (arg = oldArgs; *arg; arg++) {
3278      for (i = firstElement; i < line->numElements; i++)      for (i = firstElement; i < line->numElements; i++) {
3279   if (!argMatch(line->elements[i].item, *arg))   if (multibootArgs && cfg->cfi->mbConcatArgs &&
3280        !strcmp(line->elements[i].item, "--"))
3281        /* reached the end of hyper args, stop here */
3282        break;
3283     if (!argMatch(line->elements[i].item, *arg)) {
3284        removeElement(line, i);
3285      break;      break;
   
     if (i < line->numElements) {  
  /* if this isn't the first argument the previous argument  
    gets this arguments post-indention */  
  if (i > firstElement) {  
     free(line->elements[i - 1].indent);  
     line->elements[i - 1].indent = line->elements[i].indent;  
3286   }   }
3287        }
3288   free(line->elements[i].item);      /* handle removing LT_ROOT line too */
3289        if (useRoot && !strncmp(*arg, "root=", 5)) {
3290   for (j = i + 1; j < line->numElements; j++)   rootLine = getLineByType(LT_ROOT, entry->lines);
3291      line->elements[j - 1] = line->elements[j];   if (rootLine)
3292        removeLine(entry, rootLine);
  line->numElements--;  
3293      }      }
3294   }   }
3295    
# Line 1760  int updateActualImage(struct grubConfig Line 3300  int updateActualImage(struct grubConfig
3300   }   }
3301      }      }
3302    
     free(usedArgs);  
3303      free(newArgs);      free(newArgs);
3304      free(oldArgs);      free(oldArgs);
3305    
# Line 1786  int updateImage(struct grubConfig * cfg, Line 3325  int updateImage(struct grubConfig * cfg,
3325      return rc;      return rc;
3326  }  }
3327    
3328    int addMBInitrd(struct grubConfig * cfg, const char *newMBKernel,
3329     const char * image, const char * prefix, const char * initrd,
3330     const char * title) {
3331        struct singleEntry * entry;
3332        struct singleLine * line, * kernelLine, *endLine = NULL;
3333        int index = 0;
3334    
3335        if (!image) return 0;
3336    
3337        for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3338            kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3339            if (!kernelLine) continue;
3340    
3341     /* if title is supplied, the entry's title must match it. */
3342     if (title) {
3343        char *linetitle;
3344    
3345        line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3346        if (!line)
3347     continue;
3348    
3349        linetitle = extractTitle(cfg, line);
3350        if (!linetitle)
3351     continue;
3352        if (strcmp(title, linetitle)) {
3353     free(linetitle);
3354     continue;
3355        }
3356        free(linetitle);
3357     }
3358    
3359            if (prefix) {
3360                int prefixLen = strlen(prefix);
3361                if (!strncmp(initrd, prefix, prefixLen))
3362                    initrd += prefixLen;
3363            }
3364     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3365     if (endLine)
3366        removeLine(entry, endLine);
3367            line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),
3368     kernelLine->indent, initrd);
3369            if (!line)
3370        return 1;
3371     if (endLine) {
3372        line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3373                if (!line)
3374     return 1;
3375     }
3376    
3377            break;
3378        }
3379    
3380        return 0;
3381    }
3382    
3383    int updateInitrd(struct grubConfig * cfg, const char * image,
3384                     const char * prefix, const char * initrd, const char * title) {
3385        struct singleEntry * entry;
3386        struct singleLine * line, * kernelLine, *endLine = NULL;
3387        int index = 0;
3388    
3389        if (!image) return 0;
3390    
3391        for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3392            kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3393            if (!kernelLine) continue;
3394    
3395     /* if title is supplied, the entry's title must match it. */
3396     if (title) {
3397        char *linetitle;
3398    
3399        line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);
3400        if (!line)
3401     continue;
3402    
3403        linetitle = extractTitle(cfg, line);
3404        if (!linetitle)
3405     continue;
3406        if (strcmp(title, linetitle)) {
3407     free(linetitle);
3408     continue;
3409        }
3410        free(linetitle);
3411     }
3412    
3413            line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
3414            if (line)
3415                removeLine(entry, line);
3416            if (prefix) {
3417                int prefixLen = strlen(prefix);
3418                if (!strncmp(initrd, prefix, prefixLen))
3419                    initrd += prefixLen;
3420            }
3421     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3422     if (endLine)
3423        removeLine(entry, endLine);
3424     enum lineType_e lt;
3425     switch(kernelLine->type) {
3426        case LT_KERNEL:
3427            lt = LT_INITRD;
3428     break;
3429        case LT_KERNEL_EFI:
3430            lt = LT_INITRD_EFI;
3431     break;
3432        case LT_KERNEL_16:
3433            lt = LT_INITRD_16;
3434     break;
3435        default:
3436            lt = preferredLineType(LT_INITRD, cfg->cfi);
3437     }
3438            line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3439            if (!line)
3440        return 1;
3441     if (endLine) {
3442        line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3443                if (!line)
3444     return 1;
3445     }
3446    
3447            break;
3448        }
3449    
3450        return 0;
3451    }
3452    
3453  int checkDeviceBootloader(const char * device, const unsigned char * boot) {  int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3454      int fd;      int fd;
3455      unsigned char bootSect[512];      unsigned char bootSect[512];
# Line 1809  int checkDeviceBootloader(const char * d Line 3473  int checkDeviceBootloader(const char * d
3473      if (memcmp(boot, bootSect, 3))      if (memcmp(boot, bootSect, 3))
3474   return 0;   return 0;
3475    
3476      if (boot[1] == 0xeb) {      if (boot[1] == JMP_SHORT_OPCODE) {
3477   offset = boot[2] + 2;   offset = boot[2] + 2;
3478      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3479   offset = (boot[3] << 8) + boot[2] + 2;   offset = (boot[3] << 8) + boot[2] + 2;
3480      } else if (boot[0] == 0xeb) {      } else if (boot[0] == JMP_SHORT_OPCODE) {
3481   offset = boot[1] + 2;        offset = boot[1] + 2;
3482            /*
3483     * it looks like grub, when copying stage1 into the mbr, patches stage1
3484     * right after the JMP location, replacing other instructions such as
3485     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3486     * different bytes.
3487     */
3488          if ((bootSect[offset + 1] == NOOP_OPCODE)
3489      && (bootSect[offset + 2] == NOOP_OPCODE)) {
3490     offset = offset + 3;
3491          }
3492      } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {      } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3493   offset = (boot[2] << 8) + boot[1] + 2;   offset = (boot[2] << 8) + boot[1] + 2;
3494      } else {      } else {
# Line 1956  int checkForLilo(struct grubConfig * con Line 3630  int checkForLilo(struct grubConfig * con
3630      return checkDeviceBootloader(line->elements[1].item, boot);      return checkDeviceBootloader(line->elements[1].item, boot);
3631  }  }
3632    
3633    int checkForGrub2(struct grubConfig * config) {
3634        if (!access("/etc/grub.d/", R_OK))
3635     return 2;
3636    
3637        return 1;
3638    }
3639    
3640  int checkForGrub(struct grubConfig * config) {  int checkForGrub(struct grubConfig * config) {
3641      int fd;      int fd;
3642      unsigned char bootSect[512];      unsigned char bootSect[512];
3643      char * boot;      char * boot;
3644        int onSuse = isSuseSystem();
3645    
3646      if (parseSysconfigGrub(NULL, &boot))  
3647   return 0;      if (onSuse) {
3648     if (parseSuseGrubConf(NULL, &boot))
3649        return 0;
3650        } else {
3651     if (parseSysconfigGrub(NULL, &boot))
3652        return 0;
3653        }
3654    
3655      /* assume grub is not installed -- not an error condition */      /* assume grub is not installed -- not an error condition */
3656      if (!boot)      if (!boot)
# Line 1976  int checkForGrub(struct grubConfig * con Line 3664  int checkForGrub(struct grubConfig * con
3664      if (read(fd, bootSect, 512) != 512) {      if (read(fd, bootSect, 512) != 512) {
3665   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3666   "/boot/grub/stage1", strerror(errno));   "/boot/grub/stage1", strerror(errno));
3667     close(fd);
3668     return 1;
3669        }
3670        close(fd);
3671    
3672        /* The more elaborate checks do not work on SuSE. The checks done
3673         * seem to be reasonble (at least for now), so just return success
3674         */
3675        if (onSuse)
3676     return 2;
3677    
3678        return checkDeviceBootloader(boot, bootSect);
3679    }
3680    
3681    int checkForExtLinux(struct grubConfig * config) {
3682        int fd;
3683        unsigned char bootSect[512];
3684        char * boot;
3685        char executable[] = "/boot/extlinux/extlinux";
3686    
3687        printf("entered: checkForExtLinux()\n");
3688    
3689        if (parseSysconfigGrub(NULL, &boot))
3690     return 0;
3691    
3692        /* assume grub is not installed -- not an error condition */
3693        if (!boot)
3694     return 0;
3695    
3696        fd = open(executable, O_RDONLY);
3697        if (fd < 0)
3698     /* this doesn't exist if grub hasn't been installed */
3699     return 0;
3700    
3701        if (read(fd, bootSect, 512) != 512) {
3702     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3703     executable, strerror(errno));
3704   return 1;   return 1;
3705      }      }
3706      close(fd);      close(fd);
# Line 1983  int checkForGrub(struct grubConfig * con Line 3708  int checkForGrub(struct grubConfig * con
3708      return checkDeviceBootloader(boot, bootSect);      return checkDeviceBootloader(boot, bootSect);
3709  }  }
3710    
3711    int checkForYaboot(struct grubConfig * config) {
3712        /*
3713         * This is a simplistic check that we consider good enough for own puporses
3714         *
3715         * If we were to properly check if yaboot is *installed* we'd need to:
3716         * 1) get the system boot device (LT_BOOT)
3717         * 2) considering it's a raw filesystem, check if the yaboot binary matches
3718         *    the content on the boot device
3719         * 3) if not, copy the binary to a temporary file and run "addnote" on it
3720         * 4) check again if binary and boot device contents match
3721         */
3722        if (!access("/etc/yaboot.conf", R_OK))
3723     return 2;
3724    
3725        return 1;
3726    }
3727    
3728    int checkForElilo(struct grubConfig * config) {
3729        if (!access("/etc/elilo.conf", R_OK))
3730     return 2;
3731    
3732        return 1;
3733    }
3734    
3735  static char * getRootSpecifier(char * str) {  static char * getRootSpecifier(char * str) {
3736      char * idx, * rootspec = NULL;      char * idx, * rootspec = NULL;
3737    
# Line 1994  static char * getRootSpecifier(char * st Line 3743  static char * getRootSpecifier(char * st
3743      return rootspec;      return rootspec;
3744  }  }
3745    
3746    static char * getInitrdVal(struct grubConfig * config,
3747       const char * prefix, struct singleLine *tmplLine,
3748       const char * newKernelInitrd,
3749       const char ** extraInitrds, int extraInitrdCount)
3750    {
3751        char *initrdVal, *end;
3752        int i;
3753        size_t totalSize;
3754        size_t prefixLen;
3755        char separatorChar;
3756    
3757        prefixLen = strlen(prefix);
3758        totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3759    
3760        for (i = 0; i < extraInitrdCount; i++) {
3761     totalSize += sizeof(separatorChar);
3762     totalSize += strlen(extraInitrds[i]) - prefixLen;
3763        }
3764    
3765        initrdVal = end = malloc(totalSize);
3766    
3767        end = stpcpy (end, newKernelInitrd + prefixLen);
3768    
3769        separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3770        for (i = 0; i < extraInitrdCount; i++) {
3771     const char *extraInitrd;
3772     int j;
3773    
3774     extraInitrd = extraInitrds[i] + prefixLen;
3775     /* Don't add entries that are already there */
3776     if (tmplLine != NULL) {
3777        for (j = 2; j < tmplLine->numElements; j++)
3778     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3779        break;
3780    
3781        if (j != tmplLine->numElements)
3782     continue;
3783     }
3784    
3785     *end++ = separatorChar;
3786     end = stpcpy(end, extraInitrd);
3787        }
3788    
3789        return initrdVal;
3790    }
3791    
3792  int addNewKernel(struct grubConfig * config, struct singleEntry * template,  int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3793           const char * prefix,           const char * prefix,
3794   char * newKernelPath, char * newKernelTitle,   const char * newKernelPath, const char * newKernelTitle,
3795   char * newKernelArgs, char * newKernelInitrd,   const char * newKernelArgs, const char * newKernelInitrd,
3796                   char * newMBKernel, char * newMBKernelArgs) {   const char ** extraInitrds, int extraInitrdCount,
3797                     const char * newMBKernel, const char * newMBKernelArgs,
3798     const char * newDevTreePath) {
3799      struct singleEntry * new;      struct singleEntry * new;
3800      struct singleLine * newLine = NULL, * tmplLine = NULL, * lastLine = NULL;      struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3801      int needs;      int needs;
     char * indent = NULL;  
     char * rootspec = NULL;  
3802      char * chptr;      char * chptr;
     int i;  
     enum lineType_e type;  
3803    
3804      if (!newKernelPath) return 0;      if (!newKernelPath) return 0;
3805    
# Line 2036  int addNewKernel(struct grubConfig * con Line 3829  int addNewKernel(struct grubConfig * con
3829      config->entries = new;      config->entries = new;
3830    
3831      /* copy/update from the template */      /* copy/update from the template */
3832      needs = KERNEL_KERNEL | KERNEL_INITRD | KERNEL_TITLE;      needs = NEED_KERNEL | NEED_TITLE;
3833        if (newKernelInitrd)
3834     needs |= NEED_INITRD;
3835      if (newMBKernel) {      if (newMBKernel) {
3836          needs |= KERNEL_MB;          needs |= NEED_MB;
3837          new->multiboot = 1;          new->multiboot = 1;
3838      }      }
3839        if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
3840     needs |= NEED_DEVTREE;
3841    
3842      if (template) {      if (template) {
3843   for (tmplLine = template->lines; tmplLine; tmplLine = tmplLine->next) {   for (masterLine = template->lines;
3844      /* remember the indention level; we may need it for new lines */       masterLine && (tmplLine = lineDup(masterLine));
3845      if (tmplLine->numElements)       lineFree(tmplLine), masterLine = masterLine->next)
3846   indent = tmplLine->indent;   {
3847        dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3848    
3849      /* skip comments */      /* skip comments */
3850      chptr = tmplLine->indent;      chptr = tmplLine->indent;
3851      while (*chptr && isspace(*chptr)) chptr++;      while (*chptr && isspace(*chptr)) chptr++;
3852      if (*chptr == '#') continue;      if (*chptr == '#') continue;
3853    
3854      /* we don't need an initrd here */      if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3855      if (tmplLine->type == LT_INITRD && !newKernelInitrd) continue;   if (!template->multiboot && (needs & NEED_MB)) {
3856        /* it's not a multiboot template and this is the kernel
3857              if (tmplLine->type == LT_KERNEL &&       * line.  Try to be intelligent about inserting the
3858                  !template->multiboot && (needs & KERNEL_MB)) {       * hypervisor at the same time.
3859                  struct singleLine *l;       */
3860                  needs &= ~ KERNEL_MB;      if (config->cfi->mbHyperFirst) {
3861     /* insert the hypervisor first */
3862                  l = addLine(new, config->cfi, LT_KERNEL,   newLine = addLine(new, config->cfi, LT_HYPER,
3863                                    config->secondaryIndent,    tmplLine->indent,
3864                                    newMBKernel + strlen(prefix));    newMBKernel + strlen(prefix));
3865                     /* set up for adding the kernel line */
3866                  tmplLine = lastLine;   free(tmplLine->indent);
3867                  if (!new->lines) {   tmplLine->indent = strdup(config->secondaryIndent);
3868                      new->lines = l;   needs &= ~NEED_MB;
3869                  } else {      }
3870                      newLine->next = l;      if (needs & NEED_KERNEL) {
3871                      newLine = l;   /* use addLineTmpl to preserve line elements,
3872                  }   * otherwise we could just call addLine.  Unfortunately
3873                  continue;   * this means making some changes to the template
3874              } else if (tmplLine->type == LT_KERNEL &&   * such as the indent change above and the type
3875                         template->multiboot && !new->multiboot) {   * change below.
3876                  continue; /* don't need multiboot kernel here */   */
3877              }   struct keywordTypes * mbm_kw =
3878        getKeywordByType(LT_MBMODULE, config->cfi);
3879      if (!new->lines) {   if (mbm_kw) {
3880   newLine = malloc(sizeof(*newLine));      tmplLine->type = LT_MBMODULE;
3881   new->lines = newLine;      free(tmplLine->elements[0].item);
3882      } else {      tmplLine->elements[0].item = strdup(mbm_kw->key);
3883   newLine->next = malloc(sizeof(*newLine));   }
3884   newLine = newLine->next;   newLine = addLineTmpl(new, tmplLine, newLine,
3885      }        newKernelPath + strlen(prefix), config->cfi);
3886     needs &= ~NEED_KERNEL;
3887        }
3888        if (needs & NEED_MB) { /* !mbHyperFirst */
3889     newLine = addLine(new, config->cfi, LT_HYPER,
3890      config->secondaryIndent,
3891      newMBKernel + strlen(prefix));
3892     needs &= ~NEED_MB;
3893        }
3894     } else if (needs & NEED_KERNEL) {
3895        newLine = addLineTmpl(new, tmplLine, newLine,
3896      newKernelPath + strlen(prefix), config->cfi);
3897        needs &= ~NEED_KERNEL;
3898     }
3899    
3900        } else if (tmplLine->type == LT_HYPER &&
3901           tmplLine->numElements >= 2) {
3902     if (needs & NEED_MB) {
3903        newLine = addLineTmpl(new, tmplLine, newLine,
3904      newMBKernel + strlen(prefix), config->cfi);
3905        needs &= ~NEED_MB;
3906     }
3907    
3908      newLine->indent = strdup(tmplLine->indent);      } else if (tmplLine->type == LT_MBMODULE &&
3909      newLine->next = NULL;         tmplLine->numElements >= 2) {
3910      newLine->type = tmplLine->type;   if (new->multiboot) {
3911      newLine->numElements = tmplLine->numElements;      if (needs & NEED_KERNEL) {
3912      newLine->elements = malloc(sizeof(*newLine->elements) *   newLine = addLineTmpl(new, tmplLine, newLine,
3913      newLine->numElements);        newKernelPath +
3914      for (i = 0; i < newLine->numElements; i++) {        strlen(prefix), config->cfi);
3915   newLine->elements[i].item = strdup(tmplLine->elements[i].item);   needs &= ~NEED_KERNEL;
3916   newLine->elements[i].indent =      } else if (config->cfi->mbInitRdIsModule &&
3917   strdup(tmplLine->elements[i].indent);         (needs & NEED_INITRD)) {
3918      }   char *initrdVal;
3919     initrdVal = getInitrdVal(config, prefix, tmplLine,
3920              lastLine = tmplLine;   newKernelInitrd, extraInitrds,
3921      if (tmplLine->type == LT_KERNEL && tmplLine->numElements >= 2) {   extraInitrdCount);
3922                  char * repl;   newLine = addLineTmpl(new, tmplLine, newLine,
3923                  if (!template->multiboot) {        initrdVal, config->cfi);
3924                      needs &= ~KERNEL_KERNEL;   free(initrdVal);
3925                      repl = newKernelPath;   needs &= ~NEED_INITRD;
3926                  } else {      }
3927                      needs &= ~KERNEL_MB;   } else if (needs & NEED_KERNEL) {
3928                      repl = newMBKernel;      /* template is multi but new is not,
3929                  }       * insert the kernel in the first module slot
3930                  if (new->multiboot && !template->multiboot) {       */
3931                      free(newLine->elements[0].item);      tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3932                      newLine->elements[0].item = strdup("module");      free(tmplLine->elements[0].item);
3933                      newLine->type = LT_MBMODULE;      tmplLine->elements[0].item =
3934                  }   strdup(getKeywordByType(tmplLine->type,
3935   free(newLine->elements[1].item);   config->cfi)->key);
3936                  rootspec = getRootSpecifier(tmplLine->elements[1].item);      newLine = addLineTmpl(new, tmplLine, newLine,
3937                  if (rootspec != NULL) {    newKernelPath + strlen(prefix),
3938                      newLine->elements[1].item = sdupprintf("%s%s",    config->cfi);
3939                                                             rootspec,      needs &= ~NEED_KERNEL;
3940                                                             repl +   } else if (needs & NEED_INITRD) {
3941                                                             strlen(prefix));      char *initrdVal;
3942                  } else {      /* template is multi but new is not,
3943                      newLine->elements[1].item = strdup(repl +       * insert the initrd in the second module slot
3944                                                         strlen(prefix));       */
3945                  }      tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3946              } else if (tmplLine->type == LT_MBMODULE &&      free(tmplLine->elements[0].item);
3947                         tmplLine->numElements >= 2 && (needs & KERNEL_KERNEL)) {      tmplLine->elements[0].item =
3948                  needs &= ~KERNEL_KERNEL;   strdup(getKeywordByType(tmplLine->type,
3949                  if (!new->multiboot && template->multiboot) {   config->cfi)->key);
3950                      free(newLine->elements[0].item);      initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3951                      newLine->elements[0].item = strdup("kernel");      newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3952                      newLine->type = LT_KERNEL;      free(initrdVal);
3953                  }      needs &= ~NEED_INITRD;
3954   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;  
3955    
3956   for (i = 1; i < newLine->numElements; i++) {      } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3957      free(newLine->elements[i].item);   if (needs & NEED_INITRD &&
3958      free(newLine->elements[i].indent);      new->multiboot && !template->multiboot &&
3959        config->cfi->mbInitRdIsModule) {
3960        /* make sure we don't insert the module initrd
3961         * before the module kernel... if we don't do it here,
3962         * it will be inserted following the template.
3963         */
3964        if (!needs & NEED_KERNEL) {
3965     char *initrdVal;
3966    
3967     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3968     newLine = addLine(new, config->cfi, LT_MBMODULE,
3969      config->secondaryIndent,
3970      initrdVal);
3971     free(initrdVal);
3972     needs &= ~NEED_INITRD;
3973        }
3974     } else if (needs & NEED_INITRD) {
3975        char *initrdVal;
3976        initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3977        newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3978        free(initrdVal);
3979        needs &= ~NEED_INITRD;
3980   }   }
3981    
3982   newLine->elements[1].item = strdup(newKernelTitle);      } else if (tmplLine->type == LT_MENUENTRY &&
3983   newLine->elements[1].indent = strdup("");         (needs & NEED_TITLE)) {
3984   newLine->numElements = 2;   requote(tmplLine, config->cfi);
3985     char *nkt = malloc(strlen(newKernelTitle)+3);
3986     strcpy(nkt, "'");
3987     strcat(nkt, newKernelTitle);
3988     strcat(nkt, "'");
3989     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3990     free(nkt);
3991     needs &= ~NEED_TITLE;
3992      } else if (tmplLine->type == LT_TITLE &&      } else if (tmplLine->type == LT_TITLE &&
3993                         config->cfi->titleBracketed &&         (needs & NEED_TITLE)) {
3994                         tmplLine->numElements == 1) {   if (tmplLine->numElements >= 2) {
3995                  needs &= ~KERNEL_TITLE;      newLine = addLineTmpl(new, tmplLine, newLine,
3996                  free(newLine->elements[0].item);    newKernelTitle, config->cfi);
3997                  free(newLine->elements[0].indent);      needs &= ~NEED_TITLE;
3998                  newLine->elements = malloc(sizeof(*newLine->elements) *   } else if (tmplLine->numElements == 1 &&
3999                                             newLine->numElements);     config->cfi->titleBracketed) {
4000        /* addLineTmpl doesn't handle titleBracketed */
4001                  newLine->elements[0].item = malloc(strlen(newKernelTitle) + 3);      newLine = addLine(new, config->cfi, LT_TITLE,
4002                  sprintf(newLine->elements[0].item, "[%s]", newKernelTitle);        tmplLine->indent, newKernelTitle);
4003                  newLine->elements[0].indent = strdup("");      needs &= ~NEED_TITLE;
4004                  newLine->numElements = 1;   }
4005              }      } else if (tmplLine->type == LT_ECHO) {
4006        requote(tmplLine, config->cfi);
4007        static const char *prefix = "'Loading ";
4008        if (tmplLine->numElements > 1 &&
4009        strstr(tmplLine->elements[1].item, prefix) &&
4010        masterLine->next &&
4011        iskernel(masterLine->next->type)) {
4012     char *newTitle = malloc(strlen(prefix) +
4013     strlen(newKernelTitle) + 2);
4014    
4015     strcpy(newTitle, prefix);
4016     strcat(newTitle, newKernelTitle);
4017     strcat(newTitle, "'");
4018     newLine = addLine(new, config->cfi, LT_ECHO,
4019     tmplLine->indent, newTitle);
4020     free(newTitle);
4021        } else {
4022     /* pass through other lines from the template */
4023     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
4024     config->cfi);
4025        }
4026        } else if (tmplLine->type == LT_DEVTREE &&
4027           tmplLine->numElements == 2 && newDevTreePath) {
4028            newLine = addLineTmpl(new, tmplLine, newLine,
4029          newDevTreePath + strlen(prefix),
4030          config->cfi);
4031     needs &= ~NEED_DEVTREE;
4032        } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {
4033     const char *ndtp = newDevTreePath;
4034     if (!strncmp(newDevTreePath, prefix, strlen(prefix)))
4035        ndtp += strlen(prefix);
4036     newLine = addLine(new, config->cfi, LT_DEVTREE,
4037      config->secondaryIndent,
4038      ndtp);
4039     needs &= ~NEED_DEVTREE;
4040     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4041        } else {
4042     /* pass through other lines from the template */
4043     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
4044        }
4045   }   }
4046    
4047      } else {      } else {
4048   for (i = 0; config->cfi->keywords[i].key; i++) {   /* don't have a template, so start the entry with the
4049      if ((config->cfi->keywords[i].type == config->cfi->entrySeparator) || (config->cfi->keywords[i].type == LT_OTHER))   * appropriate starting line
4050     */
4051     switch (config->cfi->entryStart) {
4052        case LT_KERNEL:
4053        case LT_KERNEL_EFI:
4054        case LT_KERNEL_16:
4055     if (new->multiboot && config->cfi->mbHyperFirst) {
4056        /* fall through to LT_HYPER */
4057     } else {
4058        newLine = addLine(new, config->cfi,
4059              preferredLineType(LT_KERNEL, config->cfi),
4060          config->primaryIndent,
4061          newKernelPath + strlen(prefix));
4062        needs &= ~NEED_KERNEL;
4063        break;
4064     }
4065    
4066        case LT_HYPER:
4067     newLine = addLine(new, config->cfi, LT_HYPER,
4068      config->primaryIndent,
4069      newMBKernel + strlen(prefix));
4070     needs &= ~NEED_MB;
4071   break;   break;
         }  
4072    
4073   switch (config->cfi->keywords[i].type) {      case LT_MENUENTRY: {
4074      case LT_KERNEL:  needs &= ~KERNEL_KERNEL,   char *nkt = malloc(strlen(newKernelTitle)+3);
4075       chptr = newKernelPath + strlen(prefix);   strcpy(nkt, "'");
4076       type = LT_KERNEL; break;   strcat(nkt, newKernelTitle);
4077      case LT_TITLE:   needs &= ~KERNEL_TITLE, chptr = newKernelTitle;   strcat(nkt, "'");
4078       type = LT_TITLE; break;          newLine = addLine(new, config->cfi, LT_MENUENTRY,
4079      default:        config->primaryIndent, nkt);
4080                  /* zipl strikes again */   free(nkt);
4081                  if (config->cfi->titleBracketed) {   needs &= ~NEED_TITLE;
4082                      needs &= ~KERNEL_TITLE;   needs |= NEED_END;
4083                      chptr = newKernelTitle;   break;
4084                      type = LT_TITLE;      }
4085                      break;      case LT_TITLE:
4086                  } else {   if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
4087                      abort();   char * templabel;
4088                  }   int x = 0, y = 0;
4089   }  
4090     templabel = strdup(newKernelTitle);
4091     while( templabel[x]){
4092     if( templabel[x] == ' ' ){
4093     y = x;
4094     while( templabel[y] ){
4095     templabel[y] = templabel[y+1];
4096     y++;
4097     }
4098     }
4099     x++;
4100     }
4101     newLine = addLine(new, config->cfi, LT_TITLE,
4102      config->primaryIndent, templabel);
4103     free(templabel);
4104     }else{
4105     newLine = addLine(new, config->cfi, LT_TITLE,
4106      config->primaryIndent, newKernelTitle);
4107     }
4108     needs &= ~NEED_TITLE;
4109     break;
4110    
4111   newLine = addLine(new, config->cfi, type, config->primaryIndent, chptr);      default:
4112   new->lines = newLine;   abort();
4113     }
4114      }      }
4115    
4116      if (new->multiboot) {      struct singleLine *endLine = NULL;
4117          if (needs & KERNEL_MB)      endLine = getLineByType(LT_ENTRY_END, new->lines);
4118              newLine = addLine(new, config->cfi, LT_KERNEL,      if (endLine) {
4119                                config->secondaryIndent,      removeLine(new, endLine);
4120                                newMBKernel + strlen(prefix));      needs |= NEED_END;
4121          if (needs & KERNEL_KERNEL)      }
4122              newLine = addLine(new, config->cfi, LT_MBMODULE,  
4123                                config->secondaryIndent,      /* add the remainder of the lines, i.e. those that either
4124                                newKernelPath + strlen(prefix));       * weren't present in the template, or in the case of no template,
4125          /* don't need to check for title as it's guaranteed to have been       * all the lines following the entryStart.
4126           * done as we only do multiboot with grub which uses title as       */
4127           * a separator */      if (needs & NEED_TITLE) {
4128          if (needs & KERNEL_INITRD && newKernelInitrd)   newLine = addLine(new, config->cfi, LT_TITLE,
4129              newLine = addLine(new, config->cfi, LT_MBMODULE,    config->secondaryIndent,
4130                                config->secondaryIndent,    newKernelTitle);
4131                                newKernelInitrd + strlen(prefix));   needs &= ~NEED_TITLE;
4132      } else {      }
4133          if (needs & KERNEL_KERNEL)      if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4134              newLine = addLine(new, config->cfi, LT_KERNEL,   newLine = addLine(new, config->cfi, LT_HYPER,
4135                                config->secondaryIndent,    config->secondaryIndent,
4136                                newKernelPath + strlen(prefix));    newMBKernel + strlen(prefix));
4137          if (needs & KERNEL_TITLE)   needs &= ~NEED_MB;
4138              newLine = addLine(new, config->cfi, LT_TITLE,      }
4139                                config->secondaryIndent,      if (needs & NEED_KERNEL) {
4140                                newKernelTitle);   newLine = addLine(new, config->cfi,
4141          if (needs & KERNEL_INITRD && newKernelInitrd)    (new->multiboot && getKeywordByType(LT_MBMODULE,
4142              newLine = addLine(new, config->cfi, LT_INITRD,        config->cfi))
4143                                config->secondaryIndent,     ? LT_MBMODULE
4144                                newKernelInitrd + strlen(prefix));   : preferredLineType(LT_KERNEL, config->cfi),
4145      config->secondaryIndent,
4146      newKernelPath + strlen(prefix));
4147     needs &= ~NEED_KERNEL;
4148        }
4149        if (needs & NEED_MB) {
4150     newLine = addLine(new, config->cfi, LT_HYPER,
4151      config->secondaryIndent,
4152      newMBKernel + strlen(prefix));
4153     needs &= ~NEED_MB;
4154        }
4155        if (needs & NEED_INITRD) {
4156     char *initrdVal;
4157     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
4158     newLine = addLine(new, config->cfi,
4159      (new->multiboot && getKeywordByType(LT_MBMODULE,
4160          config->cfi))
4161       ? LT_MBMODULE
4162       : preferredLineType(LT_INITRD, config->cfi),
4163      config->secondaryIndent,
4164      initrdVal);
4165     free(initrdVal);
4166     needs &= ~NEED_INITRD;
4167        }
4168        if (needs & NEED_DEVTREE) {
4169     newLine = addLine(new, config->cfi, LT_DEVTREE,
4170      config->secondaryIndent,
4171      newDevTreePath);
4172     needs &= ~NEED_DEVTREE;
4173        }
4174    
4175        /* NEEDS_END must be last on bootloaders that need it... */
4176        if (needs & NEED_END) {
4177     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4178     config->secondaryIndent, NULL);
4179     needs &= ~NEED_END;
4180        }
4181    
4182        if (needs) {
4183     printf(_("grubby: needs=%d, aborting\n"), needs);
4184     abort();
4185      }      }
4186    
4187      if (updateImage(config, "0", prefix, newKernelArgs, NULL,      if (updateImage(config, "0", prefix, newKernelArgs, NULL,
# Line 2274  int addNewKernel(struct grubConfig * con Line 4190  int addNewKernel(struct grubConfig * con
4190      return 0;      return 0;
4191  }  }
4192    
4193    static void traceback(int signum)
4194    {
4195        void *array[40];
4196        size_t size;
4197    
4198        signal(SIGSEGV, SIG_DFL);
4199        memset(array, '\0', sizeof (array));
4200        size = backtrace(array, 40);
4201    
4202        fprintf(stderr, "grubby received SIGSEGV!  Backtrace (%ld):\n",
4203                (unsigned long)size);
4204        backtrace_symbols_fd(array, size, STDERR_FILENO);
4205        exit(1);
4206    }
4207    
4208  int main(int argc, const char ** argv) {  int main(int argc, const char ** argv) {
4209      poptContext optCon;      poptContext optCon;
4210      char * grubConfig = NULL;      const char * grubConfig = NULL;
4211      char * outputFile = NULL;      char * outputFile = NULL;
4212      int arg = 0;      int arg = 0;
4213      int flags = 0;      int flags = 0;
4214      int badImageOkay = 0;      int badImageOkay = 0;
4215        int configureGrub2 = 0;
4216      int configureLilo = 0, configureELilo = 0, configureGrub = 0;      int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4217      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4218        int configureExtLinux = 0;
4219      int bootloaderProbe = 0;      int bootloaderProbe = 0;
4220        int extraInitrdCount = 0;
4221      char * updateKernelPath = NULL;      char * updateKernelPath = NULL;
4222      char * newKernelPath = NULL;      char * newKernelPath = NULL;
4223      char * removeKernelPath = NULL;      char * removeKernelPath = NULL;
4224      char * newKernelArgs = NULL;      char * newKernelArgs = NULL;
4225      char * newKernelInitrd = NULL;      char * newKernelInitrd = NULL;
4226      char * newKernelTitle = NULL;      char * newKernelTitle = NULL;
4227      char * newKernelVersion = NULL;      char * newDevTreePath = NULL;
4228      char * newMBKernel = NULL;      char * newMBKernel = NULL;
4229      char * newMBKernelArgs = NULL;      char * newMBKernelArgs = NULL;
4230      char * removeMBKernelArgs = NULL;      char * removeMBKernelArgs = NULL;
# Line 2299  int main(int argc, const char ** argv) { Line 4233  int main(int argc, const char ** argv) {
4233      char * defaultKernel = NULL;      char * defaultKernel = NULL;
4234      char * removeArgs = NULL;      char * removeArgs = NULL;
4235      char * kernelInfo = NULL;      char * kernelInfo = NULL;
4236        char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4237        char * envPath = NULL;
4238      const char * chptr = NULL;      const char * chptr = NULL;
4239      struct configFileInfo * cfi = NULL;      struct configFileInfo * cfi = NULL;
4240      struct grubConfig * config;      struct grubConfig * config;
4241      struct singleEntry * template = NULL;      struct singleEntry * template = NULL;
4242      int copyDefault = 0, makeDefault = 0;      int copyDefault = 0, makeDefault = 0;
4243      int displayDefault = 0;      int displayDefault = 0;
4244        int displayDefaultIndex = 0;
4245        int displayDefaultTitle = 0;
4246        int defaultIndex = -1;
4247      struct poptOption options[] = {      struct poptOption options[] = {
4248   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4249      _("add an entry for the specified kernel"), _("kernel-path") },      _("add an entry for the specified kernel"), _("kernel-path") },
# Line 2322  int main(int argc, const char ** argv) { Line 4261  int main(int argc, const char ** argv) {
4261   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4262      _("filestystem which contains /boot directory (for testing only)"),      _("filestystem which contains /boot directory (for testing only)"),
4263      _("bootfs") },      _("bootfs") },
4264  #if defined(__i386__) || defined(__x86_64__)  #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4265   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4266      _("check if lilo is installed on lilo.conf boot sector") },      _("check which bootloader is installed on boot sector") },
4267  #endif  #endif
4268   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4269      _("path to grub config file to update (\"-\" for stdin)"),      _("path to grub config file to update (\"-\" for stdin)"),
# Line 2335  int main(int argc, const char ** argv) { Line 4274  int main(int argc, const char ** argv) {
4274        "the kernel referenced by the default image does not exist, "        "the kernel referenced by the default image does not exist, "
4275        "the first linux entry whose kernel does exist is used as the "        "the first linux entry whose kernel does exist is used as the "
4276        "template"), NULL },        "template"), NULL },
4277     { "debug", 0, 0, &debug, 0,
4278        _("print debugging information for failures") },
4279   { "default-kernel", 0, 0, &displayDefault, 0,   { "default-kernel", 0, 0, &displayDefault, 0,
4280      _("display the path of the default kernel") },      _("display the path of the default kernel") },
4281     { "default-index", 0, 0, &displayDefaultIndex, 0,
4282        _("display the index of the default kernel") },
4283     { "default-title", 0, 0, &displayDefaultTitle, 0,
4284        _("display the title of the default kernel") },
4285     { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4286        _("device tree file for new stanza"), _("dtb-path") },
4287     { "devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4288        _("device tree directory for new stanza"), _("dtb-path") },
4289   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4290      _("configure elilo bootloader") },      _("configure elilo bootloader") },
4291     { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4292        _("force grub2 stanzas to use efi") },
4293     { "env", 0, POPT_ARG_STRING, &envPath, 0,
4294        _("path for environment data"),
4295        _("path") },
4296     { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4297        _("configure extlinux bootloader (from syslinux)") },
4298   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4299      _("configure grub bootloader") },      _("configure grub bootloader") },
4300     { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4301        _("configure grub2 bootloader") },
4302   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4303      _("display boot information for specified kernel"),      _("display boot information for specified kernel"),
4304      _("kernel-path") },      _("kernel-path") },
4305   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4306      _("initrd image for the new kernel"), _("initrd-path") },      _("initrd image for the new kernel"), _("initrd-path") },
4307     { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4308        _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4309   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4310      _("configure lilo bootloader") },      _("configure lilo bootloader") },
4311   { "make-default", 0, 0, &makeDefault, 0,   { "make-default", 0, 0, &makeDefault, 0,
# Line 2365  int main(int argc, const char ** argv) { Line 4325  int main(int argc, const char ** argv) {
4325   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4326      _("make the first entry referencing the specified kernel "      _("make the first entry referencing the specified kernel "
4327        "the default"), _("kernel-path") },        "the default"), _("kernel-path") },
4328     { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4329        _("make the given entry index the default entry"),
4330        _("entry-index") },
4331   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4332      _("configure silo bootloader") },      _("configure silo bootloader") },
4333   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
# Line 2382  int main(int argc, const char ** argv) { Line 4345  int main(int argc, const char ** argv) {
4345   { 0, 0, 0, 0, 0 }   { 0, 0, 0, 0, 0 }
4346      };      };
4347    
4348        useextlinuxmenu=0;
4349    
4350        signal(SIGSEGV, traceback);
4351    
4352        int i = 0;
4353        for (int j = 1; j < argc; j++)
4354     i += strlen(argv[j]) + 1;
4355        saved_command_line = malloc(i);
4356        if (!saved_command_line) {
4357     fprintf(stderr, "grubby: %m\n");
4358     exit(1);
4359        }
4360        saved_command_line[0] = '\0';
4361        for (int j = 1; j < argc; j++) {
4362     strcat(saved_command_line, argv[j]);
4363     strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4364        }
4365    
4366      optCon = poptGetContext("grubby", argc, argv, options, 0);      optCon = poptGetContext("grubby", argc, argv, options, 0);
4367      poptReadDefaultConfig(optCon, 1);      poptReadDefaultConfig(optCon, 1);
4368    
# Line 2391  int main(int argc, const char ** argv) { Line 4372  int main(int argc, const char ** argv) {
4372      printf("grubby version %s\n", VERSION);      printf("grubby version %s\n", VERSION);
4373      exit(0);      exit(0);
4374      break;      break;
4375      case 'i':
4376        if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4377         extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4378        } else {
4379     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4380     return 1;
4381        }
4382        break;
4383   }   }
4384      }      }
4385    
# Line 2406  int main(int argc, const char ** argv) { Line 4395  int main(int argc, const char ** argv) {
4395   return 1;   return 1;
4396      }      }
4397    
4398      if ((configureLilo + configureGrub + configureELilo +      if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4399   configureYaboot + configureSilo + configureZipl) > 1) {   configureYaboot + configureSilo + configureZipl +
4400     configureExtLinux ) > 1) {
4401   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4402   return 1;   return 1;
4403      } else if (bootloaderProbe && grubConfig) {      } else if (bootloaderProbe && grubConfig) {
4404   fprintf(stderr,   fprintf(stderr,
4405      _("grubby: cannot specify config file with --bootloader-probe\n"));      _("grubby: cannot specify config file with --bootloader-probe\n"));
4406   return 1;   return 1;
4407        } else if (configureGrub2) {
4408     cfi = &grub2ConfigType;
4409     if (envPath)
4410        cfi->envFile = envPath;
4411      } else if (configureLilo) {      } else if (configureLilo) {
4412   cfi = &liloConfigType;   cfi = &liloConfigType;
4413      } else if (configureGrub) {      } else if (configureGrub) {
# Line 2426  int main(int argc, const char ** argv) { Line 4420  int main(int argc, const char ** argv) {
4420          cfi = &siloConfigType;          cfi = &siloConfigType;
4421      } else if (configureZipl) {      } else if (configureZipl) {
4422          cfi = &ziplConfigType;          cfi = &ziplConfigType;
4423        } else if (configureExtLinux) {
4424     cfi = &extlinuxConfigType;
4425     useextlinuxmenu=1;
4426      }      }
4427    
4428      if (!cfi) {      if (!cfi) {
4429            if (grub2FindConfig(&grub2ConfigType))
4430        cfi = &grub2ConfigType;
4431     else
4432        #ifdef __ia64__        #ifdef __ia64__
4433   cfi = &eliloConfigType;      cfi = &eliloConfigType;
4434        #elif __powerpc__        #elif __powerpc__
4435   cfi = &yabootConfigType;      cfi = &yabootConfigType;
4436        #elif __sparc__        #elif __sparc__
4437          cfi = &siloConfigType;              cfi = &siloConfigType;
4438        #elif __s390__        #elif __s390__
4439          cfi = &ziplConfigType;              cfi = &ziplConfigType;
4440        #elif __s390x__        #elif __s390x__
4441          cfi = &ziplConfigtype;              cfi = &ziplConfigtype;
4442        #else        #else
4443   cfi = &grubConfigType;      cfi = &grubConfigType;
4444        #endif        #endif
4445      }      }
4446    
4447      if (!grubConfig)      if (!grubConfig) {
4448   grubConfig = cfi->defaultConfig;   if (cfi->findConfig)
4449        grubConfig = cfi->findConfig(cfi);
4450     if (!grubConfig)
4451        grubConfig = cfi->defaultConfig;
4452        }
4453    
4454      if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||      if (bootloaderProbe && (displayDefault || kernelInfo ||
4455    newKernelPath || removeKernelPath || makeDefault ||      newKernelPath || removeKernelPath || makeDefault ||
4456    defaultKernel)) {      defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4457        (defaultIndex >= 0))) {
4458   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4459    "specified option"));    "specified option"));
4460   return 1;   return 1;
4461      }      }
4462    
4463      if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||      if ((displayDefault || kernelInfo) && (newKernelPath ||
4464     removeKernelPath)) {     removeKernelPath)) {
4465   fprintf(stderr, _("grubby: --default-kernel and --info may not "   fprintf(stderr, _("grubby: --default-kernel and --info may not "
4466    "be used when adding or removing kernels\n"));    "be used when adding or removing kernels\n"));
# Line 2465  int main(int argc, const char ** argv) { Line 4470  int main(int argc, const char ** argv) {
4470      if (newKernelPath && !newKernelTitle) {      if (newKernelPath && !newKernelTitle) {
4471   fprintf(stderr, _("grubby: kernel title must be specified\n"));   fprintf(stderr, _("grubby: kernel title must be specified\n"));
4472   return 1;   return 1;
4473      } else if (!newKernelPath && (newKernelTitle  || newKernelInitrd ||      } else if (!newKernelPath && (copyDefault ||
4474    newKernelInitrd || copyDefault     ||    (newKernelInitrd && !updateKernelPath)||
4475    makeDefault)) {    makeDefault || extraInitrdCount > 0)) {
4476   fprintf(stderr, _("grubby: kernel path expected\n"));   fprintf(stderr, _("grubby: kernel path expected\n"));
4477   return 1;   return 1;
4478      }      }
# Line 2491  int main(int argc, const char ** argv) { Line 4496  int main(int argc, const char ** argv) {
4496   makeDefault = 1;   makeDefault = 1;
4497   defaultKernel = NULL;   defaultKernel = NULL;
4498      }      }
4499        else if (defaultKernel && (defaultIndex >= 0)) {
4500     fprintf(stderr, _("grubby: --set-default and --set-default-index "
4501      "may not be used together\n"));
4502     return 1;
4503        }
4504    
4505      if (!strcmp(grubConfig, "-") && !outputFile) {      if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4506   fprintf(stderr, _("grubby: output file must be specified if stdin "   fprintf(stderr, _("grubby: output file must be specified if stdin "
4507   "is used\n"));   "is used\n"));
4508   return 1;   return 1;
4509      }      }
4510    
4511      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4512   && !kernelInfo && !bootloaderProbe && !updateKernelPath   && !kernelInfo && !bootloaderProbe && !updateKernelPath
4513          && !removeMBKernel) {   && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4514     && (defaultIndex == -1)) {
4515   fprintf(stderr, _("grubby: no action specified\n"));   fprintf(stderr, _("grubby: no action specified\n"));
4516   return 1;   return 1;
4517      }      }
# Line 2520  int main(int argc, const char ** argv) { Line 4531  int main(int argc, const char ** argv) {
4531   bootPrefix = "";   bootPrefix = "";
4532      }      }
4533    
4534        if (!cfi->mbAllowExtraInitRds &&
4535     extraInitrdCount > 0) {
4536     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4537     return 1;
4538        }
4539    
4540      if (bootloaderProbe) {      if (bootloaderProbe) {
4541   int lrc = 0, grc = 0;   int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4542   struct grubConfig * lconfig, * gconfig;   struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4543    
4544     const char *grub2config = grub2FindConfig(&grub2ConfigType);
4545     if (grub2config) {
4546        gconfig = readConfig(grub2config, &grub2ConfigType);
4547        if (!gconfig)
4548     gr2c = 1;
4549        else
4550     gr2c = checkForGrub2(gconfig);
4551     }
4552    
4553   if (!access(grubConfigType.defaultConfig, F_OK)) {   const char *grubconfig = grubFindConfig(&grubConfigType);
4554      gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType);   if (!access(grubconfig, F_OK)) {
4555        gconfig = readConfig(grubconfig, &grubConfigType);
4556      if (!gconfig)      if (!gconfig)
4557   grc = 1;   grc = 1;
4558      else      else
# Line 2540  int main(int argc, const char ** argv) { Line 4567  int main(int argc, const char ** argv) {
4567   lrc = checkForLilo(lconfig);   lrc = checkForLilo(lconfig);
4568   }   }
4569    
4570   if (lrc == 1 || grc == 1) return 1;   if (!access(eliloConfigType.defaultConfig, F_OK)) {
4571        econfig = readConfig(eliloConfigType.defaultConfig,
4572     &eliloConfigType);
4573        if (!econfig)
4574     erc = 1;
4575        else
4576     erc = checkForElilo(econfig);
4577     }
4578    
4579     if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4580        lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4581        if (!lconfig)
4582     extrc = 1;
4583        else
4584     extrc = checkForExtLinux(lconfig);
4585     }
4586    
4587    
4588     if (!access(yabootConfigType.defaultConfig, F_OK)) {
4589        yconfig = readConfig(yabootConfigType.defaultConfig,
4590     &yabootConfigType);
4591        if (!yconfig)
4592     yrc = 1;
4593        else
4594     yrc = checkForYaboot(yconfig);
4595     }
4596    
4597     if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4598     erc == 1)
4599        return 1;
4600    
4601   if (lrc == 2) printf("lilo\n");   if (lrc == 2) printf("lilo\n");
4602     if (gr2c == 2) printf("grub2\n");
4603   if (grc == 2) printf("grub\n");   if (grc == 2) printf("grub\n");
4604     if (extrc == 2) printf("extlinux\n");
4605     if (yrc == 2) printf("yaboot\n");
4606     if (erc == 2) printf("elilo\n");
4607    
4608   return 0;   return 0;
4609      }      }
4610    
4611        if (grubConfig == NULL) {
4612     printf("Could not find bootloader configuration file.\n");
4613     exit(1);
4614        }
4615    
4616      config = readConfig(grubConfig, cfi);      config = readConfig(grubConfig, cfi);
4617      if (!config) return 1;      if (!config) return 1;
4618    
# Line 2557  int main(int argc, const char ** argv) { Line 4622  int main(int argc, const char ** argv) {
4622          char * rootspec;          char * rootspec;
4623    
4624   if (config->defaultImage == -1) return 0;   if (config->defaultImage == -1) return 0;
4625     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4626     cfi->defaultIsSaved)
4627        config->defaultImage = 0;
4628   entry = findEntryByIndex(config, config->defaultImage);   entry = findEntryByIndex(config, config->defaultImage);
4629   if (!entry) return 0;   if (!entry) return 0;
4630   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4631    
4632   line = entry->lines;   line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
  while (line && line->type != LT_KERNEL) line = line->next;  
4633   if (!line) return 0;   if (!line) return 0;
4634    
4635          rootspec = getRootSpecifier(line->elements[1].item);          rootspec = getRootSpecifier(line->elements[1].item);
# Line 2570  int main(int argc, const char ** argv) { Line 4637  int main(int argc, const char ** argv) {
4637                 ((rootspec != NULL) ? strlen(rootspec) : 0));                 ((rootspec != NULL) ? strlen(rootspec) : 0));
4638    
4639   return 0;   return 0;
4640    
4641        } else if (displayDefaultTitle) {
4642     struct singleLine * line;
4643     struct singleEntry * entry;
4644    
4645     if (config->defaultImage == -1) return 0;
4646     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4647     cfi->defaultIsSaved)
4648        config->defaultImage = 0;
4649     entry = findEntryByIndex(config, config->defaultImage);
4650     if (!entry) return 0;
4651    
4652     if (!configureGrub2) {
4653      line = getLineByType(LT_TITLE, entry->lines);
4654      if (!line) return 0;
4655      printf("%s\n", line->elements[1].item);
4656    
4657     } else {
4658      char * title;
4659    
4660      dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4661      line = getLineByType(LT_MENUENTRY, entry->lines);
4662      if (!line) return 0;
4663      title = grub2ExtractTitle(line);
4664      if (title)
4665        printf("%s\n", title);
4666     }
4667     return 0;
4668    
4669        } else if (displayDefaultIndex) {
4670            if (config->defaultImage == -1) return 0;
4671     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4672     cfi->defaultIsSaved)
4673        config->defaultImage = 0;
4674            printf("%i\n", config->defaultImage);
4675            return 0;
4676    
4677      } else if (kernelInfo)      } else if (kernelInfo)
4678   return displayInfo(config, kernelInfo, bootPrefix);   return displayInfo(config, kernelInfo, bootPrefix);
4679    
# Line 2581  int main(int argc, const char ** argv) { Line 4685  int main(int argc, const char ** argv) {
4685      markRemovedImage(config, removeKernelPath, bootPrefix);      markRemovedImage(config, removeKernelPath, bootPrefix);
4686      markRemovedImage(config, removeMBKernel, bootPrefix);      markRemovedImage(config, removeMBKernel, bootPrefix);
4687      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4688      bootPrefix, flags);      bootPrefix, flags, defaultIndex);
4689      setFallbackImage(config, newKernelPath != NULL);      setFallbackImage(config, newKernelPath != NULL);
4690      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4691                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4692        if (updateKernelPath && newKernelInitrd) {
4693        if (newMBKernel) {
4694        if (addMBInitrd(config, newMBKernel, updateKernelPath,
4695     bootPrefix, newKernelInitrd,
4696     newKernelTitle))
4697        return 1;
4698        } else {
4699        if (updateInitrd(config, updateKernelPath, bootPrefix,
4700     newKernelInitrd, newKernelTitle))
4701     return 1;
4702        }
4703        }
4704      if (addNewKernel(config, template, bootPrefix, newKernelPath,      if (addNewKernel(config, template, bootPrefix, newKernelPath,
4705                       newKernelTitle, newKernelArgs, newKernelInitrd,                       newKernelTitle, newKernelArgs, newKernelInitrd,
4706                       newMBKernel, newMBKernelArgs)) return 1;                       (const char **)extraInitrds, extraInitrdCount,
4707                         newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;
4708            
4709    
4710      if (numEntries(config) == 0) {      if (numEntries(config) == 0) {
# Line 2597  int main(int argc, const char ** argv) { Line 4714  int main(int argc, const char ** argv) {
4714      }      }
4715    
4716      if (!outputFile)      if (!outputFile)
4717   outputFile = grubConfig;   outputFile = (char *)grubConfig;
4718    
4719      return writeConfig(config, outputFile, bootPrefix);      return writeConfig(config, outputFile, bootPrefix);
4720  }  }

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