Adjust memory layout for 2.6.22+ kernels with 32KB setup code
[mknbi.git] / bootmenu.c
1 #include "stddef.h"
2 #include "string.h"
3 #include "ansiesc.h"
4 #include "printf.h"
5 #include "md5.h"
6 #include "misc.h"
7 #include "etherboot.h"
8 #include "start32.h"
9
10 static int              menutmo = 10, menudefault  = 0;
11
12 #ifdef  MOTD
13 /**************************************************************************
14 SHOW_MOTD - display the message of the day
15 **************************************************************************/
16 void show_motd(unsigned char *motd[])
17 {
18         unsigned char   *ptr;
19         int             i, j = 0;
20
21         for (i = 0; i < RFC1533_VENDOR_NUMOFMOTD; i++) if (motd[i]) {
22                 for (j = TAG_LEN(motd[i]), ptr = motd[i]+2; j-- && *ptr; )
23                         putchar(*ptr++);
24                 putchar('\n');
25         }
26 }
27 #endif
28
29 #if     !defined(ANSIESC) || defined(CONSOLE_SERIAL)
30 static const char *const clrline = "                                                                               ";
31 #endif
32
33 static int getoptvalue(unsigned char **ptr, int *len, int *rc)
34 {
35         unsigned char *tmp,*old;
36         int  i,l;
37
38         for (tmp = *ptr, l = *len; *tmp != '='; tmp++, l--);
39         old = ++tmp; l--;
40         if (!*tmp || *tmp == ':' || l <= 0)
41                 return (0);
42         i = getdecopt(&tmp, &l);
43         if (tmp == old)
44                 return (0);
45         *rc = i;
46         *len = l;
47         *ptr = tmp;
48         return (1);
49 }
50
51 /**************************************************************************
52 PARSE_MENUOPTS - parse menu options and set control variables
53 **************************************************************************/
54 void parse_menuopts(unsigned char *opt, int len)
55 {
56         /* This code can handle that "bootpd" terminates the control string
57            with a '\000' character, which violates the standard.  */
58         while (len > 0 && *opt) {
59                 if (!memcmp(opt,"timeout=",8)) {
60                         if (!getoptvalue(&opt, &len, &menutmo))
61                                 return; }
62                 else if (!memcmp(opt,"default=",8)) {
63                         if (!getoptvalue(&opt, &len, &menudefault))
64                                 return; }
65                 while (len > 0 && *opt != ':') { opt++; len--; }
66                 while (len > 0 && *opt == ':') { opt++; len--; }
67         }
68 }
69
70 /**************************************************************************
71 GETPARMS - get user provided parameters
72 **************************************************************************/
73
74 #ifdef  USRPARMS
75 static int getparms(struct bootpd_t *bootp, unsigned char *buffer)
76 {
77         unsigned char *ptr;
78         int  ch, i;
79
80         if (!buffer)
81                 return (0);
82         i = strlen(buffer);
83         ptr = buffer + i;
84 restart:
85 #if     defined(ANSIESC) && !defined(CONSOLE_SERIAL)
86         printf("\rParams: \033[K");
87 #else
88         printf("%s%s%s","\rParams: ",clrline+8,"\rParams: ");
89 #endif
90         for (ch = 0; ch < i; ch++)
91                 putchar(buffer[ch]);
92         for (;;) {
93 #if     defined(ANSIESC) && defined(CONSOLE_CRT)
94                 enable_cursor(1);
95                 ch = getchar();
96                 enable_cursor(0);
97 #else
98                 ch = getchar();
99 #endif
100                 if (ch == '\n')
101                         break;
102                 if (ch == ('U'&0x1F)) {
103                         ptr -= i;
104                         i = 0;
105                         goto restart; }
106                 if (ch == ('H'&0x1F)) {
107                         if (i) {
108                                 i--; ptr--;
109                                 printf("\010 \010"); }
110                         continue; }
111                 if (ch == ('L'&0x1F))
112                         goto restart;
113                 if (i == 255 || (signed char)ch < (signed char)' ')
114                         continue;
115                 putchar(*ptr++ = ch);
116                 i++; }
117         putchar('\n');
118         return (i);
119 }
120 #endif
121
122 /**************************************************************************
123 GETHEX - compute one hex byte
124 **************************************************************************/
125 #ifdef  PASSWD
126 static int gethex(unsigned char *dat)
127 {
128         int  i,j;
129
130         i = (j = *dat) > '9' ? (j+9) & 0xF : j - '0';
131         dat++;
132         return (16*i + ((j = *dat) > '9' ? (j+9) & 0xF : j - '0'));
133 }
134 #endif
135
136 /**************************************************************************
137 SELECTIMAGE - interactively ask the user for the boot image
138 **************************************************************************/
139
140 /* Warning! GCC 2.7.2 has difficulties with analyzing the data flow in
141    the following function; it will sometimes clobber some of the local
142    variables. If you encounter strange behavior, try to introduce more
143    temporary variables for storing intermediate results. This might
144    help GCC... */
145
146 int selectImage(struct bootpd_t *bootp, unsigned char *imagelist[],
147         unsigned char *end_of_rfc1533)
148 {
149 #ifdef  USRPARMS
150         int flag_parms = 1;
151         int modifier_keys = 0;
152         unsigned char usrparm[256];
153 #endif
154 #ifdef  PASSWD
155         int flag_image = 1;
156 #endif
157         unsigned char   *currimg;
158         int     len;
159         unsigned char   *s;
160         int     imgnum, idx, num, opt;
161         int             i,j;
162         unsigned long   tmo;
163         in_addr         ip;
164         unsigned char   selimg[256];
165         unsigned char   *bootfile, *passwd, *defparm, *parm;
166         int     bootfilelen, passwdlen, defparmlen, parmlen;
167         unsigned char   *tags;
168
169         printf("List of available boot images:\n\n");
170         for (i = j = 0; i < RFC1533_VENDOR_NUMOFIMG; i++) {
171                 currimg = imagelist[i];
172                 if (currimg != NULL) {
173 #if     defined(ANSIESC) && !defined(CONSOLE_SERIAL)
174 #ifdef  SHOW_NUMERIC
175                         printf("\033[4C%c) ",'1'+j++);
176 #else
177                         printf("\033[4C%c) ",'A'+j++);
178 #endif
179 #else
180 #ifdef SHOW_NUMERIC
181                         printf("    %c) ",'1'+j++);
182 #else
183                         printf("    %c) ",'A'+j++);
184 #endif
185 #endif
186                         for (s = currimg+2, len = TAG_LEN(currimg)+2;
187                              (s - currimg) < len && *s != ':';
188                              putchar(*s++));
189                         putchar('\n');
190                 }
191         }
192         imgnum = j;
193         putchar('\n');
194 reselect:
195 #if     defined(ANSIESC) && !defined(CONSOLE_SERIAL)
196         printf("Select: \033[K");
197 #else
198         printf("%s%s%s","\rSelect: ",clrline+8,"\rSelect: ");
199 #endif
200         tmo = currticks()/TICKS_PER_SEC + menutmo;
201         for (;;) {
202                 if (menutmo >= 0) { for (i = -1; !iskey(); ) {
203                         if ((j = tmo - currticks()/TICKS_PER_SEC) <= 0) {
204 selectdefault:
205                                 if (menudefault >= 0 && menudefault <
206                                     RFC1533_VENDOR_NUMOFIMG) {
207                                         i = menudefault;
208                                         goto findimg;
209                                 } else if (menudefault >= RFC1533_VENDOR_IMG &&
210                                          menudefault < RFC1533_VENDOR_IMG +
211                                          RFC1533_VENDOR_NUMOFIMG &&
212                                          imagelist[menudefault -
213                                                   RFC1533_VENDOR_IMG]) {
214                                         idx = menudefault-RFC1533_VENDOR_IMG;
215                                         goto img;
216                                 }
217                                 i = ESC;
218                                 goto key;
219                         } else if (i != j) {
220 #if     defined(ANSIESC) && !defined(CONSOLE_SERIAL)
221                                 printf("\033[s(%d seconds)\033[K\033[u",i = j);
222 #else
223                                 printf("%s%s%s(%d seconds)",
224                                        "\rSelect: ",clrline+8,
225                                        "\rSelect: ",i = j);
226 #endif
227                         }
228                 } }
229 #if     defined(ANSIESC) && defined(CONSOLE_CRT)
230                 enable_cursor(1);
231                 i = getchar();
232                 enable_cursor(0);
233 #else
234                 i = getchar();
235 #endif
236         key:
237 #ifdef  USRPARAMS
238                 modifier_keys = 0;
239 #endif
240 #if     defined(USRPARMS) && defined(CONSOLE_CRT)
241                 modifier_keys |= console_getshift();
242 #endif
243                 if (i == ESC)
244                         return (255);
245                 if (i == '\n') {
246                         goto selectdefault;
247                 }
248                 if (i == '\t' || i == ' ') {
249                         menutmo = -1;
250                         printf("\r");
251                         goto reselect;
252                 }
253                 if ((i >= 'A') && (i < 'A'+imgnum)) {
254                         i -= 'A';
255 #ifdef  USRPARMS
256                         modifier_keys |= 3; /* ShiftL + ShiftR */
257 #endif
258                         break;
259                 } else if ((i >= 'a') && (i < 'a'+imgnum)) {
260                         i -= 'a';
261                         break;
262                 } else if ((i >= '1') && (i < '1'+imgnum)) {
263                         i -= '1';
264                         break;
265                 }
266         }
267 findimg:
268         idx = 0;
269         while (i >= 0) {
270                 if (imagelist[idx] != NULL) i--;
271                 idx++;
272         }
273         idx--;
274 img:
275         currimg = imagelist[idx];
276         len = TAG_LEN(currimg);
277         memcpy(selimg, currimg + 2, len);
278         selimg[len] = '\0';
279
280 #if     defined(ANSIESC) && !defined(CONSOLE_SERIAL)
281         printf("\033[K");
282 #else
283         printf("%s%s%s","\rSelect: ",clrline+8,"\rSelect: ");
284 #endif
285         s = selimg;
286         while ((s - selimg) < len && *s != ':') putchar(*s++);
287         putchar('\n');
288
289         /* Format of the 0-terminated string in selimg with length len:
290          * <menu text>:<server>:<gateway>:<boot file>:<passwd>:<flags>:<cmdline>
291          * if the string has less than 3 colons in it (i.e. no boot file), then
292          * boot from local disk, not from network.  */
293 #if     0       /* unimplemented for now */
294         for (i = ARP_SERVER; i <= ARP_GATEWAY; i++) {
295                 if ((++s - selimg) >= len) goto local_disk;
296                 if (inet_aton(s, &ip)) {
297                         arptable[i].ipaddr.s_addr = ip.s_addr;
298                         memset(arptable[i].node, 0, ETH_ALEN);
299                         while ((s - selimg) < len && *s != ':') s++;
300                 }
301         }
302 #else
303         /* Just skip the server and gateway components */
304         s++;
305         while ((s - selimg) < len && *s != ':') s++;
306         s++;
307         while ((s - selimg) < len && *s != ':') s++;
308 #endif
309         s++;
310         if ((s - selimg) >= len) goto local_disk;
311         bootfile = s;
312         while ((s - selimg) < len && *s != ':') s++;
313         bootfilelen = s - bootfile;
314         s++;
315         passwd = s;
316         while ((s - selimg) < len && *s != ':') s++;
317         passwdlen = s - passwd;
318         s++;
319         num = 0;
320         while ((s - selimg) < len && *s != ':') {
321 #if     defined(USRPARMS) || defined(PASSWD)
322                 if (*s >= '0' && *s <= '9')
323                         num = 10*num + *s - '0';
324                 else {
325                         opt = *s & ~0x20;
326 #ifdef  USRPARMS
327                         if (opt == 'P') {
328                                 flag_parms = num;
329                                 /* 0 - never interactively accept parameters
330                                  * 1 - require password before accepting parms.
331                                  * 2 - always ask for passwd and parameters
332                                  * 3 - always ask for parameters
333                                  */
334                         }
335 #endif
336 #ifdef  PASSWD
337                         if (opt == 'I') {
338                                 flag_image = num;
339                                 /* 0 - do not require a password for this image
340                                  * 1 - require a password for this image
341                                  */
342                         }
343 #endif
344                         num = 0;
345                 }
346 #endif
347                 s++;
348         }
349         s++;
350         defparm = s;
351         while ((s - selimg) < len && *s != ':') s++;
352         defparmlen = s - defparm;
353
354 #ifdef  USRPARMS
355         if (flag_parms == 1 && modifier_keys)
356                 flag_parms++;
357 #endif
358 #ifdef  PASSWD
359         if ((flag_image > 0
360 #ifdef  USRPARMS
361              || flag_parms == 2
362 #endif
363             ) && passwdlen == 32) {
364                 unsigned char md5[16];
365                 printf("Passwd: ");
366 #if     defined(ANSIESC) && defined(CONSOLE_CRT)
367                 enable_cursor(1);
368 #endif
369                 while ((i = getchar()) != '\n') {
370                         if (i == ('U'&0x1F)) md5_done(md5);
371                         else                 md5_put(i);
372                 }
373 #if     defined(ANSIESC) && defined(CONSOLE_CRT)
374                 enable_cursor(0);
375 #endif
376                 md5_done(md5);
377                 for (i = 16, s = passwd+31; i--; s -= 2) {
378                         if (gethex(s) != md5[i]) {
379 #if     defined(ANSIESC) && !defined(CONSOLE_SERIAL)
380                                 printf("\r\033[K\033[1A");
381 #else
382                                 printf("\r");
383 #endif
384                                 goto reselect;
385                         }
386                 }
387                 putchar('\n');
388         }
389 #endif
390
391         parm = defparm;
392         parmlen = defparmlen;
393 #ifdef  USRPARMS
394         if (flag_parms > 1) {
395                 parm = usrparm;
396                 memcpy(parm, defparm, defparmlen);
397                 parm[parmlen] = '\0';
398                 parmlen = getparms(bootp, parm);
399         }
400 #endif
401
402         if (bootfilelen > (int)sizeof(bootp->bootp_reply.bp_file)-1 || !*bootfile) {
403 local_disk:
404                 return (255);
405         }
406
407         tags = end_of_rfc1533;
408
409         /* Add the Etherboot magic, otherwise the other tags might be ingored.  */
410         if (tags + 8 < &bootp->bootp_extension[MAX_BOOTP_EXTLEN] - 1) {
411                 *tags++ = RFC1533_VENDOR_MAGIC;
412                 *tags++ = 6;
413                 *tags++ = '\xe4';
414                 *tags++ = '\x45';
415                 *tags++ = '\x74';
416                 *tags++ = '\x68';
417                 *tags++ = '\0';
418                 *tags++ = RFC1533_VENDOR_MAJOR;
419         }
420
421         /* This tag is very odd: with the current code the tag number of the
422          * current image selection is passed to the caller, but the image entry
423          * is lost - it is never copied to the outgoing structure.  All in all I
424          * do not understand why this tag exists at all - the image entry is
425          * useful only for the menu code - KE.  */
426         if (tags + 3 < &bootp->bootp_extension[MAX_BOOTP_EXTLEN] - 1) {
427                 *tags++ = RFC1533_VENDOR_SELECTION;
428                 *tags++ = 1;
429                 *tags++ = idx + RFC1533_VENDOR_IMG;
430         }
431
432         if (parmlen &&
433             (tags + parmlen + 2 < &bootp->bootp_extension[MAX_BOOTP_EXTLEN] - 1)) {
434                 *tags++ = RFC1533_VENDOR_ADDPARM;
435                 *tags++ = parmlen;
436                 memcpy(tags, parm, parmlen);
437                 tags += parmlen;
438         }
439
440         *tags++ = RFC1533_END;  /* End RFC1533 options */
441
442         /* if name is -, reuse previous filename */
443         if (!(bootfile[0] == '-' && bootfile[1] == ':')) {
444             memcpy(bootp->bootp_reply.bp_file, bootfile, bootfilelen);
445             bootp->bootp_reply.bp_file[bootfilelen] = '\0';
446         }
447         return (1);
448 }
449
450 /*
451  * Local variables:
452  *  c-basic-offset: 8
453  * End:
454  */