/* * configfile.c * This file is part of LCDd, the lcdproc server. * * This file is released under the GNU General Public License. Refer to the * COPYING file distributed with this package. * * Copyright (c) 2001, Joris Robijn * (c) 2003, Rene Wagner * * * Defines routines to read ini-file-like files. * Optionally retrieves settings from an LDAP directory (OpenLDAP 2.1.x) */ #include "config.h" #include #include #include #include #ifdef WITH_LDAP_SUPPORT # include #endif /* WITH_LDAP_SUPPORT */ #include "shared/report.h" #include "shared/fileio.h" typedef struct key { char * name; char * value; struct key * next_key; } key; typedef struct section { char * name; key * first_key; struct section * next_section; } section; static section * first_section = NULL; /* Yes there is a static. It's C after all :)*/ section * find_section( char * sectionname ); section * add_section( char * sectionname ); key * find_key( section * s, char * keyname, int skip ); key * add_key( section * s, char * keyname, char * value ); char get_next_char_f(buffile * f); int process_config( section ** current_section, char (*get_next_char)(), char modify_section_allowed, char * source_descr, buffile * f ); #ifdef WITH_LDAP_SUPPORT int connect_to_ldap(void); static LDAP * ld = NULL; int use_ldap=0; static char * ldap_host=NULL, * ldap_base_dn=NULL; int ldap_port; /* not supported for now * char ldap_user[255] = "", * ldap_pwd[255] = ""; */ #endif /* WITH_LDAP_SUPPORT */ /**** EXTERNAL FUNCTIONS ****/ int config_read_file( char *filename ) { buffile * f; section * curr_section = NULL; #ifdef WITH_LDAP_SUPPORT LDAPURLDesc * url = NULL; int retval; #endif /* WITH_LDAP_SUPPORT */ report( RPT_NOTICE, "Using Configuration File: %s", filename); #ifdef WITH_LDAP_SUPPORT if (ldap_is_ldap_url( filename )) { use_ldap=1; if (0 != (retval = ldap_url_parse( filename, &url))) { report( RPT_ERR, "Errors parsing LDAP URL %s: %s", filename, ldap_err2string(retval)); ldap_free_urldesc(url); return (-1); } ldap_host=strdup(url->lud_host); ldap_port=url->lud_port; report( RPT_INFO, "Using LDAP server: %s:%d", ldap_host, ldap_port); ldap_base_dn=strdup(url->lud_dn); report( RPT_INFO, "Using LDAP base DN: %s", ldap_base_dn); ldap_free_urldesc(url); if (connect_to_ldap() < 0 ) { debug( RPT_DEBUG, "connect_to_ldap returned errors."); return (-1); } return 0; } #endif /* WITH_LDAP_SUPPORT */ f = buffile_open( filename, "r" ); if( f==NULL ) { return -1; } process_config( &curr_section, get_next_char_f, 1, filename, f ); buffile_close( f ); return 0; } int config_read_string( char *sectionname, char *str ) /* All the config parameters are placed in the given section in memory.*/ { int pos=0; section * s; /* We use a nested fuction to transfer the characters from buffer to parser*/ char get_next_char() { return str[pos++]; } if( !( s=find_section( sectionname ))) { s=add_section( sectionname ); } process_config( &s, get_next_char, 0, "command line", NULL ); return 0; } char *config_get_string( char * sectionname, char * keyname, int skip, char * default_value ) { section * s; key * k; s = find_section( sectionname ); if( !s ) return default_value; k = find_key( s, keyname, skip ); if( !k ) return default_value; return k->value; /* This is the safer way:*/ /* Reallocate memory space for the return value*/ /* string_storage = realloc( string_storage, ( strlen( k->value ) / 256 + 1) * 256 ); strcpy( string_storage, k->value ); But then you also need a global static string_storage = NULL; */ } short config_get_bool( char *sectionname, char *keyname, int skip, short default_value ) { section * s; key * k; s = find_section( sectionname ); if( !s ) return default_value; k = find_key( s, keyname, skip ); if( !k ) return default_value; if( strcasecmp( k->value, "0" )==0 || strcasecmp( k->value, "false" )==0 || strcasecmp( k->value, "n" )==0 || strcasecmp( k->value, "no" )==0 ) { return 0; } if( strcasecmp( k->value, "1" )==0 || strcasecmp( k->value, "true" )==0 || strcasecmp( k->value, "y" )==0 || strcasecmp( k->value, "yes" )==0 ) { return 1; } return default_value; } long int config_get_int( char *sectionname, char *keyname, int skip, long int default_value ) { section * s; key * k; long int v; char * v_end; s = find_section( sectionname ); if( !s ) return default_value; k = find_key( s, keyname, skip ); if( !k ) return default_value; v = strtol( k->value, &v_end, 0 ); if( v_end-(k->value) != strlen(k->value) ) { /* Conversion not succesful*/ return default_value; } return v; } double config_get_float( char *sectionname, char *keyname, int skip, double default_value ) { section * s; key * k; double v; char * v_end; s = find_section( sectionname ); if( !s ) return default_value; k = find_key( s, keyname, skip ); if( !k ) return default_value; v = strtod( k->value, &v_end ); if( v_end-(k->value) != strlen(k->value) ) { /* Conversion not succesful*/ return default_value; } return v; } int config_has_section( char *sectionname ) { section * s; s = find_section( sectionname ); if( s ) return 1; else return 0; } int config_has_key( char *sectionname, char *keyname ) { section * s; key * k; int count = 0; s = find_section( sectionname ); if( !s ) return 0; for( k=s->first_key; k; k=k->next_key ) { /* Did we find the right key ?*/ if( strcasecmp( k->name, keyname ) == 0 ) { count ++; } } return count; } void config_clear() { section * s; section * next_s; key * k; key * next_k; for( s = first_section; s; ) { for( k=s->first_key; k; ) { /* Advance before we destroy the current key */ next_k = k->next_key; free( k->name ); free( k->value ); free( k ); k = next_k; } /* Advance before we destroy the current section */ next_s = s->next_section; /* And destroy it */ free( s->name ); free( s ); s = next_s; } /* And make everything inaccessable */ first_section = NULL; } /**** INTERNAL FUNCTIONS ****/ #ifdef WITH_LDAP_SUPPORT int connect_to_ldap (void) { int retval; LDAPMessage * res; debug( RPT_INFO, "Connecting to LDAP server: %s:%d", ldap_host, ldap_port); if (!(ld = ldap_init(ldap_host, ldap_port))) { report(RPT_ERR, "LDAP session could not be initialized."); return (-1); } /***************************************************** * disabled unless you really have a DN/pwd to bind to * WARNING: LCDd should not have LDAP write access!! * * if (LDAP_SUCCESS != (retval = ldap_simple_bind_s (ld, ldap_user, ldap_pwd))) { * report (RPT_ERR, "LDAP login on %s:%d failed: %s", ldap_host, ldap_port, ldap_err2string (retval)); * ldap_unbind (ld); * ld = NULL; * * return (-1); * } * fprintf(stderr, "LDAP login successful on %s:%d\n", ldap_host, ldap_port); ********************************************************/ /* check for the existence of the config object... */ if (LDAP_SUCCESS != (retval = ldap_search_s (ld, ldap_base_dn, LDAP_SCOPE_BASE, "objectClass=lcdprocConfig", NULL, 0, &res))) { report( RPT_ERR, "Could not access LDAP server on %s:%d", ldap_host, ldap_port); return (-1); } if (0 == ldap_count_entries(ld, res)) { report( RPT_ERR, "No configuration object found in LDAP at: %s", ldap_base_dn); return (-1); } debug( RPT_DEBUG, "Configuration LDAP object found."); return 0; } #define BUFSIZE 255 #endif /* WITH_LDAP_SUPPORT */ section * find_section( char * sectionname ) { section * s; #ifdef WITH_LDAP_SUPPORT LDAPMessage * res; int retval; char *filter=NULL; if (use_ldap) { debug( RPT_DEBUG, "Searching LDAP for section [%s]", sectionname); if (NULL == (filter = malloc(BUFSIZE))){ report( RPT_ERR, "Could not allocate memory in find_section()"); return NULL; } strcpy(filter, "cn="); strncat(filter, sectionname, BUFSIZE); if (LDAP_SUCCESS != (retval = ldap_search_s (ld, ldap_base_dn, LDAP_SCOPE_ONELEVEL, filter, NULL, 0, &res))) { if (NULL != filter) { free(filter); filter=NULL; } ldap_msgfree(res); report( RPT_ERR, "Could not access LDAP server on %s:%d", ldap_host, ldap_port); return NULL; } if (NULL != filter) { free(filter); filter=NULL; } if (0 == ldap_count_entries( ld, res )) { debug( RPT_DEBUG, "Section [%s] not found in LDAP.", sectionname); return NULL; } ldap_msgfree(res); debug( RPT_DEBUG, "Found section [%s] in LDAP", sectionname); s = (section*) malloc( sizeof( section )); s->name=strdup( sectionname ); s->first_key = NULL; s->next_section = NULL; return s; } #endif /* WITH_LDAP_SUPPORT */ for( s=first_section; s; s=s->next_section ) { if( strcasecmp( s->name, sectionname ) == 0 ) { return s; } } return NULL; /* not found*/ } section * add_section( char * sectionname ) { section *s; section ** place = &first_section; for( s=first_section; s; s=s->next_section ) place = &(s->next_section); *place = (section*) malloc( sizeof( section )); (*place)->name = strdup( sectionname ); (*place)->first_key = NULL; (*place)->next_section = NULL; return (*place); } key * find_key( section * s, char * keyname, int skip ) { key * k; int count = 0; key * last_key = NULL; #ifdef WITH_LDAP_SUPPORT LDAPMessage * res; LDAPMessage * entry; int retval; char *buf=NULL; char **vals; #endif /* WITH_LDAP_SUPPORT */ /* Check for NULL section*/ if(!s) return NULL; #ifdef WITH_LDAP_SUPPORT if (use_ldap) { debug( RPT_DEBUG, "Searching LDAP for key '%s' in section [%s] skipping %d entries.", keyname, s->name, skip); if (NULL == (buf = malloc(BUFSIZE))){ report (RPT_ERR, "Could not allocate memory in find_key()."); } strcpy(buf, "cn="); strncat(buf, s->name, BUFSIZE); if (LDAP_SUCCESS != (retval = ldap_search_s (ld, ldap_base_dn, LDAP_SCOPE_ONELEVEL, buf, NULL, 0, &res))) { if (NULL != buf) { free(buf); buf=NULL; } ldap_msgfree(res); report( RPT_ERR, "Could not access LDAP server on %s:%d", ldap_host, ldap_port); return NULL; } if (NULL == (entry = ldap_first_entry( ld, res ))) { debug( RPT_DEBUG, "Section [%s] not found in LDAP.", s->name); if (NULL != buf) { free(buf); buf=NULL; } /* DON'T enable the following * ldap_msgfree(entry); * ldap_msgfree below does that already */ ldap_msgfree(res); return NULL; } strcpy(buf, "lcdproc"); strncat(buf, keyname, BUFSIZE); /* debug( RPT_DEBUG, "Key name translated to attribute name: %s", buf); */ vals = ldap_get_values (ld, entry, buf); if (skip+1 > ldap_count_values (vals)) { debug( RPT_DEBUG, "No such entry found."); if (NULL != buf) { free(buf); buf=NULL; } ldap_value_free(vals); /* DON'T enable the following * ldap_msgfree(entry); * ldap_msgfree below does that already */ ldap_msgfree(res); return NULL; } /* DON'T enable the following * ldap_msgfree(entry); * ldap_msgfree below does that already */ ldap_msgfree(res); if (vals && vals[skip]) { if (NULL != buf) { free(buf); buf=NULL; } buf=strdup(vals[skip]); debug( RPT_DEBUG, "Entry found. Value is: %s", buf); ldap_value_free (vals); k=(key*) malloc( sizeof( key )); k->name = strdup( keyname ); k->value = strdup( buf ); k->next_key = NULL; if (NULL != buf) { free(buf); buf=NULL; } return k; } report( RPT_ERR, "LDAP server encountered errors."); ldap_value_free (vals); if (NULL != buf) { free(buf); buf=NULL; } return NULL; } #endif /* WITH_LDAP_SUPPORT */ for( k=s->first_key; k; k=k->next_key ) { /* Did we find the right key ?*/ if( strcasecmp( k->name, keyname ) == 0 ) { if( count == skip ) { return k; } else { count ++; last_key = k; } } } if( skip == -1 ) { return last_key; } return NULL; /* not found*/ } key * add_key( section * s, char * keyname, char * value ) { key * k; key ** place = &( s->first_key ); for( k=s->first_key; k; k=k->next_key ) place = &(k->next_key); *place = (key*) malloc( sizeof( key )); (*place)->name = strdup( keyname ); (*place)->value = strdup( value ); (*place)->next_key = NULL; return (*place); } char get_next_char_f(buffile * f) { char * buf = buffile_read(f, 1); char c = buf[0]; free(buf); return c; } /* Parser states*/ #define ST_INITIAL 0 #define ST_IGNORE 1 #define ST_SECTIONNAME 2 #define ST_KEYNAME 3 #define ST_VALUE 10 #define ST_QUOTEDVALUE 11 #define ST_QUOTEDVALUE_ESCCHAR 12 #define ST_INVALID_SECTIONNAME 23 #define ST_INVALID_KEYNAME 24 #define ST_INVALID_VALUE 30 #define ST_INVALID_QUOTEDVALUE 31 #define ST_END 99 /* Limits*/ #define MAXSECTIONNAMELENGTH 40 #define MAXKEYNAMELENGTH 40 #define MAXVALUELENGTH 200 int process_config( section ** current_section, char (*get_next_char)(), char modify_section_allowed, char * source_descr, buffile * f) { char state = ST_INITIAL; char ch; char sectionname[MAXSECTIONNAMELENGTH+1]; int sectionname_pos = 0; char keyname[MAXKEYNAMELENGTH+1]; int keyname_pos = 0; char value[MAXVALUELENGTH+1]; int value_pos = 0; int quote = 0; key * k; int line_nr = 1; while( state != ST_END ) { if (NULL != f) { ch = get_next_char(f); } else { ch = get_next_char(); } /* Secretly keep count of the line numbers*/ if( ch == '\n' ) { line_nr ++; } switch( state ) { case ST_INITIAL: switch( ch ) { case '\n': case '\r': case '\t': case ' ': break; case '#': case ';': case '=': case ']': /* It's a comment or an error*/ state = ST_IGNORE; break; case '[': /* It's a section name*/ state = ST_SECTIONNAME; sectionname[0] = 0; sectionname_pos = 0; break; case 0: break; default: /* It's a keyname*/ state = ST_KEYNAME; keyname[0] = ch; keyname[1] = 0; keyname_pos = 1; } break; case ST_IGNORE: switch( ch ) { case '\n': state = ST_INITIAL; break; } break; case ST_SECTIONNAME: switch( ch ) { case '\n': report( RPT_WARNING, "Section name incorrectly closed on line %d of %s: %s", line_nr, source_descr, sectionname ); state = ST_INITIAL; break; case '\r': case '\t': case ' ': case '"': case '[': report( RPT_WARNING, "Section name contains invalid chars on line %d of %s: %s", line_nr, source_descr, sectionname ); state = ST_INVALID_SECTIONNAME; break; case ']': if( !( *current_section=find_section( sectionname ))) { *current_section=add_section( sectionname ); } state = ST_INITIAL; break; case 0: report( RPT_WARNING, "Section name incorrectly closed on line %d of %s: %s", line_nr, source_descr, sectionname ); break; default: if( sectionname_pos