Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (hide annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
Original Path: trunk/mkinitrd-magellan/grubby/grubby.c
File MIME type: text/plain
File size: 76002 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd

1 niro 532 /* Copyright (C) 2001-2005 Red Hat, Inc.
2    
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
5     by the Free Software Foundation; either version 2 of the License, or
6     (at your option) any later version.
7    
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11     General Public License for more details.
12    
13     You should have received a copy of the GNU General Public
14     License along with this program; if not, write to the Free
15     Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
16     02111-1307 USA. */
17    
18     #include <ctype.h>
19     #include <errno.h>
20     #include <fcntl.h>
21     #include <mntent.h>
22     #include <popt.h>
23     #include <stdarg.h>
24     #include <stdlib.h>
25     #include <string.h>
26     #include <sys/stat.h>
27     #include <unistd.h>
28    
29     #include "mount_by_label.h"
30    
31     #define _(A) (A)
32    
33     #define CODE_SEG_SIZE 128 /* code segment checked by --bootloader-probe */
34    
35     /* comments get lumped in with indention */
36     struct lineElement {
37     char * item;
38     char * indent;
39     };
40    
41     enum lineType_e { LT_WHITESPACE, LT_TITLE, LT_KERNEL, LT_INITRD, LT_DEFAULT,
42     LT_UNKNOWN, LT_ROOT, LT_FALLBACK, LT_KERNELARGS, LT_BOOT,
43     LT_BOOTROOT, LT_LBA, LT_MBMODULE, LT_OTHER, LT_GENERIC };
44    
45     struct singleLine {
46     char * indent;
47     int numElements;
48     struct lineElement * elements;
49     struct singleLine * next;
50     enum lineType_e type;
51     };
52    
53     struct singleEntry {
54     struct singleLine * lines;
55     int skip;
56     int multiboot;
57     struct singleEntry * next;
58     };
59    
60     #define GRUBBY_BADIMAGE_OKAY (1 << 0)
61    
62     #define GRUB_CONFIG_NO_DEFAULT (1 << 0) /* don't write out default=0 */
63    
64     #define KERNEL_KERNEL (1 << 0)
65     #define KERNEL_INITRD (1 << 2)
66     #define KERNEL_TITLE (1 << 3)
67     #define KERNEL_ARGS (1 << 4)
68     #define KERNEL_MB (1 << 5)
69    
70     #define MAIN_DEFAULT (1 << 0)
71     #define DEFAULT_SAVED -2
72    
73     struct keywordTypes {
74     char * key;
75     enum lineType_e type;
76     char nextChar;
77     } ;
78    
79     struct configFileInfo {
80     char * defaultConfig;
81     struct keywordTypes * keywords;
82     int defaultIsIndex;
83     int defaultSupportSaved;
84     enum lineType_e entrySeparator;
85     int needsBootPrefix;
86     int argsInQuotes;
87     int maxTitleLength;
88     int titleBracketed;
89     };
90    
91     struct keywordTypes grubKeywords[] = {
92     { "title", LT_TITLE, ' ' },
93     { "root", LT_BOOTROOT, ' ' },
94     { "default", LT_DEFAULT, ' ' },
95     { "fallback", LT_FALLBACK, ' ' },
96     { "kernel", LT_KERNEL, ' ' },
97     { "initrd", LT_INITRD, ' ' },
98     { "module", LT_MBMODULE, ' ' },
99     { NULL, 0, 0 },
100     };
101    
102     struct configFileInfo grubConfigType = {
103     "/boot/grub/grub.conf", /* defaultConfig */
104     grubKeywords, /* keywords */
105     1, /* defaultIsIndex */
106     1, /* defaultSupportSaved */
107     LT_TITLE, /* entrySeparator */
108     1, /* needsBootPrefix */
109     0, /* argsInQuotes */
110     0, /* maxTitleLength */
111     0, /* titleBracketed */
112     };
113    
114     struct keywordTypes yabootKeywords[] = {
115     { "label", LT_TITLE, '=' },
116     { "root", LT_ROOT, '=' },
117     { "default", LT_DEFAULT, '=' },
118     { "image", LT_KERNEL, '=' },
119     { "bsd", LT_GENERIC, '=' },
120     { "macos", LT_GENERIC, '=' },
121     { "macosx", LT_GENERIC, '=' },
122     { "magicboot", LT_GENERIC, '=' },
123     { "darwin", LT_GENERIC, '=' },
124     { "timeout", LT_GENERIC, '=' },
125     { "install", LT_GENERIC, '=' },
126     { "fstype", LT_GENERIC, '=' },
127     { "hfstype", LT_GENERIC, '=' },
128     { "delay", LT_GENERIC, '=' },
129     { "defaultos", LT_GENERIC, '=' },
130     { "init-message", LT_GENERIC, '=' },
131     { "enablecdboot", LT_GENERIC, ' ' },
132     { "enableofboot", LT_GENERIC, ' ' },
133     { "enablenetboot", LT_GENERIC, ' ' },
134     { "nonvram", LT_GENERIC, ' ' },
135     { "hide", LT_GENERIC, ' ' },
136     { "protect", LT_GENERIC, ' ' },
137     { "nobless", LT_GENERIC, ' ' },
138     { "nonvram", LT_GENERIC, ' ' },
139     { "brokenosx", LT_GENERIC, ' ' },
140     { "usemount", LT_GENERIC, ' ' },
141     { "mntpoint", LT_GENERIC, '=' },
142     { "partition", LT_GENERIC, '=' },
143     { "device", LT_GENERIC, '=' },
144     { "fstype", LT_GENERIC, '=' },
145     { "initrd", LT_INITRD, '=' },
146     { "append", LT_KERNELARGS, '=' },
147     { "boot", LT_BOOT, '=' },
148     { "lba", LT_LBA, ' ' },
149     { NULL, 0, 0 },
150     };
151    
152     struct keywordTypes liloKeywords[] = {
153     { "label", LT_TITLE, '=' },
154     { "root", LT_ROOT, '=' },
155     { "default", LT_DEFAULT, '=' },
156     { "image", LT_KERNEL, '=' },
157     { "other", LT_OTHER, '=' },
158     { "initrd", LT_INITRD, '=' },
159     { "append", LT_KERNELARGS, '=' },
160     { "boot", LT_BOOT, '=' },
161     { "lba", LT_LBA, ' ' },
162     { NULL, 0, 0 },
163     };
164    
165     struct keywordTypes siloKeywords[] = {
166     { "label", LT_TITLE, '=' },
167     { "root", LT_ROOT, '=' },
168     { "default", LT_DEFAULT, '=' },
169     { "image", LT_KERNEL, '=' },
170     { "other", LT_OTHER, '=' },
171     { "initrd", LT_INITRD, '=' },
172     { "append", LT_KERNELARGS, '=' },
173     { "boot", LT_BOOT, '=' },
174     { NULL, 0, 0 },
175     };
176    
177     struct keywordTypes ziplKeywords[] = {
178     { "target", LT_BOOTROOT, '=' },
179     { "image", LT_KERNEL, '=' },
180     { "ramdisk", LT_INITRD, '=' },
181     { "parameters", LT_KERNELARGS, '=' },
182     { "default", LT_DEFAULT, '=' },
183     { NULL, 0, 0 },
184     };
185    
186     struct configFileInfo eliloConfigType = {
187     "/boot/efi/EFI/redhat/elilo.conf", /* defaultConfig */
188     liloKeywords, /* keywords */
189     0, /* defaultIsIndex */
190     0, /* defaultSupportSaved */
191     LT_KERNEL, /* entrySeparator */
192     1, /* needsBootPrefix */
193     1, /* argsInQuotes */
194     0, /* maxTitleLength */
195     0, /* titleBracketed */
196     };
197    
198     struct configFileInfo liloConfigType = {
199     "/etc/lilo.conf", /* defaultConfig */
200     liloKeywords, /* keywords */
201     0, /* defaultIsIndex */
202     0, /* defaultSupportSaved */
203     LT_KERNEL, /* entrySeparator */
204     0, /* needsBootPrefix */
205     1, /* argsInQuotes */
206     15, /* maxTitleLength */
207     0, /* titleBracketed */
208     };
209    
210     struct configFileInfo yabootConfigType = {
211     "/etc/yaboot.conf", /* defaultConfig */
212     yabootKeywords, /* keywords */
213     0, /* defaultIsIndex */
214     0, /* defaultSupportSaved */
215     LT_KERNEL, /* entrySeparator */
216     1, /* needsBootPrefix */
217     1, /* argsInQuotes */
218     15, /* maxTitleLength */
219     0, /* titleBracketed */
220     };
221    
222     struct configFileInfo siloConfigType = {
223     "/etc/silo.conf", /* defaultConfig */
224     siloKeywords, /* keywords */
225     0, /* defaultIsIndex */
226     0, /* defaultSupportSaved */
227     LT_KERNEL, /* entrySeparator */
228     1, /* needsBootPrefix */
229     1, /* argsInQuotes */
230     15, /* maxTitleLength */
231     0, /* titleBracketed */
232     };
233    
234     struct configFileInfo ziplConfigType = {
235     "/etc/zipl.conf", /* defaultConfig */
236     ziplKeywords, /* keywords */
237     0, /* defaultIsIndex */
238     0, /* defaultSupportSaved */
239     LT_TITLE, /* entrySeparator */
240     0, /* needsBootPrefix */
241     1, /* argsInQuotes */
242     15, /* maxTitleLength */
243     1, /* titleBracketed */
244     };
245    
246     struct grubConfig {
247     struct singleLine * theLines;
248     struct singleEntry * entries;
249     char * primaryIndent;
250     char * secondaryIndent;
251     int defaultImage; /* -1 if none specified -- this value is
252     * written out, overriding original */
253     int fallbackImage; /* just like defaultImage */
254     int flags;
255     struct configFileInfo * cfi;
256     };
257    
258    
259     struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);
260     struct singleEntry * findEntryByPath(struct grubConfig * cfg,
261     const char * path, const char * prefix,
262     int * index);
263     static char * strndup(char * from, int len);
264     static int readFile(int fd, char ** bufPtr);
265     static void lineInit(struct singleLine * line);
266     static void lineFree(struct singleLine * line);
267     static int lineWrite(FILE * out, struct singleLine * line,
268     struct configFileInfo * cfi);
269     static int getNextLine(char ** bufPtr, struct singleLine * line,
270     struct configFileInfo * cfi);
271     static char * getRootSpecifier(char * str);
272    
273     static char * strndup(char * from, int len) {
274     char * to;
275    
276     to = malloc(len + 1);
277     strncpy(to, from, len);
278     to[len] = '\0';
279    
280     return to;
281     }
282    
283     static char * sdupprintf(const char *format, ...)
284     #ifdef __GNUC__
285     __attribute__ ((format (printf, 1, 2)));
286     #else
287     ;
288     #endif
289    
290     static char * sdupprintf(const char *format, ...) {
291     char *buf = NULL;
292     char c;
293     va_list args;
294     size_t size = 0;
295     va_start(args, format);
296    
297     /* XXX requires C99 vsnprintf behavior */
298     size = vsnprintf(&c, 1, format, args) + 1;
299     if (size == -1) {
300     printf("ERROR: vsnprintf behavior is not C99\n");
301     abort();
302     }
303    
304     va_end(args);
305     va_start(args, format);
306    
307     buf = malloc(size);
308     if (buf == NULL)
309     return NULL;
310     vsnprintf(buf, size, format, args);
311     va_end (args);
312    
313     return buf;
314     }
315    
316     static int isBracketedTitle(struct singleLine * line) {
317     if ((*line->elements[0].item == '[') && (line->numElements == 1)) {
318     int len = strlen(line->elements[0].item);
319     if (*(line->elements[0].item + len - 1) == ']') {
320     /* FIXME: this is a hack... */
321     if (strcmp(line->elements[0].item, "[defaultboot]")) {
322     return 1;
323     }
324     }
325     }
326     return 0;
327     }
328    
329     /* figure out if this is a entry separator */
330     static int isEntrySeparator(struct singleLine * line,
331     struct configFileInfo * cfi) {
332     if (line->type == LT_WHITESPACE)
333     return 0;
334     if (line->type == cfi->entrySeparator)
335     return 1;
336     if (line->type == LT_OTHER)
337     return 1;
338     if (cfi->titleBracketed && isBracketedTitle(line)) {
339     return 1;
340     }
341     return 0;
342     }
343    
344     /* extract the title from within brackets (for zipl) */
345     static char * extractTitle(struct singleLine * line) {
346     /* bracketed title... let's extract it (leaks a byte) */
347     char * title;
348     title = strdup(line->elements[0].item);
349     title++;
350     *(title + strlen(title) - 1) = '\0';
351     return title;
352     }
353    
354     static int readFile(int fd, char ** bufPtr) {
355     int alloced = 0, size = 0, i = 0;
356     char * buf = NULL;
357    
358     do {
359     size += i;
360     if ((size + 1024) > alloced) {
361     alloced += 4096;
362     buf = realloc(buf, alloced + 1);
363     }
364     } while ((i = read(fd, buf + size, 1024)) > 0);
365    
366     if (i < 0) {
367     fprintf(stderr, _("error reading input: %s\n"), strerror(errno));
368     free(buf);
369     return 1;
370     }
371    
372     buf = realloc(buf, size + 2);
373     if (size == 0)
374     buf[size++] = '\n';
375     else
376     if (buf[size - 1] != '\n')
377     buf[size++] = '\n';
378     buf[size] = '\0';
379    
380     *bufPtr = buf;
381    
382     return 0;
383     }
384    
385     static void lineInit(struct singleLine * line) {
386     line->indent = NULL;
387     line->elements = NULL;
388     line->numElements = 0;
389     line->next = NULL;
390     }
391    
392     static void lineFree(struct singleLine * line) {
393     int i;
394    
395     if (line->indent) free(line->indent);
396    
397     for (i = 0; i < line->numElements; i++) {
398     free(line->elements[i].item);
399     free(line->elements[i].indent);
400     }
401    
402     if (line->elements) free(line->elements);
403     lineInit(line);
404     }
405    
406     static int lineWrite(FILE * out, struct singleLine * line,
407     struct configFileInfo * cfi) {
408     int i;
409    
410     if (fprintf(out, "%s", line->indent) == -1) return -1;
411    
412     for (i = 0; i < line->numElements; i++) {
413     if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)
414     if (fputc('"', out) == EOF) return -1;
415    
416     if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;
417     if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;
418     }
419    
420     if (line->type == LT_KERNELARGS && cfi->argsInQuotes)
421     if (fputc('"', out) == EOF) return -1;
422    
423     if (fprintf(out, "\n") == -1) return -1;
424    
425     return 0;
426     }
427    
428     /* we've guaranteed that the buffer ends w/ \n\0 */
429     static int getNextLine(char ** bufPtr, struct singleLine * line,
430     struct configFileInfo * cfi) {
431     char * end;
432     char * start = *bufPtr;
433     char * chptr;
434     int elementsAlloced = 0;
435     struct lineElement * element;
436     struct keywordTypes * keywords = cfi->keywords;
437     int first = 1;
438     int i;
439    
440     lineFree(line);
441    
442     end = strchr(start, '\n');
443     *end = '\0';
444     *bufPtr = end + 1;
445    
446     for (chptr = start; *chptr && isspace(*chptr); chptr++) ;
447    
448     line->indent = strndup(start, chptr - start);
449     start = chptr;
450    
451     while (start < end) {
452     /* we know !isspace(*start) */
453    
454     if (elementsAlloced == line->numElements) {
455     elementsAlloced += 5;
456     line->elements = realloc(line->elements,
457     sizeof(*line->elements) * elementsAlloced);
458     }
459    
460     element = line->elements + line->numElements;
461    
462     chptr = start;
463     while (*chptr && !isspace(*chptr)) {
464     if (first && *chptr == '=') break;
465     chptr++;
466     }
467     element->item = strndup(start, chptr - start);
468     start = chptr;
469    
470     /* lilo actually accepts the pathological case of append = " foo " */
471     if (*start == '=')
472     chptr = start + 1;
473     else
474     chptr = start;
475    
476     do {
477     for (; *chptr && isspace(*chptr); chptr++);
478     if (*chptr == '=')
479     chptr = chptr + 1;
480     } while (isspace(*chptr));
481    
482     element->indent = strndup(start, chptr - start);
483     start = chptr;
484    
485     line->numElements++;
486     first = 0;
487     }
488    
489     if (!line->numElements)
490     line->type = LT_WHITESPACE;
491     else {
492     for (i = 0; keywords[i].key; i++)
493     if (!strcmp(line->elements[0].item, keywords[i].key)) break;
494    
495     if (keywords[i].key) {
496     line->type = keywords[i].type;
497     } else {
498     line->type = LT_UNKNOWN;
499    
500     /* zipl does [title] instead of something reasonable like all
501     * the other boot loaders. kind of ugly */
502     if (cfi->titleBracketed && isBracketedTitle(line)) {
503     line->type = LT_TITLE;
504     }
505    
506     /* this is awkward, but we need to be able to handle keywords
507     that begin with a # (specifically for #boot in grub.conf),
508     but still make comments lines with no elements (everything
509     stored in the indent */
510     if (*line->elements[0].item == '#') {
511     char * fullLine;
512     int len;
513     int i;
514    
515     len = strlen(line->indent);
516     for (i = 0; i < line->numElements; i++)
517     len += strlen(line->elements[i].item) +
518     strlen(line->elements[i].indent);
519    
520     fullLine = malloc(len + 1);
521     strcpy(fullLine, line->indent);
522     free(line->indent);
523     line->indent = fullLine;
524    
525     for (i = 0; i < line->numElements; i++) {
526     strcat(fullLine, line->elements[i].item);
527     strcat(fullLine, line->elements[i].indent);
528     free(line->elements[i].item);
529     free(line->elements[i].indent);
530     }
531    
532     line->type = LT_WHITESPACE;
533     line->numElements = 0;
534     }
535     }
536     }
537    
538     return 0;
539     }
540    
541     static struct grubConfig * readConfig(const char * inName,
542     struct configFileInfo * cfi) {
543     int in;
544     char * incoming = NULL, * head;
545     int rc;
546     int sawEntry = 0;
547     int movedLine = 0;
548     struct grubConfig * cfg;
549     struct singleLine * last = NULL, * line, * defaultLine = NULL;
550     char * end;
551     struct singleEntry * entry = NULL;
552     int i, len;
553     char * buf;
554    
555     if (!strcmp(inName, "-")) {
556     in = 0;
557     } else {
558     if ((in = open(inName, O_RDONLY)) < 0) {
559     fprintf(stderr, _("error opening %s for read: %s\n"),
560     inName, strerror(errno));
561     return NULL;
562     }
563     }
564    
565     rc = readFile(in, &incoming);
566     close(in);
567     if (rc) return NULL;
568    
569     head = incoming;
570     cfg = malloc(sizeof(*cfg));
571     cfg->primaryIndent = strdup("");
572     cfg->secondaryIndent = strdup("\t");
573     cfg->flags = GRUB_CONFIG_NO_DEFAULT;
574     cfg->cfi = cfi;
575     cfg->theLines = NULL;
576     cfg->entries = NULL;
577     cfg->fallbackImage = 0;
578    
579     /* copy everything we have */
580     while (*head) {
581     line = malloc(sizeof(*line));
582     lineInit(line);
583    
584     if (getNextLine(&head, line, cfi)) {
585     free(line);
586     /* XXX memory leak of everything in cfg */
587     return NULL;
588     }
589    
590     if (!sawEntry && line->numElements) {
591     free(cfg->primaryIndent);
592     cfg->primaryIndent = strdup(line->indent);
593     } else if (line->numElements) {
594     free(cfg->secondaryIndent);
595     cfg->secondaryIndent = strdup(line->indent);
596     }
597    
598     if (isEntrySeparator(line, cfi)) {
599     sawEntry = 1;
600     if (!entry) {
601     cfg->entries = malloc(sizeof(*entry));
602     entry = cfg->entries;
603     } else {
604     entry->next = malloc(sizeof(*entry));
605     entry = entry->next;
606     }
607    
608     entry->skip = 0;
609     entry->multiboot = 0;
610     entry->lines = NULL;
611     entry->next = NULL;
612     }
613    
614     if (line->type == LT_DEFAULT && line->numElements == 2) {
615     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
616     defaultLine = line;
617     } else if (line->type == LT_MBMODULE) {
618     entry->multiboot = 1;
619     } else if (line->type == LT_FALLBACK && line->numElements == 2) {
620     cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);
621     if (*end) cfg->fallbackImage = -1;
622     } else if (line->type == LT_TITLE && line->numElements > 1) {
623     /* make the title a single argument (undoing our parsing) */
624     len = 0;
625     for (i = 1; i < line->numElements; i++) {
626     len += strlen(line->elements[i].item);
627     len += strlen(line->elements[i].indent);
628     }
629     buf = malloc(len + 1);
630     *buf = '\0';
631    
632     for (i = 1; i < line->numElements; i++) {
633     strcat(buf, line->elements[i].item);
634     free(line->elements[i].item);
635    
636     if ((i + 1) != line->numElements) {
637     strcat(buf, line->elements[i].indent);
638     free(line->elements[i].indent);
639     }
640     }
641    
642     line->elements[1].indent =
643     line->elements[line->numElements - 1].indent;
644     line->elements[1].item = buf;
645     line->numElements = 2;
646     } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {
647     /* Strip off any " which may be present; they'll be put back
648     on write. This is one of the few (the only?) places that grubby
649     canonicalizes the output */
650    
651     if (line->numElements >= 2) {
652     int last, len;
653    
654     if (*line->elements[1].item == '"')
655     memcpy(line->elements[1].item, line->elements[1].item + 1,
656     strlen(line->elements[1].item + 1) + 1);
657    
658     last = line->numElements - 1;
659     len = strlen(line->elements[last].item) - 1;
660     if (line->elements[last].item[len] == '"')
661     line->elements[last].item[len] = '\0';
662     }
663    
664     }
665    
666     /* If we find a generic config option which should live at the
667     top of the file, move it there. Old versions of grubby were
668     probably responsible for putting new images in the wrong
669     place in front of it anyway. */
670     if (sawEntry && line->type == LT_GENERIC) {
671     struct singleLine **l = &cfg->theLines;
672     struct singleLine **last_nonws = &cfg->theLines;
673     while (*l) {
674     if ((*l)->type != LT_WHITESPACE)
675     last_nonws = &((*l)->next);
676     l = &((*l)->next);
677     }
678     line->next = *last_nonws;
679     *last_nonws = line;
680     movedLine = 1;
681     continue; /* without setting 'last' */
682     }
683     /* If a second line of whitespace happens after a generic option
684     which was moved, drop it. */
685     if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {
686     lineFree(line);
687     free(line);
688     movedLine = 0;
689     continue;
690     }
691     movedLine = 0;
692    
693     if (sawEntry) {
694     if (!entry->lines)
695     entry->lines = line;
696     else
697     last->next = line;
698     } else {
699     if (!cfg->theLines)
700     cfg->theLines = line;
701     else {
702     last->next = line;
703     }
704     }
705    
706     last = line;
707     }
708    
709     free(incoming);
710    
711     if (defaultLine) {
712     if (cfi->defaultSupportSaved &&
713     !strncmp(defaultLine->elements[1].item, "saved", 5)) {
714     cfg->defaultImage = DEFAULT_SAVED;
715     } else if (cfi->defaultIsIndex) {
716     cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);
717     if (*end) cfg->defaultImage = -1;
718     } else if (defaultLine->numElements >= 2) {
719     i = 0;
720     while ((entry = findEntryByIndex(cfg, i))) {
721     for (line = entry->lines; line; line = line->next)
722     if (line->type == LT_TITLE) break;
723    
724     if (!cfi->titleBracketed) {
725     if (line && (line->numElements >= 2) &&
726     !strcmp(defaultLine->elements[1].item,
727     line->elements[1].item)) break;
728     } else if (line) {
729     if (!strcmp(defaultLine->elements[1].item,
730     extractTitle(line))) break;
731     }
732     i++;
733     }
734    
735     if (entry) cfg->defaultImage = i;
736     }
737     }
738    
739     return cfg;
740     }
741    
742     static void writeDefault(FILE * out, char * indent,
743     char * separator, struct grubConfig * cfg) {
744     struct singleEntry * entry;
745     struct singleLine * line;
746     int i;
747    
748     if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT) return;
749    
750     if (cfg->defaultImage == DEFAULT_SAVED)
751     fprintf(out, "%sdefault%ssaved\n", indent, separator);
752     else if (cfg->defaultImage > -1) {
753     if (cfg->cfi->defaultIsIndex) {
754     fprintf(out, "%sdefault%s%d\n", indent, separator,
755     cfg->defaultImage);
756     } else {
757     int image = cfg->defaultImage;
758    
759     entry = cfg->entries;
760     while (entry && entry->skip) entry = entry->next;
761    
762     i = 0;
763     while (entry && i < image) {
764     entry = entry->next;
765    
766     while (entry && entry->skip) entry = entry->next;
767     i++;
768     }
769    
770     if (!entry) return;
771    
772     line = entry->lines;
773     while (line && line->type != LT_TITLE) line = line->next;
774    
775     if (line && line->numElements >= 2)
776     fprintf(out, "%sdefault%s%s\n", indent, separator,
777     line->elements[1].item);
778     else if (line && (line->numElements == 1) &&
779     cfg->cfi->titleBracketed) {
780     fprintf(out, "%sdefault%s%s\n", indent, separator,
781     extractTitle(line));
782     }
783     }
784     }
785     }
786    
787     static int writeConfig(struct grubConfig * cfg, char * outName,
788     const char * prefix) {
789     FILE * out;
790     struct singleLine * line;
791     struct singleEntry * entry;
792     char * tmpOutName;
793     int needs = MAIN_DEFAULT;
794     struct stat sb;
795     int i;
796    
797     if (!strcmp(outName, "-")) {
798     out = stdout;
799     tmpOutName = NULL;
800     } else {
801     if (!lstat(outName, &sb) && S_ISLNK(sb.st_mode)) {
802     char * buf;
803     int len = 256;
804     int rc;
805    
806     /* most likely the symlink is relative, so change our
807     directory to / */
808     rc = chdir("/");
809     do {
810     buf = alloca(len + 1);
811     rc = readlink(outName, buf, len);
812     if (rc == len) len += 256;
813     } while (rc == len);
814    
815     if (rc < 0) {
816     fprintf(stderr, _("grubby: error readlink link %s: %s\n"),
817     outName, strerror(errno));
818     return 1;
819     }
820    
821     outName = buf;
822     outName[rc] = '\0';
823     }
824    
825     tmpOutName = alloca(strlen(outName) + 2);
826     sprintf(tmpOutName, "%s-", outName);
827     out = fopen(tmpOutName, "w");
828     if (!out) {
829     fprintf(stderr, _("grubby: error creating %s: %s\n"), tmpOutName,
830     strerror(errno));
831     return 1;
832     }
833    
834     if (!stat(outName, &sb)) {
835     if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
836     fprintf(stderr, _("grubby: error setting perms on %s: %s\n"),
837     tmpOutName, strerror(errno));
838     fclose(out);
839     unlink(tmpOutName);
840     return 1;
841     }
842     }
843     }
844    
845     line = cfg->theLines;
846     while (line) {
847     if (line->type == LT_DEFAULT) {
848     writeDefault(out, line->indent, line->elements[0].indent, cfg);
849     needs &= ~MAIN_DEFAULT;
850     } else if (line->type == LT_FALLBACK) {
851     if (cfg->fallbackImage > -1)
852     fprintf(out, "%s%s%s%d\n", line->indent,
853     line->elements[0].item, line->elements[0].indent,
854     cfg->fallbackImage);
855     } else {
856     if (lineWrite(out, line, cfg->cfi) == -1) {
857     fprintf(stderr, _("grubby: error writing %s: %s\n"),
858     tmpOutName, strerror(errno));
859     fclose(out);
860     unlink(tmpOutName);
861     return 1;
862     }
863     }
864    
865     line = line->next;
866     }
867    
868     if (needs & MAIN_DEFAULT) {
869     writeDefault(out, cfg->primaryIndent, "=", cfg);
870     needs &= ~MAIN_DEFAULT;
871     }
872    
873     i = 0;
874     while ((entry = findEntryByIndex(cfg, i++))) {
875     if (entry->skip) continue;
876    
877     line = entry->lines;
878     while (line) {
879     if (lineWrite(out, line, cfg->cfi) == -1) {
880     fprintf(stderr, _("grubby: error writing %s: %s\n"),
881     tmpOutName, strerror(errno));
882     fclose(out);
883     unlink(tmpOutName);
884     return 1;
885     }
886     line = line->next;
887     }
888     }
889    
890     if (tmpOutName) {
891     if (rename(tmpOutName, outName)) {
892     fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),
893     tmpOutName, outName, strerror(errno));
894     unlink(outName);
895     return 1;
896     }
897     }
898    
899     return 0;
900     }
901    
902     static int numEntries(struct grubConfig *cfg) {
903     int i = 0;
904     struct singleEntry * entry;
905    
906     entry = cfg->entries;
907     while (entry) {
908     if (!entry->skip)
909     i++;
910     entry = entry->next;
911     }
912     return i;
913     }
914    
915     int suitableImage(struct singleEntry * entry, const char * bootPrefix,
916     int skipRemoved, int flags) {
917     struct singleLine * line;
918     char * fullName;
919     int i;
920     struct stat sb, sb2;
921     char * dev;
922     char * end;
923     char * rootspec;
924    
925     line = entry->lines;
926     while (line && line->type != LT_KERNEL) line = line->next;
927    
928     if (!line) return 0;
929     if (skipRemoved && entry->skip) return 0;
930     if (line->numElements < 2) return 0;
931    
932     if (flags & GRUBBY_BADIMAGE_OKAY) return 1;
933    
934     fullName = alloca(strlen(bootPrefix) +
935     strlen(line->elements[1].item) + 1);
936     rootspec = getRootSpecifier(line->elements[1].item);
937     sprintf(fullName, "%s%s", bootPrefix,
938     line->elements[1].item + ((rootspec != NULL) ?
939     strlen(rootspec) : 0));
940     if (access(fullName, R_OK)) return 0;
941    
942     for (i = 2; i < line->numElements; i++)
943     if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
944     if (i < line->numElements) {
945     dev = line->elements[i].item + 5;
946     } else {
947     /* look for a lilo style LT_ROOT line */
948     line = entry->lines;
949     while (line && line->type != LT_ROOT) line = line->next;
950    
951     if (line && line->numElements >= 2) {
952     dev = line->elements[1].item;
953     } else {
954     int type;
955     /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS */
956     line = entry->lines;
957    
958     type = ((entry->multiboot) ? LT_MBMODULE : LT_KERNELARGS);
959    
960     while (line && line->type != type) line = line->next;
961    
962     /* failed to find one */
963     if (!line) return 0;
964    
965     for (i = 1; i < line->numElements; i++)
966     if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
967     if (i < line->numElements)
968     dev = line->elements[i].item + 5;
969     else {
970     /* it failed too... can't find root= */
971     return 0;
972     }
973     }
974     }
975    
976     if (!strncmp(dev, "LABEL=", 6)) {
977     dev += 6;
978    
979     /* check which device has this label */
980     dev = get_spec_by_volume_label(dev, &i, &i);
981     if (!dev) return 0;
982     }
983    
984     if (*dev == '/') {
985     if (stat(dev, &sb))
986     return 0;
987     } else {
988     sb.st_rdev = strtol(dev, &end, 16);
989     if (*end) return 0;
990     }
991     stat("/", &sb2);
992    
993     if (sb.st_rdev != sb2.st_dev) return 0;
994    
995     return 1;
996     }
997    
998     /* returns the first match on or after the one pointed to by index (if index
999     is not NULL) which is not marked as skip */
1000     struct singleEntry * findEntryByPath(struct grubConfig * config,
1001     const char * kernel, const char * prefix,
1002     int * index) {
1003     struct singleEntry * entry = NULL;
1004     struct singleLine * line;
1005     int i;
1006     char * chptr;
1007     char * rootspec = NULL;
1008     enum lineType_e checkType = LT_KERNEL;
1009    
1010     if (isdigit(*kernel)) {
1011     int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
1012    
1013     i = 0;
1014     indexVars[i] = strtol(kernel, &chptr, 10);
1015     while (*chptr == ',') {
1016     i++;
1017     kernel = chptr + 1;
1018     indexVars[i] = strtol(kernel, &chptr, 10);
1019     }
1020    
1021     if (*chptr) {
1022     /* can't parse it, bail */
1023     return NULL;
1024     }
1025    
1026     indexVars[i + 1] = -1;
1027    
1028     i = 0;
1029     if (index) {
1030     while (i < *index) i++;
1031     if (indexVars[i] == -1) return NULL;
1032     }
1033    
1034     entry = findEntryByIndex(config, indexVars[i]);
1035     if (!entry) return NULL;
1036    
1037     line = entry->lines;
1038     while (line && line->type != LT_KERNEL)
1039     line = line->next;
1040    
1041     if (!line) return NULL;
1042    
1043     if (index) *index = indexVars[i];
1044     return entry;
1045     }
1046    
1047     if (!strcmp(kernel, "DEFAULT")) {
1048     if (index && *index > config->defaultImage) {
1049     entry = NULL;
1050     } else {
1051     entry = findEntryByIndex(config, config->defaultImage);
1052     if (entry && entry->skip)
1053     entry = NULL;
1054     else if (index)
1055     *index = config->defaultImage;
1056     }
1057     } else if (!strcmp(kernel, "ALL")) {
1058     if (index)
1059     i = *index;
1060     else
1061     i = 0;
1062    
1063     while ((entry = findEntryByIndex(config, i))) {
1064     if (!entry->skip) break;
1065     i++;
1066     }
1067    
1068     if (entry && index)
1069     *index = i;
1070     } else {
1071     if (index)
1072     i = *index;
1073     else
1074     i = 0;
1075    
1076     if (!strncmp(kernel, "TITLE=", 6)) {
1077     prefix = "";
1078     checkType = LT_TITLE;
1079     kernel += 6;
1080     }
1081    
1082     while ((entry = findEntryByIndex(config, i))) {
1083     line = entry->lines;
1084     while (line && line->type != checkType) line=line->next;
1085    
1086    
1087     if (line && line->numElements >= 2 && !entry->skip) {
1088     rootspec = getRootSpecifier(line->elements[1].item);
1089     if (!strcmp(line->elements[1].item +
1090     ((rootspec != NULL) ? strlen(rootspec) : 0),
1091     kernel + strlen(prefix)))
1092     break;
1093     }
1094    
1095     /* have to check multiboot lines too */
1096     if (entry->multiboot) {
1097     while (line && line->type != LT_MBMODULE) line = line->next;
1098     if (line && line->numElements >= 2 && !entry->skip) {
1099     rootspec = getRootSpecifier(line->elements[1].item);
1100     if (!strcmp(line->elements[1].item +
1101     ((rootspec != NULL) ? strlen(rootspec) : 0),
1102     kernel + strlen(prefix)))
1103     break;
1104     }
1105     }
1106    
1107     i++;
1108     }
1109    
1110     if (index) *index = i;
1111     }
1112    
1113     if (!entry) return NULL;
1114    
1115     /* make sure this entry has a kernel identifier; this skips non-Linux
1116     boot entries (could find netbsd etc, though, which is unfortunate) */
1117     line = entry->lines;
1118     while (line && line->type != LT_KERNEL) line = line->next;
1119     if (!line) {
1120     if (!index) index = &i;
1121     (*index)++;
1122     return findEntryByPath(config, kernel, prefix, index);
1123     }
1124    
1125     return entry;
1126     }
1127    
1128     struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {
1129     struct singleEntry * entry;
1130    
1131     entry = cfg->entries;
1132     while (index && entry) {
1133     entry = entry->next;
1134     index--;
1135     }
1136    
1137     return entry;
1138     }
1139    
1140     /* Find a good template to use for the new kernel. An entry is
1141     * good if the kernel and mkinitrd exist (even if the entry
1142     * is going to be removed). Try and use the default entry, but
1143     * if that doesn't work just take the first. If we can't find one,
1144     * bail. */
1145     struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,
1146     int * indexPtr, int skipRemoved, int flags) {
1147     struct singleEntry * entry, * entry2;
1148     int index;
1149    
1150     if (cfg->defaultImage > -1) {
1151     entry = findEntryByIndex(cfg, cfg->defaultImage);
1152     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
1153     if (indexPtr) *indexPtr = cfg->defaultImage;
1154     return entry;
1155     }
1156     }
1157    
1158     index = 0;
1159     while ((entry = findEntryByIndex(cfg, index))) {
1160     if (suitableImage(entry, prefix, skipRemoved, flags)) {
1161     int j;
1162     for (j = 0; j < index; j++) {
1163     entry2 = findEntryByIndex(cfg, j);
1164     if (entry2->skip) index--;
1165     }
1166     if (indexPtr) *indexPtr = index;
1167    
1168     return entry;
1169     }
1170    
1171     index++;
1172     }
1173    
1174     fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));
1175    
1176     return NULL;
1177     }
1178    
1179     char * findBootPrefix(void) {
1180     struct stat sb, sb2;
1181    
1182     stat("/", &sb);
1183     #ifdef __ia64__
1184     stat("/boot/efi/EFI/redhat/", &sb2);
1185     #else
1186     stat("/boot", &sb2);
1187     #endif
1188    
1189     if (sb.st_dev == sb2.st_dev)
1190     return strdup("");
1191    
1192     #ifdef __ia64__
1193     return strdup("/boot/efi/EFI/redhat/");
1194     #else
1195     return strdup("/boot");
1196     #endif
1197     }
1198    
1199     void markRemovedImage(struct grubConfig * cfg, const char * image,
1200     const char * prefix) {
1201     struct singleEntry * entry;
1202    
1203     if (!image) return;
1204    
1205     while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
1206     entry->skip = 1;
1207     }
1208    
1209     void setDefaultImage(struct grubConfig * config, int hasNew,
1210     const char * defaultKernelPath, int newIsDefault,
1211     const char * prefix, int flags) {
1212     struct singleEntry * entry, * entry2, * newDefault;
1213     int i, j;
1214    
1215     if (newIsDefault) {
1216     config->defaultImage = 0;
1217     return;
1218     } else if (defaultKernelPath) {
1219     i = 0;
1220     if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
1221     config->defaultImage = i;
1222     } else {
1223     config->defaultImage = -1;
1224     return;
1225     }
1226     }
1227    
1228     /* defaultImage now points to what we'd like to use, but before any order
1229     changes */
1230     if (config->defaultImage == DEFAULT_SAVED)
1231     /* default is set to saved, we don't want to change it */
1232     return;
1233    
1234     if (config->defaultImage > -1)
1235     entry = findEntryByIndex(config, config->defaultImage);
1236     else
1237     entry = NULL;
1238    
1239     if (entry && !entry->skip) {
1240     /* we can preserve the default */
1241     if (hasNew)
1242     config->defaultImage++;
1243    
1244     /* count the number of entries erased before this one */
1245     for (j = 0; j < config->defaultImage; j++) {
1246     entry2 = findEntryByIndex(config, j);
1247     if (entry2->skip) config->defaultImage--;
1248     }
1249     } else if (hasNew) {
1250     config->defaultImage = 0;
1251     } else {
1252     /* Either we just erased the default (or the default line was bad
1253     * to begin with) and didn't put a new one in. We'll use the first
1254     * valid image. */
1255     newDefault = findTemplate(config, prefix, &config->defaultImage, 1,
1256     flags);
1257     if (!newDefault)
1258     config->defaultImage = -1;
1259     }
1260     }
1261    
1262     void setFallbackImage(struct grubConfig * config, int hasNew) {
1263     struct singleEntry * entry, * entry2;
1264     int j;
1265    
1266     if (config->fallbackImage == -1) return;
1267    
1268     entry = findEntryByIndex(config, config->fallbackImage);
1269     if (!entry || entry->skip) {
1270     config->fallbackImage = -1;
1271     return;
1272     }
1273    
1274     if (hasNew)
1275     config->fallbackImage++;
1276    
1277     /* count the number of entries erased before this one */
1278     for (j = 0; j < config->fallbackImage; j++) {
1279     entry2 = findEntryByIndex(config, j);
1280     if (entry2->skip) config->fallbackImage--;
1281     }
1282     }
1283    
1284     void displayEntry(struct singleEntry * entry, const char * prefix, int index) {
1285     struct singleLine * line;
1286     char * root = NULL;
1287     int i;
1288    
1289     line = entry->lines;
1290     while (line && line->type != LT_KERNEL) line = line->next;
1291    
1292     printf("index=%d\n", index);
1293    
1294     printf("kernel=%s\n", line->elements[1].item);
1295    
1296     if (line->numElements >= 3) {
1297     printf("args=\"");
1298     i = 2;
1299     while (i < line->numElements) {
1300     if (!strncmp(line->elements[i].item, "root=", 5)) {
1301     root = line->elements[i].item + 5;
1302     } else {
1303     printf("%s%s", line->elements[i].item,
1304     line->elements[i].indent);
1305     }
1306    
1307     i++;
1308     }
1309     printf("\"\n");
1310     } else {
1311     line = entry->lines;
1312     while (line && line->type != LT_KERNELARGS) line=line->next;
1313    
1314     if (line) {
1315     char * s;
1316    
1317     printf("args=\"");
1318     i = 1;
1319     while (i < line->numElements) {
1320     if (!strncmp(line->elements[i].item, "root=", 5)) {
1321     root = line->elements[i].item + 5;
1322     } else {
1323     s = line->elements[i].item;
1324    
1325     printf("%s%s", s, line->elements[i].indent);
1326     }
1327    
1328     i++;
1329     }
1330    
1331     s = line->elements[i - 1].indent;
1332     printf("\"\n");
1333     }
1334     }
1335    
1336     if (!root) {
1337     line = entry->lines;
1338     while (line && line->type != LT_ROOT) line = line->next;
1339    
1340     if (line && line->numElements >= 2)
1341     root=line->elements[1].item;
1342     }
1343    
1344     if (root) {
1345     char * s = alloca(strlen(root) + 1);
1346    
1347     strcpy(s, root);
1348     if (s[strlen(s) - 1] == '"')
1349     s[strlen(s) - 1] = '\0';
1350     /* make sure the root doesn't have a trailing " */
1351     printf("root=%s\n", s);
1352     }
1353    
1354     line = entry->lines;
1355     while (line && line->type != LT_INITRD) line = line->next;
1356    
1357     if (line && line->numElements >= 2) {
1358     printf("initrd=%s", prefix);
1359     for (i = 1; i < line->numElements; i++)
1360     printf("%s%s", line->elements[i].item, line->elements[i].indent);
1361     printf("\n");
1362     }
1363     }
1364    
1365     int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
1366     FILE * in;
1367     char buf[1024];
1368     char * chptr;
1369     char * start;
1370     char * param;
1371    
1372     in = fopen("/etc/sysconfig/grub", "r");
1373     if (!in) return 1;
1374    
1375     if (lbaPtr) *lbaPtr = 0;
1376     if (bootPtr) *bootPtr = NULL;
1377    
1378     while (fgets(buf, sizeof(buf), in)) {
1379     start = buf;
1380     while (isspace(*start)) start++;
1381     if (*start == '#') continue;
1382    
1383     chptr = strchr(start, '=');
1384     if (!chptr) continue;
1385     chptr--;
1386     while (*chptr && isspace(*chptr)) chptr--;
1387     chptr++;
1388     *chptr = '\0';
1389    
1390     param = chptr + 1;
1391     while (*param && isspace(*param)) param++;
1392     if (*param == '=') {
1393     param++;
1394     while (*param && isspace(*param)) param++;
1395     }
1396    
1397     chptr = param;
1398     while (*chptr && !isspace(*chptr)) chptr++;
1399     *chptr = '\0';
1400    
1401     if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
1402     *lbaPtr = 1;
1403     else if (!strcmp(start, "boot") && bootPtr)
1404     *bootPtr = strdup(param);
1405     }
1406    
1407     fclose(in);
1408    
1409     return 0;
1410     }
1411    
1412     void dumpSysconfigGrub(void) {
1413     char * boot;
1414     int lba;
1415    
1416     if (!parseSysconfigGrub(&lba, &boot)) {
1417     if (lba) printf("lba\n");
1418     if (boot) printf("boot=%s\n", boot);
1419     }
1420     }
1421    
1422     int displayInfo(struct grubConfig * config, char * kernel,
1423     const char * prefix) {
1424     int i = 0;
1425     struct singleEntry * entry;
1426     struct singleLine * line;
1427    
1428     entry = findEntryByPath(config, kernel, prefix, &i);
1429     if (!entry) {
1430     fprintf(stderr, _("grubby: kernel not found\n"));
1431     return 1;
1432     }
1433    
1434     /* this is a horrible hack to support /etc/sysconfig/grub; there must
1435     be a better way */
1436     if (config->cfi == &grubConfigType) {
1437     dumpSysconfigGrub();
1438     } else {
1439     line = config->theLines;
1440     while (line && line->type != LT_BOOT) line = line->next;
1441     if (line && line->numElements >= 1) {
1442     printf("boot=%s\n", line->elements[1].item);
1443     }
1444    
1445     line = config->theLines;
1446     while (line && line->type != LT_LBA) line = line->next;
1447     if (line) printf("lba\n");
1448     }
1449    
1450     displayEntry(entry, prefix, i);
1451    
1452     i++;
1453     while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
1454     displayEntry(entry, prefix, i);
1455     i++;
1456     }
1457    
1458     return 0;
1459     }
1460    
1461     /* val may be NULL */
1462     struct singleLine * addLine(struct singleEntry * entry,
1463     struct configFileInfo * cfi,
1464     enum lineType_e type, const char * defaultIndent,
1465     char * val) {
1466     struct singleLine * line, * prev;
1467     int i;
1468    
1469     for (i = 0; cfi->keywords[i].key; i++)
1470     if (cfi->keywords[i].type == type) break;
1471     if (type != LT_TITLE || !cfi->titleBracketed)
1472     if (!cfi->keywords[i].key) abort();
1473    
1474     /* The last non-empty line gives us the indention to us and the line
1475     to insert after. Note that comments are considered empty lines, which
1476     may not be ideal? If there are no lines or we are looking at the
1477     first line, we use defaultIndent (the first line is normally indented
1478     differently from the rest) */
1479     if (entry->lines) {
1480     line = entry->lines;
1481     prev = NULL;
1482     while (line) {
1483     if (line->numElements) prev = line;
1484     line = line->next;
1485     }
1486     if (!prev) {
1487     /* just use the last line */
1488     prev = entry->lines;
1489     while (prev->next) prev = prev->next;
1490     }
1491    
1492     line = prev->next;
1493     prev->next = malloc(sizeof(*line));
1494     prev->next->next = line;
1495     line = prev->next;
1496    
1497     if (prev == entry->lines)
1498     line->indent = strdup(defaultIndent);
1499     else
1500     line->indent = strdup(prev->indent);
1501     } else {
1502     line = malloc(sizeof(*line));
1503     line->indent = strdup(defaultIndent);
1504     line->next = NULL;
1505     }
1506    
1507     if (type != LT_TITLE || !cfi->titleBracketed) {
1508     line->type = type;
1509     line->numElements = val ? 2 : 1;
1510     line->elements = malloc(sizeof(*line->elements) * line->numElements);
1511     line->elements[0].item = strdup(cfi->keywords[i].key);
1512     line->elements[0].indent = malloc(2);
1513     line->elements[0].indent[0] = cfi->keywords[i].nextChar;
1514     line->elements[0].indent[1] = '\0';
1515    
1516     if (val) {
1517     line->elements[1].item = val;
1518     line->elements[1].indent = strdup("");
1519     }
1520     } else {
1521     /* we're doing the title of a bracketed title (zipl) */
1522     line->type = type;
1523     line->numElements = 1;
1524     line->elements = malloc(sizeof(*line->elements) * line->numElements);
1525    
1526     line->elements[0].item = malloc(strlen(val) + 3);
1527     sprintf(line->elements[0].item, "[%s]", val);
1528     line->elements[0].indent = strdup("");
1529     }
1530    
1531     return line;
1532     }
1533    
1534     void removeLine(struct singleEntry * entry, struct singleLine * line) {
1535     struct singleLine * prev;
1536     int i;
1537    
1538     for (i = 0; i < line->numElements; i++) {
1539     free(line->elements[i].item);
1540     free(line->elements[i].indent);
1541     }
1542     free(line->elements);
1543     free(line->indent);
1544    
1545     if (line == entry->lines) {
1546     entry->lines = line->next;
1547     } else {
1548     prev = entry->lines;
1549     while (prev->next != line) prev = prev->next;
1550     prev->next = line->next;
1551     }
1552    
1553     free(line);
1554     }
1555    
1556     int argMatch(const char * one, const char * two) {
1557     char * first, * second;
1558     char * chptr;
1559    
1560     first = strcpy(alloca(strlen(one) + 1), one);
1561     second = strcpy(alloca(strlen(two) + 1), two);
1562    
1563     chptr = strchr(first, '=');
1564     if (chptr) *chptr = '\0';
1565    
1566     chptr = strchr(second, '=');
1567     if (chptr) *chptr = '\0';
1568    
1569     return strcmp(first, second);
1570     }
1571    
1572     int updateActualImage(struct grubConfig * cfg, const char * image,
1573     const char * prefix, const char * addArgs,
1574     const char * removeArgs, int multibootArgs) {
1575     struct singleEntry * entry;
1576     struct singleLine * line, * rootLine;
1577     int index = 0;
1578     int i, j, k;
1579     const char ** newArgs, ** oldArgs;
1580     const char ** arg;
1581     const char * chptr;
1582     int useKernelArgs = 0;
1583     int useRoot = 0;
1584     int firstElement;
1585     int *usedElements, *usedArgs;
1586    
1587     if (!image) return 0;
1588    
1589     if (!addArgs) {
1590     newArgs = malloc(sizeof(*newArgs));
1591     *newArgs = NULL;
1592     } else {
1593     if (poptParseArgvString(addArgs, NULL, &newArgs)) {
1594     fprintf(stderr,
1595     _("grubby: error separating arguments '%s'\n"), addArgs);
1596     return 1;
1597     }
1598     }
1599    
1600     if (!removeArgs) {
1601     oldArgs = malloc(sizeof(*oldArgs));
1602     *oldArgs = NULL;
1603     } else {
1604     if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
1605     fprintf(stderr,
1606     _("grubby: error separating arguments '%s'\n"), removeArgs);
1607     free(newArgs);
1608     return 1;
1609     }
1610     }
1611    
1612     for (i = 0; cfg->cfi->keywords[i].key; i++)
1613     if (cfg->cfi->keywords[i].type == LT_KERNELARGS) break;
1614    
1615     if (cfg->cfi->keywords[i].key)
1616     useKernelArgs = 1;
1617    
1618     for (i = 0; cfg->cfi->keywords[i].key; i++)
1619     if (cfg->cfi->keywords[i].type == LT_ROOT) break;
1620    
1621     if (cfg->cfi->keywords[i].key)
1622     useRoot = 1;
1623    
1624     k = 0;
1625     for (arg = newArgs; *arg; arg++)
1626     k++;
1627     usedArgs = calloc(k, sizeof(int));
1628    
1629     while ((entry = findEntryByPath(cfg, image, prefix, &index))) {
1630     index++;
1631    
1632     line = entry->lines;
1633     while (line && line->type != LT_KERNEL) line = line->next;
1634     if (!line) continue;
1635     firstElement = 2;
1636    
1637     if (entry->multiboot && !multibootArgs) {
1638     /* first mb module line is the real kernel */
1639     while (line && line->type != LT_MBMODULE) line = line->next;
1640     firstElement = 2;
1641     } else if (useKernelArgs) {
1642     while (line && line->type != LT_KERNELARGS) line = line->next;
1643     firstElement = 1;
1644     }
1645    
1646     if (!line && useKernelArgs) {
1647     /* no append in there, need to add it */
1648     line = addLine(entry, cfg->cfi, LT_KERNELARGS, NULL, NULL);
1649     }
1650    
1651     usedElements = calloc(line->numElements, sizeof(int));
1652    
1653     k = 0;
1654     for (arg = newArgs; *arg; arg++) {
1655     if (usedArgs[k]) {
1656     k++;
1657     continue;
1658     }
1659     for (i = firstElement; i < line->numElements; i++) {
1660     if (usedElements[i])
1661     continue;
1662     if (!argMatch(line->elements[i].item, *arg)) {
1663     usedElements[i]=1;
1664     usedArgs[k]=1;
1665     break;
1666     }
1667     }
1668     chptr = strchr(*arg, '=');
1669    
1670     if (i < line->numElements) {
1671     /* replace */
1672     free(line->elements[i].item);
1673     line->elements[i].item = strdup(*arg);
1674     } else if (useRoot && !strncmp(*arg, "root=/dev/", 10) && *chptr) {
1675     rootLine = entry->lines;
1676     while (rootLine && rootLine->type != LT_ROOT)
1677     rootLine = rootLine->next;
1678     if (!rootLine) {
1679     rootLine = addLine(entry, cfg->cfi, LT_ROOT, NULL, NULL);
1680     rootLine->elements = realloc(rootLine->elements,
1681     2 * sizeof(*rootLine->elements));
1682     rootLine->numElements++;
1683     rootLine->elements[1].indent = strdup("");
1684     rootLine->elements[1].item = strdup("");
1685     }
1686    
1687     free(rootLine->elements[1].item);
1688     rootLine->elements[1].item = strdup(chptr + 1);
1689     } else {
1690     /* append */
1691     line->elements = realloc(line->elements,
1692     (line->numElements + 1) * sizeof(*line->elements));
1693     line->elements[line->numElements].item = strdup(*arg);
1694     usedElements = realloc(usedElements,
1695     (line->numElements + 1) * sizeof(int));
1696     usedElements[line->numElements] = 1;
1697    
1698     if (line->numElements > 1) {
1699     /* add to existing list of arguments */
1700     line->elements[line->numElements].indent =
1701     line->elements[line->numElements - 1].indent;
1702     line->elements[line->numElements - 1].indent = strdup(" ");
1703     } else {
1704     /* First thing on this line; treat a bit differently. Note
1705     this is only possible if we've added a LT_KERNELARGS
1706     entry */
1707     line->elements[line->numElements].indent = strdup("");
1708     }
1709    
1710     line->numElements++;
1711    
1712     /* if we updated a root= here even though there is a
1713     LT_ROOT available we need to remove the LT_ROOT entry
1714     (this will happen if we switch from a device to a label) */
1715     if (useRoot && !strncmp(*arg, "root=", 5)) {
1716     rootLine = entry->lines;
1717     while (rootLine && rootLine->type != LT_ROOT)
1718     rootLine = rootLine->next;
1719     if (rootLine) {
1720     removeLine(entry, rootLine);
1721     }
1722     }
1723     }
1724     k++;
1725     }
1726    
1727     free(usedElements);
1728    
1729     /* no arguments to remove (i.e. no append line) */
1730     if (!line) continue;
1731    
1732     /* this won't remove an LT_ROOT item properly (but then again,
1733     who cares? */
1734     for (arg = oldArgs; *arg; arg++) {
1735     for (i = firstElement; i < line->numElements; i++)
1736     if (!argMatch(line->elements[i].item, *arg))
1737     break;
1738    
1739     if (i < line->numElements) {
1740     /* if this isn't the first argument the previous argument
1741     gets this arguments post-indention */
1742     if (i > firstElement) {
1743     free(line->elements[i - 1].indent);
1744     line->elements[i - 1].indent = line->elements[i].indent;
1745     }
1746    
1747     free(line->elements[i].item);
1748    
1749     for (j = i + 1; j < line->numElements; j++)
1750     line->elements[j - 1] = line->elements[j];
1751    
1752     line->numElements--;
1753     }
1754     }
1755    
1756     if (line->numElements == 1) {
1757     /* don't need the line at all (note it has to be a
1758     LT_KERNELARGS for this to happen */
1759     removeLine(entry, line);
1760     }
1761     }
1762    
1763     free(usedArgs);
1764     free(newArgs);
1765     free(oldArgs);
1766    
1767     return 0;
1768     }
1769    
1770     int updateImage(struct grubConfig * cfg, const char * image,
1771     const char * prefix, const char * addArgs,
1772     const char * removeArgs,
1773     const char * addMBArgs, const char * removeMBArgs) {
1774     int rc = 0;
1775    
1776     if (!image) return rc;
1777    
1778     /* update the main args first... */
1779     if (addArgs || removeArgs)
1780     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);
1781     if (rc) return rc;
1782    
1783     /* and now any multiboot args */
1784     if (addMBArgs || removeMBArgs)
1785     rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);
1786     return rc;
1787     }
1788    
1789     int checkDeviceBootloader(const char * device, const unsigned char * boot) {
1790     int fd;
1791     unsigned char bootSect[512];
1792     int offset;
1793    
1794     fd = open(device, O_RDONLY);
1795     if (fd < 0) {
1796     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
1797     device, strerror(errno));
1798     return 1;
1799     }
1800    
1801     if (read(fd, bootSect, 512) != 512) {
1802     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
1803     device, strerror(errno));
1804     return 1;
1805     }
1806     close(fd);
1807    
1808     /* first three bytes should match, a jmp short should be in there */
1809     if (memcmp(boot, bootSect, 3))
1810     return 0;
1811    
1812     if (boot[1] == 0xeb) {
1813     offset = boot[2] + 2;
1814     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
1815     offset = (boot[3] << 8) + boot[2] + 2;
1816     } else if (boot[0] == 0xeb) {
1817     offset = boot[1] + 2;
1818     } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
1819     offset = (boot[2] << 8) + boot[1] + 2;
1820     } else {
1821     return 0;
1822     }
1823    
1824     if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
1825     return 0;
1826    
1827     return 2;
1828     }
1829    
1830     int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {
1831     int fd;
1832     char buf[65536];
1833     char * end;
1834     char * chptr;
1835     char * chptr2;
1836     int rc;
1837    
1838     /* it's on raid; we need to parse /proc/mdstat and check all of the
1839     *raw* devices listed in there */
1840    
1841     if (!strncmp(mdDev, "/dev/", 5))
1842     mdDev += 5;
1843    
1844     if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
1845     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
1846     strerror(errno));
1847     return 2;
1848     }
1849    
1850     rc = read(fd, buf, sizeof(buf) - 1);
1851     if (rc < 0 || rc == (sizeof(buf) - 1)) {
1852     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
1853     strerror(errno));
1854     close(fd);
1855     return 2;
1856     }
1857     close(fd);
1858     buf[rc] = '\0';
1859    
1860     chptr = buf;
1861     while (*chptr) {
1862     end = strchr(chptr, '\n');
1863     if (!end) break;
1864     *end = '\0';
1865    
1866     if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
1867     chptr[strlen(mdDev)] == ' ') {
1868    
1869     /* found the device */
1870     while (*chptr && *chptr != ':') chptr++;
1871     chptr++;
1872     while (*chptr && isspace(*chptr)) chptr++;
1873    
1874     /* skip the "active" bit */
1875     while (*chptr && !isspace(*chptr)) chptr++;
1876     while (*chptr && isspace(*chptr)) chptr++;
1877    
1878     /* skip the raid level */
1879     while (*chptr && !isspace(*chptr)) chptr++;
1880     while (*chptr && isspace(*chptr)) chptr++;
1881    
1882     /* everything else is partition stuff */
1883     while (*chptr) {
1884     chptr2 = chptr;
1885     while (*chptr2 && *chptr2 != '[') chptr2++;
1886     if (!*chptr2) break;
1887    
1888     /* yank off the numbers at the end */
1889     chptr2--;
1890     while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;
1891     chptr2++;
1892     *chptr2 = '\0';
1893    
1894     /* Better, now we need the /dev/ back. We're done with
1895     * everything before this point, so we can just put
1896     * the /dev/ part there. There will always be room. */
1897     memcpy(chptr - 5, "/dev/", 5);
1898     rc = checkDeviceBootloader(chptr - 5, boot);
1899     if (rc != 2) {
1900     return rc;
1901     }
1902    
1903     chptr = chptr2 + 1;
1904     /* skip the [11] bit */
1905     while (*chptr && !isspace(*chptr)) chptr++;
1906     /* and move to the next one */
1907     while (*chptr && isspace(*chptr)) chptr++;
1908     }
1909    
1910     /* we're good to go */
1911     return 2;
1912     }
1913    
1914     chptr = end + 1;
1915     }
1916    
1917     fprintf(stderr,
1918     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
1919     mdDev);
1920     return 0;
1921     }
1922    
1923     int checkForLilo(struct grubConfig * config) {
1924     int fd;
1925     unsigned char boot[512];
1926     struct singleLine * line;
1927    
1928     for (line = config->theLines; line; line = line->next)
1929     if (line->type == LT_BOOT) break;
1930    
1931     if (!line) {
1932     fprintf(stderr,
1933     _("grubby: no boot line found in lilo configuration\n"));
1934     return 1;
1935     }
1936    
1937     if (line->numElements != 2) return 1;
1938    
1939     fd = open("/boot/boot.b", O_RDONLY);
1940     if (fd < 0) {
1941     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
1942     "/boot/boot.b", strerror(errno));
1943     return 1;
1944     }
1945    
1946     if (read(fd, boot, 512) != 512) {
1947     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
1948     "/boot/boot.b", strerror(errno));
1949     return 1;
1950     }
1951     close(fd);
1952    
1953     if (!strncmp("/dev/md", line->elements[1].item, 7))
1954     return checkLiloOnRaid(line->elements[1].item, boot);
1955    
1956     return checkDeviceBootloader(line->elements[1].item, boot);
1957     }
1958    
1959     int checkForGrub(struct grubConfig * config) {
1960     int fd;
1961     unsigned char bootSect[512];
1962     char * boot;
1963    
1964     if (parseSysconfigGrub(NULL, &boot))
1965     return 0;
1966    
1967     /* assume grub is not installed -- not an error condition */
1968     if (!boot)
1969     return 0;
1970    
1971     fd = open("/boot/grub/stage1", O_RDONLY);
1972     if (fd < 0)
1973     /* this doesn't exist if grub hasn't been installed */
1974     return 0;
1975    
1976     if (read(fd, bootSect, 512) != 512) {
1977     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
1978     "/boot/grub/stage1", strerror(errno));
1979     return 1;
1980     }
1981     close(fd);
1982    
1983     return checkDeviceBootloader(boot, bootSect);
1984     }
1985    
1986     static char * getRootSpecifier(char * str) {
1987     char * idx, * rootspec = NULL;
1988    
1989     if (*str == '(') {
1990     idx = rootspec = strdup(str);
1991     while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;
1992     *(++idx) = '\0';
1993     }
1994     return rootspec;
1995     }
1996    
1997     int addNewKernel(struct grubConfig * config, struct singleEntry * template,
1998     const char * prefix,
1999     char * newKernelPath, char * newKernelTitle,
2000     char * newKernelArgs, char * newKernelInitrd,
2001     char * newMBKernel, char * newMBKernelArgs) {
2002     struct singleEntry * new;
2003     struct singleLine * newLine = NULL, * tmplLine = NULL, * lastLine = NULL;
2004     int needs;
2005     char * indent = NULL;
2006     char * rootspec = NULL;
2007     char * chptr;
2008     int i;
2009     enum lineType_e type;
2010    
2011     if (!newKernelPath) return 0;
2012    
2013     /* if the newKernelTitle is too long silently munge it into something
2014     * we can live with. truncating is first check, then we'll just mess with
2015     * it until it looks better */
2016     if (config->cfi->maxTitleLength &&
2017     (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
2018     char * buf = alloca(config->cfi->maxTitleLength + 7);
2019     char * numBuf = alloca(config->cfi->maxTitleLength + 1);
2020     int i = 1;
2021    
2022     sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);
2023     while (findEntryByPath(config, buf, NULL, NULL)) {
2024     sprintf(numBuf, "%d", i++);
2025     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
2026     }
2027    
2028     newKernelTitle = buf + 6;
2029     }
2030    
2031     new = malloc(sizeof(*new));
2032     new->skip = 0;
2033     new->multiboot = 0;
2034     new->next = config->entries;
2035     new->lines = NULL;
2036     config->entries = new;
2037    
2038     /* copy/update from the template */
2039     needs = KERNEL_KERNEL | KERNEL_INITRD | KERNEL_TITLE;
2040     if (newMBKernel) {
2041     needs |= KERNEL_MB;
2042     new->multiboot = 1;
2043     }
2044    
2045     if (template) {
2046     for (tmplLine = template->lines; tmplLine; tmplLine = tmplLine->next) {
2047     /* remember the indention level; we may need it for new lines */
2048     if (tmplLine->numElements)
2049     indent = tmplLine->indent;
2050    
2051     /* skip comments */
2052     chptr = tmplLine->indent;
2053     while (*chptr && isspace(*chptr)) chptr++;
2054     if (*chptr == '#') continue;
2055    
2056     /* we don't need an initrd here */
2057     if (tmplLine->type == LT_INITRD && !newKernelInitrd) continue;
2058    
2059     if (tmplLine->type == LT_KERNEL &&
2060     !template->multiboot && (needs & KERNEL_MB)) {
2061     struct singleLine *l;
2062     needs &= ~ KERNEL_MB;
2063    
2064     l = addLine(new, config->cfi, LT_KERNEL,
2065     config->secondaryIndent,
2066     newMBKernel + strlen(prefix));
2067    
2068     tmplLine = lastLine;
2069     if (!new->lines) {
2070     new->lines = l;
2071     } else {
2072     newLine->next = l;
2073     newLine = l;
2074     }
2075     continue;
2076     } else if (tmplLine->type == LT_KERNEL &&
2077     template->multiboot && !new->multiboot) {
2078     continue; /* don't need multiboot kernel here */
2079     }
2080    
2081     if (!new->lines) {
2082     newLine = malloc(sizeof(*newLine));
2083     new->lines = newLine;
2084     } else {
2085     newLine->next = malloc(sizeof(*newLine));
2086     newLine = newLine->next;
2087     }
2088    
2089    
2090     newLine->indent = strdup(tmplLine->indent);
2091     newLine->next = NULL;
2092     newLine->type = tmplLine->type;
2093     newLine->numElements = tmplLine->numElements;
2094     newLine->elements = malloc(sizeof(*newLine->elements) *
2095     newLine->numElements);
2096     for (i = 0; i < newLine->numElements; i++) {
2097     newLine->elements[i].item = strdup(tmplLine->elements[i].item);
2098     newLine->elements[i].indent =
2099     strdup(tmplLine->elements[i].indent);
2100     }
2101    
2102     lastLine = tmplLine;
2103     if (tmplLine->type == LT_KERNEL && tmplLine->numElements >= 2) {
2104     char * repl;
2105     if (!template->multiboot) {
2106     needs &= ~KERNEL_KERNEL;
2107     repl = newKernelPath;
2108     } else {
2109     needs &= ~KERNEL_MB;
2110     repl = newMBKernel;
2111     }
2112     if (new->multiboot && !template->multiboot) {
2113     free(newLine->elements[0].item);
2114     newLine->elements[0].item = strdup("module");
2115     newLine->type = LT_MBMODULE;
2116     }
2117     free(newLine->elements[1].item);
2118     rootspec = getRootSpecifier(tmplLine->elements[1].item);
2119     if (rootspec != NULL) {
2120     newLine->elements[1].item = sdupprintf("%s%s",
2121     rootspec,
2122     repl +
2123     strlen(prefix));
2124     } else {
2125     newLine->elements[1].item = strdup(repl +
2126     strlen(prefix));
2127     }
2128     } else if (tmplLine->type == LT_MBMODULE &&
2129     tmplLine->numElements >= 2 && (needs & KERNEL_KERNEL)) {
2130     needs &= ~KERNEL_KERNEL;
2131     if (!new->multiboot && template->multiboot) {
2132     free(newLine->elements[0].item);
2133     newLine->elements[0].item = strdup("kernel");
2134     newLine->type = LT_KERNEL;
2135     }
2136     free(newLine->elements[1].item);
2137     rootspec = getRootSpecifier(tmplLine->elements[1].item);
2138     if (rootspec != NULL) {
2139     newLine->elements[1].item = sdupprintf("%s%s",
2140     rootspec,
2141     newKernelPath +
2142     strlen(prefix));
2143     } else {
2144     newLine->elements[1].item = strdup(newKernelPath +
2145     strlen(prefix));
2146     }
2147     } else if (tmplLine->type == LT_INITRD &&
2148     tmplLine->numElements >= 2) {
2149     needs &= ~KERNEL_INITRD;
2150     free(newLine->elements[1].item);
2151     if (new->multiboot && !template->multiboot) {
2152     free(newLine->elements[0].item);
2153     newLine->elements[0].item = strdup("module");
2154     newLine->type = LT_MBMODULE;
2155     }
2156     rootspec = getRootSpecifier(tmplLine->elements[1].item);
2157     if (rootspec != NULL) {
2158     newLine->elements[1].item = sdupprintf("%s%s",
2159     rootspec,
2160     newKernelInitrd +
2161     strlen(prefix));
2162     } else {
2163     newLine->elements[1].item = strdup(newKernelInitrd +
2164     strlen(prefix));
2165     }
2166     } else if (tmplLine->type == LT_MBMODULE &&
2167     tmplLine->numElements >= 2 && (needs & KERNEL_INITRD)) {
2168     needs &= ~KERNEL_INITRD;
2169     if (!new->multiboot && template->multiboot) {
2170     free(newLine->elements[0].item);
2171     newLine->elements[0].item = strdup("initrd");
2172     newLine->type = LT_INITRD;
2173     }
2174     free(newLine->elements[1].item);
2175     rootspec = getRootSpecifier(tmplLine->elements[1].item);
2176     if (rootspec != NULL) {
2177     newLine->elements[1].item = sdupprintf("%s%s",
2178     rootspec,
2179     newKernelInitrd +
2180     strlen(prefix));
2181     } else {
2182     newLine->elements[1].item = strdup(newKernelInitrd +
2183     strlen(prefix));
2184     }
2185     } else if (tmplLine->type == LT_TITLE &&
2186     tmplLine->numElements >= 2) {
2187     needs &= ~KERNEL_TITLE;
2188    
2189     for (i = 1; i < newLine->numElements; i++) {
2190     free(newLine->elements[i].item);
2191     free(newLine->elements[i].indent);
2192     }
2193    
2194     newLine->elements[1].item = strdup(newKernelTitle);
2195     newLine->elements[1].indent = strdup("");
2196     newLine->numElements = 2;
2197     } else if (tmplLine->type == LT_TITLE &&
2198     config->cfi->titleBracketed &&
2199     tmplLine->numElements == 1) {
2200     needs &= ~KERNEL_TITLE;
2201     free(newLine->elements[0].item);
2202     free(newLine->elements[0].indent);
2203     newLine->elements = malloc(sizeof(*newLine->elements) *
2204     newLine->numElements);
2205    
2206     newLine->elements[0].item = malloc(strlen(newKernelTitle) + 3);
2207     sprintf(newLine->elements[0].item, "[%s]", newKernelTitle);
2208     newLine->elements[0].indent = strdup("");
2209     newLine->numElements = 1;
2210     }
2211     }
2212     } else {
2213     for (i = 0; config->cfi->keywords[i].key; i++) {
2214     if ((config->cfi->keywords[i].type == config->cfi->entrySeparator) || (config->cfi->keywords[i].type == LT_OTHER))
2215     break;
2216     }
2217    
2218     switch (config->cfi->keywords[i].type) {
2219     case LT_KERNEL: needs &= ~KERNEL_KERNEL,
2220     chptr = newKernelPath + strlen(prefix);
2221     type = LT_KERNEL; break;
2222     case LT_TITLE: needs &= ~KERNEL_TITLE, chptr = newKernelTitle;
2223     type = LT_TITLE; break;
2224     default:
2225     /* zipl strikes again */
2226     if (config->cfi->titleBracketed) {
2227     needs &= ~KERNEL_TITLE;
2228     chptr = newKernelTitle;
2229     type = LT_TITLE;
2230     break;
2231     } else {
2232     abort();
2233     }
2234     }
2235    
2236     newLine = addLine(new, config->cfi, type, config->primaryIndent, chptr);
2237     new->lines = newLine;
2238     }
2239    
2240     if (new->multiboot) {
2241     if (needs & KERNEL_MB)
2242     newLine = addLine(new, config->cfi, LT_KERNEL,
2243     config->secondaryIndent,
2244     newMBKernel + strlen(prefix));
2245     if (needs & KERNEL_KERNEL)
2246     newLine = addLine(new, config->cfi, LT_MBMODULE,
2247     config->secondaryIndent,
2248     newKernelPath + strlen(prefix));
2249     /* don't need to check for title as it's guaranteed to have been
2250     * done as we only do multiboot with grub which uses title as
2251     * a separator */
2252     if (needs & KERNEL_INITRD && newKernelInitrd)
2253     newLine = addLine(new, config->cfi, LT_MBMODULE,
2254     config->secondaryIndent,
2255     newKernelInitrd + strlen(prefix));
2256     } else {
2257     if (needs & KERNEL_KERNEL)
2258     newLine = addLine(new, config->cfi, LT_KERNEL,
2259     config->secondaryIndent,
2260     newKernelPath + strlen(prefix));
2261     if (needs & KERNEL_TITLE)
2262     newLine = addLine(new, config->cfi, LT_TITLE,
2263     config->secondaryIndent,
2264     newKernelTitle);
2265     if (needs & KERNEL_INITRD && newKernelInitrd)
2266     newLine = addLine(new, config->cfi, LT_INITRD,
2267     config->secondaryIndent,
2268     newKernelInitrd + strlen(prefix));
2269     }
2270    
2271     if (updateImage(config, "0", prefix, newKernelArgs, NULL,
2272     newMBKernelArgs, NULL)) return 1;
2273    
2274     return 0;
2275     }
2276    
2277     int main(int argc, const char ** argv) {
2278     poptContext optCon;
2279     char * grubConfig = NULL;
2280     char * outputFile = NULL;
2281     int arg = 0;
2282     int flags = 0;
2283     int badImageOkay = 0;
2284     int configureLilo = 0, configureELilo = 0, configureGrub = 0;
2285     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
2286     int bootloaderProbe = 0;
2287     char * updateKernelPath = NULL;
2288     char * newKernelPath = NULL;
2289     char * removeKernelPath = NULL;
2290     char * newKernelArgs = NULL;
2291     char * newKernelInitrd = NULL;
2292     char * newKernelTitle = NULL;
2293     char * newKernelVersion = NULL;
2294     char * newMBKernel = NULL;
2295     char * newMBKernelArgs = NULL;
2296     char * removeMBKernelArgs = NULL;
2297     char * removeMBKernel = NULL;
2298     char * bootPrefix = NULL;
2299     char * defaultKernel = NULL;
2300     char * removeArgs = NULL;
2301     char * kernelInfo = NULL;
2302     const char * chptr = NULL;
2303     struct configFileInfo * cfi = NULL;
2304     struct grubConfig * config;
2305     struct singleEntry * template = NULL;
2306     int copyDefault = 0, makeDefault = 0;
2307     int displayDefault = 0;
2308     struct poptOption options[] = {
2309     { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
2310     _("add an entry for the specified kernel"), _("kernel-path") },
2311     { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
2312     _("add an entry for the specified multiboot kernel"), NULL },
2313     { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
2314     _("default arguments for the new kernel or new arguments for "
2315     "kernel being updated"), _("args") },
2316     { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
2317     _("default arguments for the new multiboot kernel or "
2318     "new arguments for multiboot kernel being updated"), NULL },
2319     { "bad-image-okay", 0, 0, &badImageOkay, 0,
2320     _("don't sanity check images in boot entries (for testing only)"),
2321     NULL },
2322     { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
2323     _("filestystem which contains /boot directory (for testing only)"),
2324     _("bootfs") },
2325     #if defined(__i386__) || defined(__x86_64__)
2326     { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
2327     _("check if lilo is installed on lilo.conf boot sector") },
2328     #endif
2329     { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
2330     _("path to grub config file to update (\"-\" for stdin)"),
2331     _("path") },
2332     { "copy-default", 0, 0, &copyDefault, 0,
2333     _("use the default boot entry as a template for the new entry "
2334     "being added; if the default is not a linux image, or if "
2335     "the kernel referenced by the default image does not exist, "
2336     "the first linux entry whose kernel does exist is used as the "
2337     "template"), NULL },
2338     { "default-kernel", 0, 0, &displayDefault, 0,
2339     _("display the path of the default kernel") },
2340     { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
2341     _("configure elilo bootloader") },
2342     { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
2343     _("configure grub bootloader") },
2344     { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
2345     _("display boot information for specified kernel"),
2346     _("kernel-path") },
2347     { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
2348     _("initrd image for the new kernel"), _("initrd-path") },
2349     { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
2350     _("configure lilo bootloader") },
2351     { "make-default", 0, 0, &makeDefault, 0,
2352     _("make the newly added entry the default boot entry"), NULL },
2353     { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
2354     _("path to output updated config file (\"-\" for stdout)"),
2355     _("path") },
2356     { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
2357     _("remove kernel arguments"), NULL },
2358     { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
2359     _("remove multiboot kernel arguments"), NULL },
2360     { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
2361     _("remove all entries for the specified kernel"),
2362     _("kernel-path") },
2363     { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
2364     _("remove all entries for the specified multiboot kernel"), NULL },
2365     { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
2366     _("make the first entry referencing the specified kernel "
2367     "the default"), _("kernel-path") },
2368     { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
2369     _("configure silo bootloader") },
2370     { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
2371     _("title to use for the new kernel entry"), _("entry-title") },
2372     { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
2373     _("updated information for the specified kernel"),
2374     _("kernel-path") },
2375     { "version", 'v', 0, NULL, 'v',
2376     _("print the version of this program and exit"), NULL },
2377     { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
2378     _("configure yaboot bootloader") },
2379     { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
2380     _("configure zipl bootloader") },
2381     POPT_AUTOHELP
2382     { 0, 0, 0, 0, 0 }
2383     };
2384    
2385     optCon = poptGetContext("grubby", argc, argv, options, 0);
2386     poptReadDefaultConfig(optCon, 1);
2387    
2388     while ((arg = poptGetNextOpt(optCon)) >= 0) {
2389     switch (arg) {
2390     case 'v':
2391     printf("grubby version %s\n", VERSION);
2392     exit(0);
2393     break;
2394     }
2395     }
2396    
2397     if (arg < -1) {
2398     fprintf(stderr, _("grubby: bad argument %s: %s\n"),
2399     poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
2400     poptStrerror(arg));
2401     return 1;
2402     }
2403    
2404     if ((chptr = poptGetArg(optCon))) {
2405     fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
2406     return 1;
2407     }
2408    
2409     if ((configureLilo + configureGrub + configureELilo +
2410     configureYaboot + configureSilo + configureZipl) > 1) {
2411     fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
2412     return 1;
2413     } else if (bootloaderProbe && grubConfig) {
2414     fprintf(stderr,
2415     _("grubby: cannot specify config file with --bootloader-probe\n"));
2416     return 1;
2417     } else if (configureLilo) {
2418     cfi = &liloConfigType;
2419     } else if (configureGrub) {
2420     cfi = &grubConfigType;
2421     } else if (configureELilo) {
2422     cfi = &eliloConfigType;
2423     } else if (configureYaboot) {
2424     cfi = &yabootConfigType;
2425     } else if (configureSilo) {
2426     cfi = &siloConfigType;
2427     } else if (configureZipl) {
2428     cfi = &ziplConfigType;
2429     }
2430    
2431     if (!cfi) {
2432     #ifdef __ia64__
2433     cfi = &eliloConfigType;
2434     #elif __powerpc__
2435     cfi = &yabootConfigType;
2436     #elif __sparc__
2437     cfi = &siloConfigType;
2438     #elif __s390__
2439     cfi = &ziplConfigType;
2440     #elif __s390x__
2441     cfi = &ziplConfigtype;
2442     #else
2443     cfi = &grubConfigType;
2444     #endif
2445     }
2446    
2447     if (!grubConfig)
2448     grubConfig = cfi->defaultConfig;
2449    
2450     if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||
2451     newKernelPath || removeKernelPath || makeDefault ||
2452     defaultKernel)) {
2453     fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
2454     "specified option"));
2455     return 1;
2456     }
2457    
2458     if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||
2459     removeKernelPath)) {
2460     fprintf(stderr, _("grubby: --default-kernel and --info may not "
2461     "be used when adding or removing kernels\n"));
2462     return 1;
2463     }
2464    
2465     if (newKernelPath && !newKernelTitle) {
2466     fprintf(stderr, _("grubby: kernel title must be specified\n"));
2467     return 1;
2468     } else if (!newKernelPath && (newKernelTitle || newKernelInitrd ||
2469     newKernelInitrd || copyDefault ||
2470     makeDefault)) {
2471     fprintf(stderr, _("grubby: kernel path expected\n"));
2472     return 1;
2473     }
2474    
2475     if (newKernelPath && updateKernelPath) {
2476     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
2477     "not be used together"));
2478     return 1;
2479     }
2480    
2481     if (makeDefault && defaultKernel) {
2482     fprintf(stderr, _("grubby: --make-default and --default-kernel "
2483     "may not be used together\n"));
2484     return 1;
2485     } else if (defaultKernel && removeKernelPath &&
2486     !strcmp(defaultKernel, removeKernelPath)) {
2487     fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));
2488     return 1;
2489     } else if (defaultKernel && newKernelPath &&
2490     !strcmp(defaultKernel, newKernelPath)) {
2491     makeDefault = 1;
2492     defaultKernel = NULL;
2493     }
2494    
2495     if (!strcmp(grubConfig, "-") && !outputFile) {
2496     fprintf(stderr, _("grubby: output file must be specified if stdin "
2497     "is used\n"));
2498     return 1;
2499     }
2500    
2501     if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
2502     && !kernelInfo && !bootloaderProbe && !updateKernelPath
2503     && !removeMBKernel) {
2504     fprintf(stderr, _("grubby: no action specified\n"));
2505     return 1;
2506     }
2507    
2508     flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
2509    
2510     if (cfi->needsBootPrefix) {
2511     if (!bootPrefix) {
2512     bootPrefix = findBootPrefix();
2513     if (!bootPrefix) return 1;
2514     } else {
2515     /* this shouldn't end with a / */
2516     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
2517     bootPrefix[strlen(bootPrefix) - 1] = '\0';
2518     }
2519     } else {
2520     bootPrefix = "";
2521     }
2522    
2523     if (bootloaderProbe) {
2524     int lrc = 0, grc = 0;
2525     struct grubConfig * lconfig, * gconfig;
2526    
2527     if (!access(grubConfigType.defaultConfig, F_OK)) {
2528     gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType);
2529     if (!gconfig)
2530     grc = 1;
2531     else
2532     grc = checkForGrub(gconfig);
2533     }
2534    
2535     if (!access(liloConfigType.defaultConfig, F_OK)) {
2536     lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);
2537     if (!lconfig)
2538     lrc = 1;
2539     else
2540     lrc = checkForLilo(lconfig);
2541     }
2542    
2543     if (lrc == 1 || grc == 1) return 1;
2544    
2545     if (lrc == 2) printf("lilo\n");
2546     if (grc == 2) printf("grub\n");
2547    
2548     return 0;
2549     }
2550    
2551     config = readConfig(grubConfig, cfi);
2552     if (!config) return 1;
2553    
2554     if (displayDefault) {
2555     struct singleLine * line;
2556     struct singleEntry * entry;
2557     char * rootspec;
2558    
2559     if (config->defaultImage == -1) return 0;
2560     entry = findEntryByIndex(config, config->defaultImage);
2561     if (!entry) return 0;
2562     if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
2563    
2564     line = entry->lines;
2565     while (line && line->type != LT_KERNEL) line = line->next;
2566     if (!line) return 0;
2567    
2568     rootspec = getRootSpecifier(line->elements[1].item);
2569     printf("%s%s\n", bootPrefix, line->elements[1].item +
2570     ((rootspec != NULL) ? strlen(rootspec) : 0));
2571    
2572     return 0;
2573     } else if (kernelInfo)
2574     return displayInfo(config, kernelInfo, bootPrefix);
2575    
2576     if (copyDefault) {
2577     template = findTemplate(config, bootPrefix, NULL, 0, flags);
2578     if (!template) return 1;
2579     }
2580    
2581     markRemovedImage(config, removeKernelPath, bootPrefix);
2582     markRemovedImage(config, removeMBKernel, bootPrefix);
2583     setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
2584     bootPrefix, flags);
2585     setFallbackImage(config, newKernelPath != NULL);
2586     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
2587     removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
2588     if (addNewKernel(config, template, bootPrefix, newKernelPath,
2589     newKernelTitle, newKernelArgs, newKernelInitrd,
2590     newMBKernel, newMBKernelArgs)) return 1;
2591    
2592    
2593     if (numEntries(config) == 0) {
2594     fprintf(stderr, _("grubby: doing this would leave no kernel entries. "
2595     "Not writing out new config.\n"));
2596     return 1;
2597     }
2598    
2599     if (!outputFile)
2600     outputFile = grubConfig;
2601    
2602     return writeConfig(config, outputFile, bootPrefix);
2603     }