/* * getltscfg.c: - parse lts.conf configuration, and return settings * * (c) 1999,2003,2006 by James A. McQuillan (McQuillan Systems, LLC) * * This software is distributed under the terms and conditions of the * GNU General Public License version 2. See file COPYING for the full * text of the license. * */ #define MAIN #undef input #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _GNU_SOURCE /* need the fnmatch FNM_CASEFOLD flag */ #include #include "getltscfg.h" extern FILE *yyin; struct TUPLE { char *keyword; char *value; struct TUPLE *next; }; struct SECT { char *name; struct TUPLE *tuple_list; struct SECT *next; }; struct CHAIN { char *name; struct SECT *sect; struct CHAIN *next; struct CHAIN *prev; }; typedef struct TUPLE TUPLETYPE; typedef struct SECT SECTTYPE; typedef struct CHAIN CHAINTYPE; SECTTYPE *headsect; SECTTYPE *cursect; TUPLETYPE *curtuple; TUPLETYPE *headtuple; TUPLETYPE *tailtuple; CHAINTYPE *headchain; CHAINTYPE *tailchain; CHAINTYPE *curchain; #define NUM_WORKSTATION_ID 4 char *aWorkstationId[NUM_WORKSTATION_ID]; char sIPAddr[64]; char sMACAddr[64]; int fDumpAll; static char *sConfigFile = "/etc/lts.conf"; static char *sHostName = ""; #define MAX_FILE_INCLUDES 10 struct stat *includeFiles[MAX_FILE_INCLUDES]; int includeFileCount = 0; struct utsname unamebuf; FILE *CFGFILE; char *pOptionName; int verbose = 0; // // Function prototypes // void DecodeCommandLine(int, const char **); int yyparse(void); //------------------------------------------------------------------------------ // // Creating a new section involves allocating a section structure, filling in // the fields, and then linking the structure into the linked list that starts // with headsect. // void process_section( char *sectname ) { if(verbose) { fprintf(stderr, "getlts::process_section [%s]\n",sectname); } // // Allocate a section structure // SECTTYPE *sectptr = (SECTTYPE *)malloc(sizeof(SECTTYPE)); // // Allocate some memory to hold the name of the section // sectptr->name = (char *)malloc(strlen(sectname)+1); // // Copy the name in // strcpy(sectptr->name,sectname); // // set the 'next' pointer to zero // sectptr->next = 0; if( cursect ) cursect->next = sectptr; else headsect = sectptr; cursect = sectptr; cursect->tuple_list = 0; // Each section starts with an empty tuple list curtuple = 0; } //------------------------------------------------------------------------------ // This procedure takes a filename as argument (which has already been verified // as readable) process it as if the contents were part of the original config file // In other words, this code supports 'include' syntax for the config files void process_include( char *incfile ) { if(verbose) { fprintf(stderr, "getlts::process_include [%s]\n",incfile); } // attempt to parse the new config file parseConfig(incfile); } //------------------------------------------------------------------------------ void process_tuple(char *kwd, char *val) { if(verbose) { fprintf(stderr, "getlts::process_tuple [%s,%s]\n",kwd,val); } // // Allocate a TUPLE structure // TUPLETYPE *tupleptr = (TUPLETYPE *)malloc(sizeof(TUPLETYPE)); // // Allocate some memory to hold the keyword // tupleptr->keyword = (char *)malloc(strlen(kwd)+1); strcpy(tupleptr->keyword,kwd); // // Allocate some memory to hold the value // tupleptr->value = (char *)malloc(strlen(val)+1); strcpy(tupleptr->value,val); // // Set the 'next' pointer to zero // tupleptr->next = 0; if( curtuple ) curtuple->next = tupleptr; else cursect->tuple_list = tupleptr; curtuple = tupleptr; } //------------------------------------------------------------------------------ // // find_chain_entry(), returns TRUE if it found the entry, and FALSE otherwise // int find_chain_entry(char *name, int glob) { int fFound = FALSE; SECTTYPE *worksect; cursect = headsect; int matches = FALSE; while( cursect && !fFound ){ matches = strcasecmp(cursect->name,name) == 0; if (glob) matches = !matches && fnmatch(cursect->name, name, FNM_CASEFOLD) == 0; if(matches){ CHAINTYPE *chainptr = (CHAINTYPE *)malloc(sizeof(CHAINTYPE)); worksect = cursect; fFound = TRUE; if(curchain) curchain->next = chainptr; else headchain = chainptr; chainptr->name = (char *)malloc(strlen(cursect->name)+1); strcpy(chainptr->name,cursect->name); chainptr->sect = cursect; chainptr->next = 0; chainptr->prev = curchain; curchain = chainptr; tailchain = chainptr; } cursect = cursect->next; } return(fFound); } //------------------------------------------------------------------------------ void add_tuple(TUPLETYPE *tuple) { int fFound = FALSE; TUPLETYPE *t; t = headtuple; while(t && !fFound){ if(strcasecmp(t->keyword,tuple->keyword) == 0) fFound = TRUE; else t = t->next; } if(fFound){ // replace it free((char *)t->value); t->value = (char *)malloc(strlen(tuple->value)+1); strcpy(t->value,tuple->value); } else{ // Allocate the space for the tuple t = (TUPLETYPE *)malloc(sizeof(TUPLETYPE)); t->next = 0; // Copy the keyword t->keyword = (char *)malloc(strlen(tuple->keyword)+1); strcpy(t->keyword,tuple->keyword); // Copy the value t->value = (char *)malloc(strlen(tuple->value)+1); strcpy(t->value,tuple->value); if(tailtuple) tailtuple->next = t; else headtuple = t; tailtuple = t; } } //------------------------------------------------------------------------------ // // void GetAddrs() // // This routine obtains the IP and MAC addresses of the first // appropriately configured network interface. // void GetAddrs() { int numreqs = 10; struct ifconf ifc; struct ifreq *ifr; /* netdevice(7) */ struct ifreq info; struct sockaddr_in *sa; char *pIPAddr = &sIPAddr[0]; char *pMACAddr = &sMACAddr[0]; int skfd, n; *pIPAddr = '\0'; *pMACAddr = '\0'; skfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if(skfd < 0){ perror("socket"); return; } // // Get a list of all the interfaces. // ifc.ifc_buf = NULL; for(;;){ ifc.ifc_len = sizeof(struct ifreq) * numreqs; ifc.ifc_buf = (char *)realloc(ifc.ifc_buf, ifc.ifc_len); if(ifc.ifc_buf == NULL) { fprintf(stderr, "Out of memory\n"); return; } if(ioctl(skfd, SIOCGIFCONF, &ifc) < 0){ perror("SIOCGIFCONF"); goto out; } if(ifc.ifc_len == (int)sizeof(struct ifreq) * numreqs){ // assume it overflowed and try again numreqs += 10; continue; } break; } // // Look for the first interface that has an IP address, is not // loopback, and is up. // ifr = ifc.ifc_req; for(n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)){ if(ifr->ifr_addr.sa_family != AF_INET) continue; strcpy(info.ifr_name, ifr->ifr_name); if(ioctl(skfd, SIOCGIFFLAGS, &info) < 0){ perror("SIOCGIFFLAGS"); goto out; } if(!(info.ifr_flags & IFF_LOOPBACK) && (info.ifr_flags & IFF_UP)){ sa = (struct sockaddr_in *) &ifr->ifr_addr; sprintf(pIPAddr, "%s", inet_ntoa(sa->sin_addr)); break; } ifr++; } if(n == ifc.ifc_len){ fprintf(stderr, "No configured interface found\n"); goto out; } if(ioctl(skfd, SIOCGIFHWADDR, &info) < 0){ perror("SIOCGIFHWADDR"); goto out; } for(n = 0; n < IFHWADDRLEN; n++) pMACAddr += sprintf(pMACAddr, "%02x%s", info.ifr_hwaddr.sa_data[n] & 0xff, n < IFHWADDRLEN-1 ? ":" : ""); out: free(ifc.ifc_buf); } //------------------------------------------------------------------------------ int main( int argc, const char **argv ) { int i; int status; int fFound; CHAINTYPE *chain; fDumpAll = FALSE; DecodeCommandLine( argc, argv ); fSyntaxError = FALSE; // // Initialize some of our list pointers // cursect = headsect = 0; curchain = headchain = tailchain = 0; tailtuple = headtuple = 0; for( i = 0; i < NUM_WORKSTATION_ID; i++ ){ aWorkstationId[i] = 0L; } if( sHostName[0] ){ aWorkstationId[0] = (char *)malloc(strlen(sHostName)+1); strcpy(aWorkstationId[0],sHostName); } else{ // Get the hostname from the system uname(&unamebuf); aWorkstationId[0] = (char *)malloc(strlen(unamebuf.nodename)+1); strcpy(aWorkstationId[0],unamebuf.nodename); // Get the GetAddrs(); aWorkstationId[1] = (char *)malloc(strlen(sIPAddr)+1); strcpy(aWorkstationId[1],sIPAddr); aWorkstationId[2] = (char *)malloc(strlen(sMACAddr)+1); strcpy(aWorkstationId[2],sMACAddr); } // treat the main config file just like any other include if(parseConfig(sConfigFile) != 0) { char buf[255]; sprintf(buf,"Error opening config file [%s]", sConfigFile); perror(buf); exit(2); } lineno = 1; yyparse(); // // When the parsing is complete, we have a tree in memory // that contains ALL of the sections with all of the values. // // // The next step is to find the section that matches the workstation // that we are interested in. Then, walk through the linked list // looking for any entries that indicate we want to "inherit" entries // from another section. We'll build a new linked list of those // sections that we want to inherit from. Then, we'll tack any wildcard matches // and the 'Default' section on the end of that. Once we have the inheritance list, we // need to walk that list backwards, building the list of // tuples (keyword/value pairs). // Finally, when we are all done, we should have all of the values that // should go with the workstation we are interested in. So, we // either dump all of the entries out, or we dump out a single entry. // This is dependent on whether this program was called with the // '-a' option or not. // // // Find the entry that exactly matches our workstation. // i = 0; fFound = FALSE; while( aWorkstationId[i] && !fFound ){ if(find_chain_entry(aWorkstationId[i], FALSE)){ fFound = TRUE; } i++; } chain = headchain; while(chain){ curtuple = chain->sect->tuple_list; while(curtuple){ if(strcasecmp(curtuple->keyword,"LIKE") == 0) status = find_chain_entry(curtuple->value, FALSE); curtuple = curtuple->next; } chain = chain->next; } for (i=0; aWorkstationId[i]; ++i){ if((status = find_chain_entry(aWorkstationId[i], TRUE))){ chain = headchain; while(chain){ curtuple = chain->sect->tuple_list; while(curtuple){ if(strcasecmp(curtuple->keyword,"LIKE") == 0) status = find_chain_entry(curtuple->value, FALSE); curtuple = curtuple->next; } chain = chain->next; } } } // // the last entry in the chain is the '[Default]' entry // status = find_chain_entry("default", FALSE); curchain = tailchain; while(curchain){ curtuple = curchain->sect->tuple_list; while(curtuple){ if(strcasecmp(curtuple->keyword,"LIKE") != 0) add_tuple(curtuple); curtuple = curtuple->next; } curchain = curchain->prev; } if(fDumpAll){ // // Spit out all of the variables for this workstation // curtuple = headtuple; while(curtuple){ char *p = curtuple->keyword; while(*p){ *p = toupper(*p); p++; } if(strcasecmp(curtuple->value,"NONE") != 0){ printf("%s=\"%s\"\n", curtuple->keyword, curtuple->value); } curtuple = curtuple->next; } curtuple = headtuple; while(curtuple){ if(strcasecmp(curtuple->value,"NONE") != 0){ printf("export %s\n", curtuple->keyword); } curtuple = curtuple->next; } } else{ // // Dump just one var // int fFound = FALSE; curtuple = headtuple; while(curtuple && !fFound ){ if(strcasecmp(curtuple->keyword,pOptionName) == 0){ if(strcasecmp(curtuple->value,"NONE") != 0){ printf("%s\n", curtuple->value); } fFound = TRUE; } curtuple = curtuple->next; } } return(0); } //------------------------------------------------------------------------------ void usage(int argc, const char **argv) { assert(argc > 0); fprintf(stderr, "\nUsage: %s [options] [keyword]\n", argv[0]); fprintf(stderr, "\n Options: [{-c|--configfile} ]\n"); fprintf(stderr, " [{-n|--hostname} ]\n"); fprintf(stderr, " [{-a|--all}]\n"); fprintf(stderr, " [{-v|--verbose}]\n"); fprintf(stderr, " [{-h|--help}]\n"); fprintf(stderr, " [keyword]\n\n"); exit(1); } //------------------------------------------------------------------------------ void DecodeCommandLine(int argc, const char **argv) { signed int c; poptContext optCon; // context for parsing command-line options struct poptOption optionsTable[] = { { "configfile", 'c', POPT_ARG_STRING, &sConfigFile, 0, NULL, NULL }, { "hostname", 'n', POPT_ARG_STRING, &sHostName, 0, NULL, NULL }, { "all", 'a', 0, 0, 'a', NULL, NULL }, { "verbose", 'v', 0, 0, 'v', NULL, NULL }, { "help", 'h', 0, 0, 'h', NULL, NULL }, { NULL, 0, 0, NULL, 0, NULL, NULL } }; if( argc < 2 ){ usage(argc,argv); } optCon = poptGetContext((const char *)NULL, argc, (const char **)argv, (const struct poptOption *)optionsTable, 0 ); while( ( c = poptGetNextOpt(optCon) ) >= 0 ) { switch(c){ case 'a': fDumpAll = TRUE; break; case 'h': usage(argc,argv); break; case 'v': verbose = TRUE; break; } } // // Make sure they supplied the name of a configuration key // if( ! fDumpAll ){ if( ( pOptionName = (char *)poptGetArg(optCon) ) == NULL ){ usage(argc,argv); } } poptFreeContext(optCon); } int parseConfig(char *config) { FILE *INCFILE; int i, found = 0; struct stat cstat; char buf[255]; // get a stat on the file if(stat(config, &cstat) != 0) { sprintf(buf,"Error retrieving file stats for file [%s]",config); perror(buf); exit(2); } // verify that we haven't already opened this file.. for(i = 0; i < includeFileCount && i < MAX_FILE_INCLUDES; i++) { if(verbose) { fprintf(stderr, "parseConfig::looping over includeFiles, trying to match (dev: [%d], inode: [%d]) against (dev: [%d], inode: [%d])\n",(int)cstat.st_dev,(int)cstat.st_ino,(int)((struct stat)*includeFiles[i]).st_dev,(int)((struct stat)*includeFiles[i]).st_ino); } if(cstat.st_dev == ((struct stat)*includeFiles[i]).st_dev && cstat.st_ino == ((struct stat)*includeFiles[i]).st_ino) { fprintf(stderr, "Warning: ignoring previously seen include file: %s\n",config); found = 1; } } if(i < MAX_FILE_INCLUDES && found == 0) { if(verbose) { fprintf(stderr, "Adding file to include list..\n"); } includeFiles[i] = (struct stat *)malloc(sizeof(cstat)); memcpy(includeFiles[i],&cstat,sizeof(cstat)); includeFileCount++; if((INCFILE = fopen(config,"r")) == NULL ) { sprintf(buf,"Error opening config file [%s]", config); perror(buf); exit(2); } // I'm not certain if closing yyin is necessary or appropriate. I couldn't get it to work correctly. /* if(fileno(yyin) > 0) { fclose(yyin); } */ lineno = 0; strcpy(curConfigFile,config); yyin = INCFILE; return 0; } else if(i >= MAX_FILE_INCLUDES) { fprintf( stderr, "Warning: MAX_FILE_INCLUDES (%d) exceeded. Ignoring include file %s\n",MAX_FILE_INCLUDES,config); return 1; } else { fprintf( stderr, "Warning: Include file %s has already been included. Skipping to avoid infinite loop.\n",config); return 2; } }