/* ************************************************************************** * * Boot-ROM-Code to load an operating system across a TCP/IP network. * * Module: menu.c * Purpose: Display a menu of bootimage choices * Entries: domenu * ************************************************************************** * * Copyright (C) 1995-2003 Gero Kuhlmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: menu.c,v 1.5 2003/01/25 23:29:41 gkminix Exp $ */ #include #include #include #include #include #include "bootpriv.h" #include "menu.h" /* ************************************************************************** * * Internal representation of each bootimage description */ struct image { int tagnum; /* image tag number */ int labellen; /* length of label */ int filenamelen; /* length of file name */ unsigned char *label; /* pointer to label */ unsigned char *filename; /* pointer to file name */ t_ipaddr server; /* server IP address */ t_ipaddr gateway; /* gateway IP address */ }; /* ************************************************************************** * * Global variables: */ static struct image imagelist[MENU_IMG_NUM]; /* list of menu items */ static t_ipaddr def_server; /* default server */ static t_ipaddr def_gateway; /* default gateway */ static int def_timeout; /* default keyboard timeout */ static int def_choice; /* default menu choice */ static int imagenum; /* number of menu items */ /* ************************************************************************** * * Get a numeric response from the user */ static int getselect() { int num; int pos; int i, ch; /* Clear keyboard buffer */ while (chkkey() >= 0) ; /* Main loop */ num = -1; pos = 0; while (TRUE) { if ((ch = getkey(def_timeout)) < 0 || ch == CHR_CR) return(num < 0 ? def_choice : num); else if (ch == CHR_ESC) return(-1); else if (ch == CHR_BS) { if (pos > 0) { printf("\b \b"); num = num / 10; pos--; } if (pos == 0) num = -1; continue; } else if (ch < '0' || ch > '9') continue; i = ch - '0'; if (num >= 0) i += num * 10; if (i >= imagenum) continue; num = i; printf("%c", ch); pos++; } } /* ************************************************************************** * * Decode image file description tag */ static int decode_image(tagstr, tagnum) unsigned char *tagstr; int tagnum; { register unsigned char *cp; register unsigned char *bp; struct image *ip; int strlength; int i; /* Copy tag number */ ip = &(imagelist[imagenum]); ip->tagnum = tagnum; /* Decode label */ cp = tagstr; strlength = *cp++; ip->label = cp; for (i = 0; *cp && *cp != ':' && strlength > 0; cp++, i++, strlength--) ; ip->labellen = i; /* Skip until next colon */ for ( ; *cp && *cp != ':' && strlength > 0; cp++, strlength--) ; if (!*cp || strlength == 0) return(FALSE); cp++; strlength--; /* Decode server IP address */ bp = cp; for ( ; *cp && *cp != ':' && strlength > 0; cp++, strlength--) ; if (!*cp || strlength == 0) return(FALSE); if (*bp == ':') ip->server = n_IP_ANY; else if ((ip->server = resolve((char *)bp)) == n_IP_ANY) return(FALSE); cp++; strlength--; /* Decode gateway IP address */ bp = cp; for ( ; *cp && *cp != ':' && strlength > 0; cp++, strlength--) ; if (!*cp || strlength == 0) return(FALSE); if (*bp == ':') ip->gateway = n_IP_ANY; else if ((ip->gateway = resolve((char *)bp)) == n_IP_ANY) return(FALSE); cp++; strlength--; /* Decode filename */ ip->filename = cp; for (i = 0; *cp && i < MAX_FNAM_LEN - 1 && strlength > 0; cp++, i++, strlength--) ; if (*cp && strlength != 0) return(FALSE); ip->filenamelen = i; imagenum++; return(TRUE); } /* ************************************************************************** * * Decode default parameter tag */ static int decode_params(tagstr) unsigned char *tagstr; { register unsigned char *cp; int *resp; int strlength; int i, offs; /* Decode the parameter tag string */ cp = tagstr; strlength = *cp++; while (*cp && strlength > 0) { /* Check for valid strings */ if (!memcmp(cp, "timeout=", 8)) { offs = 8; resp = &def_timeout; } else if (!memcmp(cp, "default=", 8)) { offs = 8; resp = &def_choice; } else return(FALSE); /* Decode any number following the equal sign */ cp += offs; strlength -= offs; i = 0; while (strlength > 0 && *cp >= '0' && *cp <= '9') { i = i * 10 + (*cp++ - '0'); strlength--; } if ((*cp && *cp != ':') || strlength < 0) return(FALSE); *resp = i; /* Skip colon */ cp++; strlength--; } /* Adjust default choice */ if (def_choice >= MENU_IMG_FIRST) { for (i = 0; i < imagenum; i++) if (imagelist[i].tagnum == def_choice) { def_choice = i; break; } } if (def_choice >= imagenum) return(FALSE); return(TRUE); } /* ************************************************************************** * * Display a menu of bootimage choices */ int domenu() { register unsigned char *cp; struct bootp *bp; struct image *ip; int i; /* * Select BOOTP reply record for all further operations */ if ((bp = bootp_bufs[BOOTP_REPLY]) == NULL) return(MENU_INVALID); cur_bootp_buf = BOOTP_REPLY; /* * Check the menu magic number in the BOOTP parameter area, including * the menu definition version number. */ if ((cp = get_vend(MENU_ID)) == NULL || *cp != 6 || *((unsigned long *)(cp + 1)) != MENU_MAGIC) return(MENU_INVALID); #if MENU_VER_MINOR > 0 if (*(cp + 5) != MENU_VER_MAJOR || *(cp + 6) > MENU_VER_MINOR) { #else if (*(cp + 5) != MENU_VER_MAJOR) { #endif printf("\nMENU: Invalid version number\n"); return(MENU_INVALID); } /* * Initialize the name resolver to allow for symbolic names in BOOTP * tags. */ res_config(); /* * Initialize the default image description from the BOOTP block. This * is necessary to be able to restore the BOOTP block with repetitive * calls to this function. */ def_server = bp->bp_siaddr; def_gateway = n_IP_ANY; if((cp = get_vend(VEND_ROUTER)) != NULL) def_gateway = *((t_ipaddr *)(cp + 1)); /* Decode the image file descriptions */ imagenum = 0; for (i = MENU_IMG_FIRST; i <= MENU_IMG_LAST; i++) if ((cp = get_vend(i)) != NULL && !decode_image(cp, i)) { printf("\nMENU: Invalid image description %d\n", i); return(MENU_INVALID); } /* Decode the default parameter tag */ if ((cp = get_vend(MENU_PARAMS)) != NULL && !decode_params(cp)) { printf("\nMENU: Invalid TAG %d\n", MENU_PARAMS); return(MENU_INVALID); } /* Print the message strings */ printf("\n\n"); for (i = MENU_DISP_FIRST; i <= MENU_DISP_LAST; i++) if ((cp = get_vend(i)) != NULL) { int len = *cp++; printf("%ls\n", cp, len); } /* Print the menu and wait for a user keypress */ if (imagenum > 0) { printf("\n\n"); for (i = 0; i < imagenum; i++) printf("[%s%d]\t%ls\n", i < 10 ? " ":"", i, imagelist[i].label, imagelist[i].labellen); printf("\nSelect a choice: "); i = getselect(); printf("\n\n"); /* If no filename specified for image, abort menu selection */ ip = &(imagelist[i]); if (i < 0 || !ip->filenamelen) return(MENU_ABORT); /* Set bootimage file name */ memset(bp->bp_file, 0, sizeof(bp->bp_file)); memcpy(bp->bp_file, ip->filename, ip->filenamelen); /* Set IP of tftp server */ memset(bp->bp_sname, 0, sizeof(bp->bp_sname)); if (ip->server != n_IP_ANY) bp->bp_siaddr = ip->server; else bp->bp_siaddr = def_server; /* Set gateway IP to reach the server */ if (ip->gateway != n_IP_ANY) def_gateway = ip->gateway; if ((cp = get_vend(VEND_ROUTER)) != NULL) *((t_ipaddr *)(cp + 1)) = def_gateway; else if ((cp = get_vend(VEND_END)) != NULL && (cp + IP_ALEN + 2) <= ((unsigned char *)bp) + bootp_sizes[cur_bootp_buf]) { *(cp - 1) = VEND_ROUTER; *cp = IP_ALEN; *((t_ipaddr *)(cp + 1)) = def_gateway; *(cp + IP_ALEN + 1) = VEND_END; } printf("Selected %ls\n\n", ip->label, ip->labellen); } return(MENU_OK); }