/* * PXE daemon - enable the remote booting of PXE enabled machines. * Copyright (C) 2000 Tim Hurman (kano@kano.org.uk) * * 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 (at your option) 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ /****************************************************************************** * options.cc - read the config options from the file * ******************************************************************************/ #include "options.h" // global vars CSA_t CSA_types[CSA_MAX_TYPES] = { {0, "X86PC"}, {1, "PC98"}, {2, "IA64PC"}, {3, "DEC"}, {4, "ArcX86"}, {5, "ILC"}, {(uint16_t)-1, NULL} }; /****************************************************************************** * Options - constructor - read from the default file * ******************************************************************************/ Options::Options(LogFile *_log) { this->log = _log; ReadFile(PXECONFIGFILE); } /****************************************************************************** * Options - constructor - read from the specified file * ******************************************************************************/ Options::Options(LogFile *_log, const char *filename) { this->log = _log; ReadFile(filename); } /****************************************************************************** * Options - Deconstuctor * ******************************************************************************/ Options::~Options() { services_t *serv_ptr, *serv_prev; if(interface != NULL) delete[] interface; if(prompt != NULL) delete[] prompt; if(domain != NULL) delete[] domain; if(tftpdbase != NULL) delete[] tftpdbase; port = 0; // scan and delete options for(serv_ptr=serv_prev=serv_head; serv_ptr != NULL; ) { serv_prev = serv_ptr; serv_ptr = serv_ptr->next; delete[] serv_prev->filebase; delete[] serv_prev->menu_text; delete serv_prev; } serv_head = NULL; } /****************************************************************************** * ReadFile - read the config file * ******************************************************************************/ void Options::ReadFile(const char *filename) { std::fstream *fp; int len; char *key,*val; struct in_addr t_addr; int key_id = 1; char *buf; // initalise vars buf = new char[1024]; tftpdbase = new char[strlen(TFTPD_BASE)+1]; strcpy(tftpdbase, TFTPD_BASE); domain = new char[strlen(DEF_DOMAIN)+1]; strcpy(domain, DEF_DOMAIN); prompt = new char[strlen(DEF_PROMPT)+1]; strcpy(prompt, DEF_PROMPT); interface = NULL; multicast_address = DEF_MULTI_BOOT; mtftp_address = DEF_MTFTP_ADDR; mtftp_cport = DEF_MTFTP_CPORT; mtftp_sport = DEF_MTFTP_SPORT; port = DEF_PORT; prompt_timeout = DEF_PROMPT_TIMEOUT; default_address = 0; use_multicast = use_broadcast = 1; serv_head = NULL; fp = new std::fstream(filename, std::ios::in); if(fp == NULL) throw new SysException(errno, "Options::ReadFile:fopen()"); while(fp->getline(buf, 1024)) { len = strlen(buf)-1; if(-1 == len) goto Options_ReadFile_next; for(; len > 0; len--) if((buf[len] != '\n') && (buf[len] != '\r')) { len++; break; } buf[len] = 0; if((buf[0] == ' ') || (buf[0] == '#') || (buf[0] == 0)) goto Options_ReadFile_next; // examine the string contents key = strtok(buf, "="); if(key == NULL) goto Options_ReadFile_next; val = strtok(NULL, "="); if(val == NULL) goto Options_ReadFile_next; // examine key if(strcmp("interface", key) == 0) { if (NULL != interface) delete interface; interface = new char[strlen(val)+1]; strcpy(interface, val); } else if(strcmp("prompt", key) == 0) { delete prompt; prompt = new char[strlen(val)+1]; strcpy(prompt, val); } else if(strcmp("listen_port", key) == 0) { port = atoi(val); } else if(strcmp("use_multicast", key) == 0) { use_multicast = atoi(val); } else if(strcmp("use_broadcast", key) == 0) { use_broadcast = atoi(val); } else if(strcmp("multicast_address", key) == 0) { t_addr.s_addr = 0; #ifdef HAVE_INET_ATON if(0 == inet_aton(val, &t_addr)) #else t_addr.s_addr = inet_addr(val); if((unsigned)-1 == t_addr.s_addr) #endif // HAVE_INET_ATON t_addr.s_addr = DEF_MTFTP_ADDR; multicast_address = ntohl(t_addr.s_addr); } else if(strcmp("domain", key) == 0) { delete domain; domain = new char[strlen(val)+1]; strcpy(domain, val); } else if(strcmp("tftpdbase", key) == 0) { delete tftpdbase; tftpdbase = new char[strlen(val)+1]; strcpy(tftpdbase, val); } else if(strcmp("service", key) == 0) { AddService(val, &key_id); } else if(strcmp("mtftp_address", key) == 0) { t_addr.s_addr = 0; #ifdef HAVE_INET_ATON if(0 == inet_aton(val, &t_addr)) #else t_addr.s_addr = inet_addr(val); if((unsigned)-1 == t_addr.s_addr) #endif // HAVE_INET_ATON t_addr.s_addr = htonl(DEF_MTFTP_ADDR); mtftp_address = ntohl(t_addr.s_addr); } else if(strcmp("mtftp_client_port", key) == 0) { mtftp_cport = atoi(val); if(mtftp_cport == 0) mtftp_cport = DEF_MTFTP_CPORT; } else if(strcmp("mtftp_server_port", key) == 0) { mtftp_sport = atoi(val); if(mtftp_sport == 0) mtftp_sport = DEF_MTFTP_SPORT; } else if(strcmp("prompt_timeout", key) == 0) { prompt_timeout = atoi(val); if((0 == prompt_timeout) && (EINVAL == errno)) prompt_timeout = DEF_PROMPT_TIMEOUT; } else if(strcmp("default_address", key) == 0) { t_addr.s_addr = 0; #ifdef HAVE_INET_ATON if(0 == inet_aton(val, &t_addr)) #else t_addr.s_addr = inet_addr(val); if((unsigned)-1 == t_addr.s_addr) #endif // HAVE_INET_ATON t_addr.s_addr = DEF_MTFTP_ADDR; default_address = ntohl(t_addr.s_addr); } else { log->Event(LEVEL_ERR, "Options::ReadFile", 2, "Unknown key:", key); } Options_ReadFile_next: fp = fp; } fp->close(); delete[] buf; } /****************************************************************************** * GetInterface - get the interface name * ******************************************************************************/ char * Options::GetInterface() { return(interface); } /****************************************************************************** * GetPort - return the port number * ******************************************************************************/ uint16_t Options::GetPort() { return(port); } /****************************************************************************** * UseMulticast - shall we use multicast discovery * ******************************************************************************/ int Options::UseMulticast() { return(use_multicast); } /****************************************************************************** * UseBroadcast - shall we use broadcast discovery * ******************************************************************************/ int Options::UseBroadcast() { return(use_broadcast); } /****************************************************************************** * GetMulticast - return the multicast address specified * ******************************************************************************/ uint32_t Options::GetMulticast() { return(multicast_address); } /****************************************************************************** * AddService - add a service to the list * ******************************************************************************/ void Options::AddService(char *serviceinfo, int *key_id) { services_t *serv_ptr, *serv_tail, *serv_prev; char *ptr; int i; ptr = strtok(serviceinfo, ","); if(ptr == NULL) { log->Event(LEVEL_ERR, "AddService:parse", 1, "Invalid service declaration"); return; } // see if it is a recognised CSA for(i=0; ((CSA_types[i].arch_name != NULL) && (strcmp(CSA_types[i].arch_name, ptr) != 0)); i++); // valid CSA? if(CSA_types[i].arch_name == NULL) { log->Event(LEVEL_ERR, "AddService:parse", 1, "Unknown client system architecture"); return; } // assign the info serv_ptr = new services_t; serv_ptr->csa = CSA_types[i].arch_id; // min layer ptr = strtok(NULL, ","); if(ptr == NULL) { log->Event(LEVEL_ERR, "AddService:parse", 1, "Invalid service declaration"); delete serv_ptr; return; } serv_ptr->min_level = atoi(ptr); // max layer ptr = strtok(NULL, ","); if(ptr == NULL) { log->Event(LEVEL_ERR, "AddService:parse", 1, "Invalid service declaration"); delete serv_ptr; return; } serv_ptr->max_level = atoi(ptr); // basename ptr = strtok(NULL, ","); if(ptr == NULL) { log->Event(LEVEL_ERR, "AddService:parse", 1, "Invalid service declaration"); delete serv_ptr; return; } serv_ptr->filebase = new char[strlen(ptr)+1]; strcpy(serv_ptr->filebase, ptr); // menu entry ptr = strtok(NULL, "\0"); if(ptr == NULL) { log->Event(LEVEL_ERR, "AddService:parse", 1, "Invalid service declaration"); delete[] serv_ptr->filebase; delete serv_ptr; return; } if(strlen(ptr) > 255) { log->Event(LEVEL_ERR, "AddService:parse", 1, "Menu item too long"); delete[] serv_ptr->filebase; delete serv_ptr; return; } serv_ptr->menu_text = new char[strlen(ptr)+1]; strcpy(serv_ptr->menu_text, ptr); if(strcmp(serv_ptr->filebase, "local") == 0) serv_ptr->menu_id = 0; else { serv_ptr->menu_id = *key_id; (*key_id)++; } // find the tail of the list for(serv_prev=serv_tail=serv_head; serv_tail != NULL; serv_tail=serv_tail->next) serv_prev = serv_tail; // append + sort out minor faults if(serv_tail == serv_prev) // on head serv_head = serv_ptr; else serv_prev->next = serv_ptr; // end of list serv_ptr->next = NULL; } /****************************************************************************** * GetMTFTPAddr - return the multicast address of the mtftp daemon * ******************************************************************************/ uint32_t Options::GetMTFTPAddr() { return(mtftp_address); } /****************************************************************************** * GetMTFTPAddr - return the multicast address of the mtftp daemon * ******************************************************************************/ uint32_t Options::GetDefAddr() { return(default_address); } /****************************************************************************** * GetMTFTPsport - return the multicast server port of the mtftp daemon * ******************************************************************************/ uint16_t Options::GetMTFTPsport() { return(mtftp_sport); } /****************************************************************************** * GetMTFTPcport - return the multicast client port of the mtftp daemon * ******************************************************************************/ uint16_t Options::GetMTFTPcport() { return(mtftp_cport); } /****************************************************************************** * GetMenuTimeout - return the amount of time the boot menu is shown for * ******************************************************************************/ uint8_t Options::GetMenuTimeout() { return(prompt_timeout); } /****************************************************************************** * GetMenuPrompt - return the menu string * ******************************************************************************/ char * Options::GetMenuPrompt() { char *c = new char[strlen(prompt)+1]; strcpy(c, prompt); return(c); } /****************************************************************************** * CheckMenu - check to see if there is a menu item for a specific CSA * ******************************************************************************/ uint16_t Options::CheckMenu(uint16_t reqcsa) { services_t *serv_ptr; serv_ptr = serv_head; while(NULL != serv_ptr) { if(serv_ptr->csa == reqcsa) return(serv_ptr->csa); serv_ptr = serv_ptr->next; } return((uint16_t)-1); } /****************************************************************************** * MakeBootMenu - make the option for the boot menu * ******************************************************************************/ option * Options::MakeBootMenu(int csa, int *arch_id, int *menu_id) { int i,j; uint16_t menu_id_n; int count = 0; services_t *serv_ptr; option *opt = new option; opt->len = i = 0; // work out how much memory we need serv_ptr = serv_head; while(NULL != serv_ptr) { if(serv_ptr->csa == csa) opt->len += strlen(serv_ptr->menu_text)+3; serv_ptr = serv_ptr->next; } opt->data = new uint8_t[opt->len]; // copy the menu items serv_ptr = serv_head; while(NULL != serv_ptr) { if(serv_ptr->csa == csa) { menu_id_n = htons(serv_ptr->menu_id); memcpy(opt->data+i, &menu_id_n, 2); i += 2; j = strlen(serv_ptr->menu_text); opt->data[i] = j; i++; memcpy(opt->data+i, serv_ptr->menu_text, j); i += j; if(0 == count) { *arch_id = serv_ptr->csa; *menu_id = serv_ptr->menu_id; } count++; } serv_ptr = serv_ptr->next; } // last check if(0 == opt->len) return(NULL); return(opt); } /****************************************************************************** * GetMinLayer - get the lowest layer for this item * ******************************************************************************/ uint8_t Options::GetMinLayer(int arch_id, int menu_id) { services_t *serv_ptr; serv_ptr = serv_head; while(NULL != serv_ptr) { if((menu_id == serv_ptr->menu_id) && (arch_id == serv_ptr->csa)) break; serv_ptr = serv_ptr->next; } if(NULL == serv_ptr) return(0); return(serv_ptr->min_level); } /****************************************************************************** * GetMaxLayer - get the highest layer for this item * ******************************************************************************/ uint8_t Options::GetMaxLayer(int arch_id, int menu_id) { services_t *serv_ptr; serv_ptr = serv_head; while(NULL != serv_ptr) { if((menu_id == serv_ptr->menu_id) && (arch_id == serv_ptr->csa)) break; serv_ptr = serv_ptr->next; } if(NULL == serv_ptr) return(0); return(serv_ptr->max_level); } /****************************************************************************** * MakeFilename - make the boot filename for a specific arch/layer * ******************************************************************************/ char * Options::MakeFilename(int menu_id, int arch_id, uint8_t layer) { services_t *serv_ptr; char *tmpc; int i; serv_ptr = serv_head; while(NULL != serv_ptr) { if((menu_id == serv_ptr->menu_id) && (arch_id == serv_ptr->csa)) break; serv_ptr = serv_ptr->next; } if(NULL == serv_ptr) return(NULL); if((layer < serv_ptr->min_level) || (serv_ptr->max_level < layer)) return(NULL); // search for the arch name for(i=0; i != CSA_types[i].arch_id; i++); tmpc = new char[strlen(CSA_types[i].arch_name) + (strlen(serv_ptr->filebase)*2) + 8]; sprintf(tmpc, "%s/%s/%s.%d", CSA_types[i].arch_name, serv_ptr->filebase, serv_ptr->filebase, layer); return(tmpc); } /****************************************************************************** * CheckLayer - see if the layer requested is withing the valid range * ******************************************************************************/ int Options::CheckLayer(int menu_id, int arch_id, uint8_t layer) { services_t *serv_ptr; serv_ptr = serv_head; while(NULL != serv_ptr) { if((menu_id == serv_ptr->menu_id) && (arch_id == serv_ptr->csa)) break; serv_ptr = serv_ptr->next; } if(NULL == serv_ptr) return(-1); if((layer < serv_ptr->min_level) || (serv_ptr->max_level < layer)) return(-1); return(0); }