]> git.sur5r.net Git - openldap/blob - servers/slurpd/config.c
ITS#2594 add URI support for replica config
[openldap] / servers / slurpd / config.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*
7  * Copyright (c) 1996 Regents of the University of Michigan.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms are permitted
11  * provided that this notice is preserved and that due credit is given
12  * to the University of Michigan at Ann Arbor. The name of the University
13  * may not be used to endorse or promote products derived from this
14  * software without specific prior written permission. This software
15  * is provided ``as is'' without express or implied warranty.
16  */
17
18
19 /*
20  * config.c - configuration file handling routines
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/stdlib.h>
28 #include <ac/string.h>
29 #include <ac/socket.h>
30 #include <ac/ctype.h>
31
32 #include <ldap.h>
33
34 #include "slurp.h"
35 #include "globals.h"
36
37 #define ARGS_STEP       512
38
39 /* Forward declarations */
40 static void     add_replica LDAP_P(( char **, int ));
41 static int      parse_replica_line LDAP_P(( char **, int, Ri *));
42 static void     parse_line LDAP_P(( char * ));
43 static char     *getline LDAP_P(( FILE * ));
44 static char     *strtok_quote LDAP_P(( char *, char * ));
45
46 int     cargc = 0, cargv_size = 0;
47 char    **cargv;
48 /* current config file line # */
49 static int      lineno;
50
51 char *slurpd_pid_file = NULL;
52 char *slurpd_args_file = NULL;
53
54 /*
55  * Read the slapd config file, looking only for config options we're
56  * interested in.  Since we haven't detached from the controlling
57  * terminal yet, we just perror() and fprintf here.
58  */
59 int
60 slurpd_read_config(
61     char        *fname
62 )
63 {
64     FILE        *fp;
65     char        *line;
66
67         cargv = ch_calloc( ARGS_STEP + 1, sizeof(*cargv) );
68         cargv_size = ARGS_STEP + 1;
69
70 #ifdef NEW_LOGGING
71     LDAP_LOG ( CONFIG, ARGS, 
72                 "slurpd_read_config: Config: opening config file \"%s\"\n", 
73                 fname, 0, 0 );
74 #else
75     Debug( LDAP_DEBUG_CONFIG, "Config: opening config file \"%s\"\n",
76             fname, 0, 0 );
77 #endif
78
79     if ( (fp = fopen( fname, "r" )) == NULL ) {
80         perror( fname );
81         exit( EXIT_FAILURE );
82     }
83
84     lineno = 0;
85     while ( (line = getline( fp )) != NULL ) {
86         /* skip comments and blank lines */
87         if ( line[0] == '#' || line[0] == '\0' ) {
88             continue;
89         }
90
91 #ifdef NEW_LOGGING
92     LDAP_LOG ( CONFIG, DETAIL1, 
93                 "slurpd_read_config: Config: (%s)\n", line, 0, 0 );
94 #else
95         Debug( LDAP_DEBUG_CONFIG, "Config: (%s)\n", line, 0, 0 );
96 #endif
97
98         parse_line( line );
99
100         if ( cargc < 1 ) {
101             fprintf( stderr, "line %d: bad config line (ignored)\n", lineno );
102             continue;
103         }
104
105         /* replication log file to which changes are appended */
106         if ( strcasecmp( cargv[0], "replogfile" ) == 0 ) {
107             /* 
108              * if slapd_replogfile has a value, the -r option was given,
109              * so use that value.  If slapd_replogfile has length == 0,
110              * then we should use the value in the config file we're reading.
111              */
112             if ( sglob->slapd_replogfile[ 0 ] == '\0' ) {
113                 if ( cargc < 2 ) {
114                     fprintf( stderr,
115                         "line %d: missing filename in \"replogfile ",
116                         lineno );
117                     fprintf( stderr, "<filename>\" line\n" );
118                     exit( EXIT_FAILURE );
119                 } else if ( cargc > 2 && *cargv[2] != '#' ) {
120                     fprintf( stderr,
121                         "line %d: extra cruft at the end of \"replogfile %s\"",
122                         lineno, cargv[1] );
123                     fprintf( stderr, "line (ignored)\n" );
124                 }
125                 strcpy( sglob->slapd_replogfile, cargv[1] );
126             }
127         } else if ( strcasecmp( cargv[0], "replica" ) == 0 ) {
128             add_replica( cargv, cargc );
129             
130             /* include another config file */
131         } else if ( strcasecmp( cargv[0], "include" ) == 0 ) {
132             char *savefname;
133             int savelineno;
134
135             if ( cargc < 2 ) {
136 #ifdef NEW_LOGGING
137                 LDAP_LOG( CONFIG, CRIT,
138                         "%s: line %d: missing filename in \"include "
139                         "<filename>\" line.\n", fname, lineno , 0 );
140 #else
141                 Debug( LDAP_DEBUG_ANY,
142         "%s: line %d: missing filename in \"include <filename>\" line\n",
143                         fname, lineno, 0 );
144 #endif
145                 
146                 return( 1 );
147             }
148             savefname = strdup( cargv[1] );
149             savelineno = lineno;
150             
151             if ( slurpd_read_config( savefname ) != 0 ) {
152                 return( 1 );
153             }
154                 
155             free( savefname );
156             lineno = savelineno - 1;
157
158         } else if ( strcasecmp( cargv[0], "replica-pidfile" ) == 0 ) {
159                 if ( cargc < 2 ) {
160 #ifdef NEW_LOGGING
161                         LDAP_LOG( CONFIG, CRIT, 
162                                 "%s: line %d missing file name in \"replica-pidfile <file>\" "
163                                 "line.\n", fname, lineno, 0 );
164 #else
165                         Debug( LDAP_DEBUG_ANY,
166             "%s: line %d: missing file name in \"replica-pidfile <file>\" line\n",
167                                 fname, lineno, 0 );
168 #endif
169
170                         return( 1 );
171                 }
172
173                 slurpd_pid_file = ch_strdup( cargv[1] );
174
175         } else if ( strcasecmp( cargv[0], "replica-argsfile" ) == 0 ) {
176                 if ( cargc < 2 ) {
177 #ifdef NEW_LOGGING
178                         LDAP_LOG( CONFIG, CRIT, 
179                                    "%s: %d: missing file name in "
180                                    "\"argsfile <file>\" line.\n",
181                                    fname, lineno, 0 );
182 #else
183                         Debug( LDAP_DEBUG_ANY,
184             "%s: line %d: missing file name in \"argsfile <file>\" line\n",
185                             fname, lineno, 0 );
186 #endif
187
188                         return( 1 );
189                 }
190
191                 slurpd_args_file = ch_strdup( cargv[1] );
192         }
193     }
194     fclose( fp );
195 #ifdef NEW_LOGGING
196     LDAP_LOG ( CONFIG, RESULTS, 
197                 "slurpd_read_config: Config: "
198                 "** configuration file successfully read and parsed\n", 0, 0, 0 );
199 #else
200     Debug( LDAP_DEBUG_CONFIG,
201             "Config: ** configuration file successfully read and parsed\n",
202             0, 0, 0 );
203 #endif
204     return 0;
205 }
206
207
208
209
210 /*
211  * Parse one line of input.
212  */
213 static void
214 parse_line(
215     char        *line
216 )
217 {
218     char *      token;
219
220     cargc = 0;
221     for ( token = strtok_quote( line, " \t" ); token != NULL;
222         token = strtok_quote( NULL, " \t" ) )
223     {
224         if ( cargc == cargv_size - 1 ) {
225             char **tmp;
226             tmp = ch_realloc( cargv, (cargv_size + ARGS_STEP) *
227                                sizeof(*cargv) );
228             if (tmp == NULL) {
229                 cargc = 0;
230                 return;
231             }
232             cargv = tmp;
233             cargv_size += ARGS_STEP;
234         }
235
236         cargv[cargc++] = token;
237     }
238     cargv[cargc] = NULL;
239 }
240
241
242
243
244 static char *
245 strtok_quote(
246     char *line,
247     char *sep
248 )
249 {
250     int         inquote;
251     char        *tmp;
252     static char *next;
253
254     if ( line != NULL ) {
255         next = line;
256     }
257     while ( *next && strchr( sep, *next ) ) {
258         next++;
259     }
260
261     if ( *next == '\0' ) {
262         next = NULL;
263         return( NULL );
264     }
265     tmp = next;
266
267     for ( inquote = 0; *next; ) {
268         switch ( *next ) {
269         case '"':
270             if ( inquote ) {
271                 inquote = 0;
272             } else {
273                 inquote = 1;
274             }
275             AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
276             break;
277
278         case '\\':
279             if ( next[1] )
280                 AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
281             next++;             /* dont parse the escaped character */
282             break;
283
284         default:
285             if ( ! inquote ) {
286                 if ( strchr( sep, *next ) != NULL ) {
287                     *next++ = '\0';
288                     return( tmp );
289                 }
290             }
291             next++;
292             break;
293         }
294     }
295
296     return( tmp );
297 }
298
299 #define CATLINE( buf )  { \
300     int len; \
301     len = strlen( buf ); \
302     while ( lcur + len + 1 > lmax ) { \
303         lmax += BUFSIZ; \
304         line = (char *) ch_realloc( line, lmax ); \
305     } \
306     strcpy( line + lcur, buf ); \
307     lcur += len; \
308 }
309
310
311
312 /*
313  * Get a line of input.
314  */
315 static char *
316 getline(
317     FILE *fp
318 )
319 {
320     char        *p;
321     static char buf[BUFSIZ];
322     static char *line;
323     static int  lmax, lcur;
324
325     lcur = 0;
326     CATLINE( buf );
327     while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
328         if ( (p = strchr( buf, '\n' )) != NULL ) {
329                 if( p > buf && p[-1] == '\r' ) --p;       
330                 *p = '\0';
331         }
332         lineno++;
333         if ( ! isspace( (unsigned char) buf[0] ) ) {
334             return( line );
335         }
336
337         /* change leading whitespace to space */
338         buf[0] = ' ';
339
340         CATLINE( buf );
341     }
342     buf[0] = '\0';
343
344     return( line[0] ? line : NULL );
345 }
346
347
348 /*
349  * Add a node to the array of replicas.
350  */
351 static void
352 add_replica(
353     char        **cargv,
354     int         cargc
355 )
356 {
357     int nr;
358
359     nr = ++sglob->num_replicas;
360     sglob->replicas = (Ri **) ch_realloc( sglob->replicas,
361             ( nr + 1 )  * sizeof( Re * ));
362     if ( sglob->replicas == NULL ) {
363         fprintf( stderr, "out of memory, add_replica\n" );
364         exit( EXIT_FAILURE );
365     }
366     sglob->replicas[ nr ] = NULL; 
367
368     if ( Ri_init( &(sglob->replicas[ nr - 1 ])) < 0 ) {
369         fprintf( stderr, "out of memory, Ri_init\n" );
370         exit( EXIT_FAILURE );
371     }
372     if ( parse_replica_line( cargv, cargc,
373             sglob->replicas[ nr - 1] ) < 0 ) {
374         /* Something bad happened - back out */
375         fprintf( stderr,
376             "Warning: failed to add replica \"%s:%d - ignoring replica\n",
377             sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
378             "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
379             sglob->replicas[ nr - 1 ]->ri_port );
380         sglob->replicas[ nr - 1] = NULL;
381         sglob->num_replicas--;
382     } else {
383 #ifdef NEW_LOGGING
384     LDAP_LOG ( CONFIG, RESULTS, 
385                 "add_replica: Config: ** successfully added replica \"%s%d\"\n", 
386                 sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
387                 "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
388                 sglob->replicas[ nr - 1 ]->ri_port, 0 );
389 #else
390         Debug( LDAP_DEBUG_CONFIG,
391                 "Config: ** successfully added replica \"%s:%d\"\n",
392                 sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
393                 "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
394                 sglob->replicas[ nr - 1 ]->ri_port, 0 );
395 #endif
396         sglob->replicas[ nr - 1]->ri_stel =
397                 sglob->st->st_add( sglob->st,
398                 sglob->replicas[ nr - 1 ] );
399         if ( sglob->replicas[ nr - 1]->ri_stel == NULL ) {
400             fprintf( stderr, "Failed to add status element structure\n" );
401             exit( EXIT_FAILURE );
402         }
403     }
404 }
405
406
407
408 /* 
409  * Parse a "replica" line from the config file.  replica lines should be
410  * in the following format:
411  * replica    host=<hostname:portnumber> binddn=<binddn>
412  *            bindmethod="simple" credentials=<creds>
413  *
414  * where:
415  * <hostname:portnumber> describes the host name and port number where the
416  * replica is running,
417  *
418  * <binddn> is the DN to bind to the replica slapd as,
419  *
420  * bindmethod is "simple", and
421  *
422  * <creds> are the credentials (e.g. password) for binddn.  <creds> are
423  * only used for bindmethod=simple.  
424  *
425  * The "replica" config file line may be split across multiple lines.  If
426  * a line begins with whitespace, it is considered a continuation of the
427  * previous line.
428  */
429 #define GOT_HOST        1
430 #define GOT_DN          2
431 #define GOT_METHOD      4
432 #define GOT_ALL         ( GOT_HOST | GOT_DN | GOT_METHOD )
433 #define GOT_MECH        8
434
435 static int
436 parse_replica_line( 
437     char        **cargv,
438     int         cargc,
439     Ri          *ri
440 )
441 {
442     int         gots = 0;
443     int         i;
444     char        *hp, *val;
445     LDAPURLDesc *ludp;
446
447     for ( i = 1; i < cargc; i++ ) {
448         if ( !strncasecmp( cargv[ i ], HOSTSTR, sizeof( HOSTSTR ) - 1 ) ) {
449                 if ( gots & GOT_HOST ) {
450                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
451                         fprintf( stderr, "file, too many host or uri names specified, line %d\n",
452                                 lineno );
453                         return -1;
454                 }       
455             val = cargv[ i ] + sizeof( HOSTSTR ); /* '\0' string terminator accounts for '=' */
456             if (( hp = strchr( val, ':' )) != NULL ) {
457                 *hp = '\0';
458                 hp++;
459                 ri->ri_port = atoi( hp );
460             }
461             if ( ri->ri_port <= 0 ) {
462                 ri->ri_port = 0;
463             }
464             ri->ri_hostname = strdup( val );
465             gots |= GOT_HOST;
466         } else if ( !strncasecmp( cargv[ i ], URISTR, sizeof( URISTR ) - 1 ) ) {
467                 if ( gots & GOT_HOST ) {
468                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
469                         fprintf( stderr, "file, too many host or uri names specified, line %d\n",
470                                 lineno );
471                         return -1;
472                 }               
473                 if ( ldap_url_parse( cargv[ i ] + sizeof( URISTR ), &ludp ) != LDAP_SUCCESS ) {
474                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
475                         fprintf( stderr, "file, bad uri format specified, line %d\n",
476                                 lineno );
477                         return -1;
478                 }
479                 if (ludp->lud_host == NULL) {
480                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
481                         fprintf( stderr, "file, missing uri hostname, line %d\n",
482                                 lineno );
483                         return -1;
484                 }
485                 ri->ri_hostname = strdup ( ludp->lud_host );
486                 ri->ri_port = ludp->lud_port;
487                 ri->ri_uri = strdup ( cargv[ i ] + sizeof( URISTR ) );          
488                 ldap_free_urldesc( ludp );                              
489             gots |= GOT_HOST;
490         } else if ( !strncasecmp( cargv[ i ], 
491                         ATTRSTR, sizeof( ATTRSTR ) - 1 ) ) {
492             /* ignore it */ ;
493         } else if ( !strncasecmp( cargv[ i ], 
494                         SUFFIXSTR, sizeof( SUFFIXSTR ) - 1 ) ) {
495             /* ignore it */ ;
496         } else if ( !strncasecmp( cargv[i], STARTTLSSTR, sizeof(STARTTLSSTR)-1 )) {
497             val = cargv[ i ] + sizeof( STARTTLSSTR );
498                 if( !strcasecmp( val, CRITICALSTR ) ) {
499                         ri->ri_tls = TLS_CRITICAL;
500                 } else {
501                         ri->ri_tls = TLS_ON;
502                 }
503         } else if ( !strncasecmp( cargv[ i ], TLSSTR, sizeof( TLSSTR ) - 1 ) ) {
504             val = cargv[ i ] + sizeof( TLSSTR );
505                 if( !strcasecmp( val, CRITICALSTR ) ) {
506                         ri->ri_tls = TLS_CRITICAL;
507                 } else {
508                         ri->ri_tls = TLS_ON;
509                 }
510         } else if ( !strncasecmp( cargv[ i ],
511                         BINDDNSTR, sizeof( BINDDNSTR ) - 1 ) ) { 
512             val = cargv[ i ] + sizeof( BINDDNSTR );
513             ri->ri_bind_dn = strdup( val );
514             gots |= GOT_DN;
515         } else if ( !strncasecmp( cargv[ i ], BINDMETHSTR,
516                 sizeof( BINDMETHSTR ) - 1 ) ) {
517             val = cargv[ i ] + sizeof( BINDMETHSTR );
518             if ( !strcasecmp( val, KERBEROSSTR )) {
519             fprintf( stderr, "Error: a bind method of \"kerberos\" was\n" );
520             fprintf( stderr, "specified in the slapd configuration file.\n" );
521             fprintf( stderr, "slurpd no longer supports Kerberos.\n" );
522             exit( EXIT_FAILURE );
523             } else if ( !strcasecmp( val, SIMPLESTR )) {
524                 ri->ri_bind_method = AUTH_SIMPLE;
525                 gots |= GOT_METHOD;
526             } else if ( !strcasecmp( val, SASLSTR )) {
527                 ri->ri_bind_method = AUTH_SASL;
528                 gots |= GOT_METHOD;
529             } else {
530                 ri->ri_bind_method = -1;
531             }
532         } else if ( !strncasecmp( cargv[ i ], 
533                         SASLMECHSTR, sizeof( SASLMECHSTR ) - 1 ) ) {
534             val = cargv[ i ] + sizeof( SASLMECHSTR );
535             gots |= GOT_MECH;
536             ri->ri_saslmech = strdup( val );
537         } else if ( !strncasecmp( cargv[ i ], 
538                         CREDSTR, sizeof( CREDSTR ) - 1 ) ) {
539             val = cargv[ i ] + sizeof( CREDSTR );
540             ri->ri_password = strdup( val );
541         } else if ( !strncasecmp( cargv[ i ], 
542                         SECPROPSSTR, sizeof( SECPROPSSTR ) - 1 ) ) {
543             val = cargv[ i ] + sizeof( SECPROPSSTR );
544             ri->ri_secprops = strdup( val );
545         } else if ( !strncasecmp( cargv[ i ], 
546                         REALMSTR, sizeof( REALMSTR ) - 1 ) ) {
547             val = cargv[ i ] + sizeof( REALMSTR );
548             ri->ri_realm = strdup( val );
549         } else if ( !strncasecmp( cargv[ i ], 
550                         AUTHCSTR, sizeof( AUTHCSTR ) - 1 ) ) {
551             val = cargv[ i ] + sizeof( AUTHCSTR );
552             ri->ri_authcId = strdup( val );
553         } else if ( !strncasecmp( cargv[ i ], 
554                         OLDAUTHCSTR, sizeof( OLDAUTHCSTR ) - 1 ) ) {
555             /* Old authcID is provided for some backwards compatibility */
556             val = cargv[ i ] + sizeof( OLDAUTHCSTR );
557             ri->ri_authcId = strdup( val );
558         } else if ( !strncasecmp( cargv[ i ], 
559                         AUTHZSTR, sizeof( AUTHZSTR ) - 1 ) ) {
560             val = cargv[ i ] + sizeof( AUTHZSTR );
561             ri->ri_authzId = strdup( val );
562         } else if ( !strncasecmp( cargv[ i ], 
563                         SRVTABSTR, sizeof( SRVTABSTR ) - 1 ) ) {
564             val = cargv[ i ] + sizeof( SRVTABSTR );
565             if ( ri->ri_srvtab != NULL ) {
566                 free( ri->ri_srvtab );
567             }
568             ri->ri_srvtab = strdup( val );
569         } else {
570             fprintf( stderr, 
571                     "Error: parse_replica_line: unknown keyword \"%s\"\n",
572                     cargv[ i ] );
573         }
574     }
575     
576         if ( ri->ri_bind_method == AUTH_SASL) {
577                 if ((gots & GOT_MECH) == 0) {
578                         fprintf( stderr, "Error: \"replica\" line needs SASLmech flag in " );
579                         fprintf( stderr, "slapd config file, line %d\n", lineno );
580                         return -1;
581                 }
582         }
583         else if ( gots != GOT_ALL ) {
584                 fprintf( stderr, "Error: Malformed \"replica\" line in slapd " );
585                 fprintf( stderr, "config file, line %d\n", lineno );
586                 return -1;
587         }
588     return 0;
589 }
590