2 * (c) Copyright 1992 by Panagiotis Tsirigotis
3 * (c) Sections Copyright 1998-2001 by Rob Braun
4 * All rights reserved. The file named COPYRIGHT specifies the terms
5 * and conditions for redistribution.
10 #include <sys/types.h>
28 #include "includedir.h"
38 * A NULL value for the name field marks the end of the table
40 * The 3rd value is the number of attribute values.
41 * If the number is positive, exactly that many values must be specified.
42 * If the number is -1, 0 or more values may be specified.
43 * If the number is -2, 0 or more values may be specified and the operators
44 * '+=' and '-=' may be used.
46 static const struct attribute service_attributes[] =
48 { "socket_type", A_SOCKET_TYPE, 1, socket_type_parser },
49 { "protocol", A_PROTOCOL, 1, protocol_parser },
50 { "wait", A_WAIT, 1, wait_parser },
51 { "user", A_USER, 1, user_parser },
52 { "group", A_GROUP, 1, group_parser },
53 { "server", A_SERVER, 1, server_parser },
54 { "server_args", A_SERVER_ARGS, -1, server_args_parser },
55 { "instances", A_INSTANCES, 1, instances_parser },
56 { "log_on_success", A_LOG_ON_SUCCESS,-2, log_on_success_parser },
57 { "log_on_failure", A_LOG_ON_FAILURE,-2, log_on_failure_parser },
58 { "log_type", A_LOG_TYPE, -1, log_type_parser },
59 { "only_from", A_ONLY_FROM, -2, only_from_parser },
60 { "no_access", A_NO_ACCESS, -2, no_access_parser },
61 { "access_times", A_ACCESS_TIMES, -1, access_times_parser },
62 { "type", A_TYPE, -1, type_parser },
64 { "rpc_version", A_RPC_VERSION, 1, rpc_version_parser },
65 { "rpc_number", A_RPC_NUMBER, 1, rpc_number_parser },
67 { "id", A_ID, 1, id_parser },
68 { "env", A_ENV, -2, env_parser },
69 { "port", A_PORT, 1, port_parser },
70 { "passenv", A_PASSENV, -2, passenv_parser },
71 { "flags", A_FLAGS, -1, flags_parser },
72 { "nice", A_NICE, 1, nice_parser },
73 { "redirect", A_REDIR, 2, redir_parser },
74 { "banner", A_BANNER, 1, banner_parser },
75 { "bind", A_BIND, 1, bind_parser },
76 { "interface", A_BIND, 1, bind_parser },
77 { "per_source", A_PER_SOURCE, 1, per_source_parser },
78 { "groups", A_GROUPS, 1, groups_parser },
79 { "banner_success", A_BANNER_SUCCESS, 1, banner_success_parser },
80 { "banner_fail", A_BANNER_FAIL, 1, banner_fail_parser },
81 { "cps", A_CPS, 2, cps_parser },
82 { "disable", A_SVCDISABLE, 1, svcdisable_parser },
84 { "max_load", A_MAX_LOAD, 1, max_load_parser },
87 { "rlimit_as", A_RLIMIT_AS, 1, rlim_as_parser },
90 { "rlimit_cpu", A_RLIMIT_CPU, 1, rlim_cpu_parser },
93 { "rlimit_data", A_RLIMIT_DATA, 1, rlim_data_parser },
96 { "rlimit_rss", A_RLIMIT_RSS, 1, rlim_rss_parser },
99 { "rlimit_stack", A_RLIMIT_STACK, 1, rlim_stack_parser },
101 { "v6only", A_V6ONLY, 1, v6only_parser },
102 { "deny_time", A_DENY_TIME, 1, deny_time_parser },
103 { "umask", A_UMASK, 1, umask_parser },
105 { "mdns", A_MDNS, 1, mdns_parser },
108 { "libwrap", A_LIBWRAP, 1, libwrap_parser },
110 { NULL, A_NONE, -1, NULL }
113 static const struct attribute default_attributes[] =
115 { "log_type", A_LOG_TYPE, -2, log_type_parser },
116 { "log_on_success", A_LOG_ON_SUCCESS, -2, log_on_success_parser },
117 { "log_on_failure", A_LOG_ON_FAILURE, -2, log_on_failure_parser },
118 { "disabled", A_DISABLED, -2, disabled_parser },
119 { "no_access", A_NO_ACCESS, -2, no_access_parser },
120 { "only_from", A_ONLY_FROM, -2, only_from_parser },
121 { "instances", A_INSTANCES, 1, instances_parser },
122 { "passenv", A_PASSENV, -2, passenv_parser },
123 { "banner", A_BANNER, 1, banner_parser },
124 { "bind", A_BIND, 1, bind_parser },
125 { "interface", A_BIND, 1, bind_parser },
126 { "per_source", A_PER_SOURCE, 1, per_source_parser },
127 { "groups", A_GROUPS, 1, groups_parser },
128 { "banner_success", A_BANNER_SUCCESS, 1, banner_success_parser },
129 { "banner_fail", A_BANNER_FAIL, 1, banner_fail_parser },
130 { "cps", A_CPS, 2, cps_parser },
131 { "enabled", A_ENABLED, -2, enabled_parser },
133 { "max_load", A_MAX_LOAD, 1, max_load_parser },
135 { "v6only", A_V6ONLY, 1, v6only_parser },
136 { "umask", A_UMASK, 1, umask_parser },
138 { "mdns", A_MDNS, 1, mdns_parser },
140 { NULL, A_NONE, 0, NULL }
144 #define MODIFIABLE( ap ) ( (ap)->a_nvalues == -2 )
145 #define FIXED_VALUES( ap ) ( (ap)->a_nvalues > 0 )
148 const char *current_file = NULL;
150 static void get_service_entry( int fd, pset_h, const char *,
151 struct service_config * );
152 static void fill_attribute( unsigned attr_id, struct service_config *scp,
153 struct service_config *def );
154 static entry_e find_next_entry(int , char **) ;
155 static status_e parse_entry(entry_e, int, struct service_config *) ;
158 * Given the id, return the name (only the service attributes are searched)
160 const char *attr_name_lookup( unsigned int id )
162 const struct attribute *ap ;
164 for ( ap = &service_attributes[ 0 ] ; ap->a_name ; ap++ )
165 if ( id == ap->a_id )
166 return( ap->a_name ) ;
167 return( CHAR_NULL ) ;
182 * Parsing rules and rationale
184 * The parse_conf_file function parses a configuration file identified
185 * by a file descriptor and fills the service table and defaults of
186 * the configuration argument.
188 * The configuration information for a service comes from 2 sources: the
189 * service entry and, possibly, the defaults entry.
190 * Attributes specified in the defaults entry can be overriden or
191 * modified by the service entry. Modifiable attributes can be identified
192 * by the value -2 for the 'a_nvalues' field of the struct attribute. Those
193 * attributes with a different value for 'a_nvalues' are overridable ones.
194 * The modifiable attributes are filled in only if the entry tries to modify
199 * Read the configuration file (descriptor fd) and place all
200 * services found there in the configuration.
202 void parse_conf_file( int fd, struct configuration *confp, const char *filename)
204 pset_h sconfs = CNF_SERVICE_CONFS( confp ) ;
205 struct service_config *default_config = CNF_DEFAULTS( confp ) ;
206 boolean_e found_defaults = NO ;
207 struct service_config default_default_config ;
208 const char *func = "parse_conf_file" ;
212 current_file = filename;
213 CLEAR( default_default_config ) ;
218 char *service_name = NULL;
221 * if find_next_entry is successful, service_name
222 * will point to malloc'ed memory
224 entry_type = find_next_entry( fd, &service_name ) ;
225 switch ( entry_type )
229 int saved_line_count = line_count;
230 incfd = open(service_name, O_RDONLY);
232 parsemsg( LOG_ERR, func,
233 "Unable to open included configuration file: %s",
237 parsemsg( LOG_DEBUG,func,
238 "Reading included configuration file: %s",service_name);
239 parse_conf_file(incfd, confp, service_name);
241 * parse_conf_file eventually calls Srdline, try Sclosing it
245 /* Restore since we've returned from included file */
246 current_file = filename;
247 line_count = saved_line_count;
250 case INCLUDEDIR_ENTRY:
252 int saved_line_count = line_count;
253 handle_includedir(service_name, confp);
254 current_file = filename;
255 line_count = saved_line_count;
259 get_service_entry( fd, sconfs, service_name, default_config ) ;
262 if ( found_defaults == YES )
264 parsemsg( LOG_ERR, func,
265 "only 1 defaults entry is allowed. This entry will be ignored" ) ;
268 else if ( parse_entry( DEFAULTS_ENTRY, fd,
269 default_config ) == OK ) {
270 found_defaults = YES ;
272 * We must check bind_address to see if it was deferred.
274 if (SC_SPECIFIED( default_config, A_BIND) &&
275 SC_BIND_ADDR(default_config) == NULL)
276 M_CLEAR( default_config->sc_specified_attributes, A_BIND ) ;
294 * Find the next service entry.
295 * Look for a line of the form:
297 * <white-space> service <white-space> <service_name>
299 * followed by a line containing only the ENTRY_BEGIN character
301 static entry_e find_next_entry( int fd, char **snamep )
306 entry_e entry_type=0;
307 char *line = next_line( fd ) ;
308 const char *func = "find_next_entry" ;
310 if ( line == CHAR_NULL )
313 strp = str_parse( line, " \t", STR_RETURN_ERROR, INT_NULL ) ;
316 parsemsg( LOG_CRIT, func, "str_parse failed" ) ;
317 return( BAD_ENTRY ) ;
320 if ( ( p = str_component( strp ) ) == CHAR_NULL )
323 * This shouldn't happen since it implies that there is a bug
326 parsemsg( LOG_WARNING, func, "empty line" ) ;
327 str_endparse( strp ) ;
328 return( BAD_ENTRY ) ;
334 if ( EQ( p, KW_SERVICE ) || EQ( p, KW_INCLUDE ) || EQ(p, KW_INCLUDEDIR))
336 if ( EQ( p, KW_INCLUDE ))
337 entry_type = INCLUDE_ENTRY;
338 else if ( EQ( p, KW_INCLUDEDIR ))
339 entry_type = INCLUDEDIR_ENTRY;
342 * Now get the service name
344 if ( ( p = str_component( strp ) ) == CHAR_NULL )
346 parsemsg( LOG_ERR, func, "service name missing" ) ;
347 str_endparse( strp ) ;
348 return( BAD_ENTRY ) ;
351 sname = new_string( p ) ;
352 if ( sname == CHAR_NULL )
354 out_of_memory( func ) ;
355 str_endparse( strp ) ;
356 return( BAD_ENTRY ) ;
358 str_endparse( strp ) ;
360 if( (entry_type == INCLUDE_ENTRY) ||
361 (entry_type == INCLUDEDIR_ENTRY))
364 return( entry_type ) ;
367 entry_type = SERVICE_ENTRY ;
369 else if ( EQ( p, KW_DEFAULTS ) )
371 str_endparse( strp ) ;
372 entry_type = DEFAULTS_ENTRY ;
376 parsemsg( LOG_ERR, func, "missing service keyword" ) ;
377 str_endparse( strp ) ;
378 return( BAD_ENTRY ) ;
382 * Now look for ENTRY_BEGIN
384 line = next_line( fd ) ;
385 if ( line == NULL || ! line_has_only_1_char( line, ENTRY_BEGIN ) )
387 parsemsg( LOG_ERR, func,
388 "Service %s: missing '%c'", sname, ENTRY_BEGIN ) ;
389 if ( entry_type == SERVICE_ENTRY )
391 return( BAD_ENTRY ) ;
394 return( entry_type ) ;
399 * Get a service entry. Steps:
401 * 1. Parse entry attributes
402 * 2. Determine service id
403 * 3. Insert entry in table
405 static void get_service_entry( int fd,
408 struct service_config *defaults )
410 struct service_config *scp ;
411 const char *func = "get_service_entry" ;
413 scp = sc_alloc( name ) ;
420 /* Now fill in default attributes if given. */
421 if ( SC_SPECIFIED( defaults, A_LOG_ON_SUCCESS ) &&
422 ! SC_IS_PRESENT( scp, A_LOG_ON_SUCCESS) )
423 fill_attribute( A_LOG_ON_SUCCESS, scp, defaults ) ;
424 if ( SC_SPECIFIED( defaults, A_LOG_ON_FAILURE ) &&
425 ! SC_IS_PRESENT( scp, A_LOG_ON_FAILURE ) )
426 fill_attribute( A_LOG_ON_FAILURE, scp, defaults ) ;
427 if ( SC_SPECIFIED( defaults, A_ONLY_FROM ) &&
428 ! SC_IS_PRESENT( scp, A_ONLY_FROM ) )
429 fill_attribute( A_ONLY_FROM, scp, defaults ) ;
430 if ( SC_SPECIFIED( defaults, A_NO_ACCESS ) &&
431 ! SC_IS_PRESENT( scp, A_NO_ACCESS ) )
432 fill_attribute( A_NO_ACCESS, scp, defaults ) ;
433 if ( SC_SPECIFIED( defaults, A_PASSENV ) &&
434 ! SC_IS_PRESENT( scp, A_PASSENV ) )
435 fill_attribute( A_PASSENV, scp, defaults ) ;
436 if ( SC_SPECIFIED( defaults, A_ACCESS_TIMES ) &&
437 ! SC_IS_PRESENT( scp, A_ACCESS_TIMES ) )
438 fill_attribute( A_ACCESS_TIMES, scp, defaults ) ;
439 if ( SC_SPECIFIED( defaults, A_BANNER ) &&
440 ! SC_IS_PRESENT( scp, A_BANNER ) )
441 fill_attribute( A_BANNER, scp, defaults ) ;
442 if ( SC_SPECIFIED( defaults, A_BANNER_SUCCESS ) &&
443 ! SC_IS_PRESENT( scp, A_BANNER_SUCCESS ) )
444 fill_attribute( A_BANNER_SUCCESS, scp, defaults ) ;
445 if ( SC_SPECIFIED( defaults, A_BANNER_FAIL ) &&
446 ! SC_IS_PRESENT( scp, A_BANNER_FAIL ) )
447 fill_attribute( A_BANNER_FAIL, scp, defaults ) ;
449 if ( parse_entry( SERVICE_ENTRY, fd, scp ) == FAILED )
457 * If no service id was specified, set it equal to the service name
459 if ( ! SC_SPECIFIED( scp, A_ID ) ) {
460 if ( (SC_ID(scp) = new_string( SC_NAME(scp) )) )
461 SC_PRESENT( scp, A_ID ) ;
464 out_of_memory( func ) ;
470 if ( ! (pset_add( sconfs, scp )) )
472 out_of_memory( func ) ;
481 * Fill in scp the value of the modifiable attribute attr from def.
482 * These modifiable attributes are:
483 * log_on_{success,failure}
488 static void fill_attribute( unsigned attr_id,
489 struct service_config *scp,
490 struct service_config *def )
494 case A_LOG_ON_SUCCESS:
495 M_ASSIGN( SC_LOG_ON_SUCCESS(scp), SC_LOG_ON_SUCCESS(def) ) ;
496 SC_PRESENT( scp, A_LOG_ON_SUCCESS ) ;
499 case A_LOG_ON_FAILURE:
500 M_ASSIGN( SC_LOG_ON_FAILURE(scp), SC_LOG_ON_FAILURE(def) ) ;
501 SC_PRESENT( scp, A_LOG_ON_FAILURE ) ;
505 if ( addrlist_copy( SC_ONLY_FROM(def), &SC_ONLY_FROM(scp) ) == OK )
506 SC_PRESENT( scp, A_ONLY_FROM ) ;
510 if ( addrlist_copy( SC_NO_ACCESS(def), &SC_NO_ACCESS(scp) ) == OK )
511 SC_PRESENT( scp, A_NO_ACCESS ) ;
515 if ( copy_pset( SC_PASS_ENV_VARS(def),
516 &SC_PASS_ENV_VARS(scp), 0 ) == OK )
517 SC_PRESENT( scp, A_PASSENV ) ;
521 if ( copy_pset( SC_ACCESS_TIMES(def),
522 &SC_ACCESS_TIMES(scp), 0 ) == OK )
523 SC_PRESENT( scp, A_ACCESS_TIMES ) ;
527 if ((SC_BANNER(scp) = new_string(SC_BANNER(def))) != NULL)
528 SC_PRESENT( scp, A_BANNER );
531 case A_BANNER_SUCCESS:
532 if ((SC_BANNER_SUCCESS(scp) = new_string(SC_BANNER_SUCCESS(def)))
534 SC_PRESENT( scp, A_BANNER_SUCCESS );
538 if ((SC_BANNER_FAIL(scp) = new_string(SC_BANNER_FAIL(def))) != NULL)
539 SC_PRESENT( scp, A_BANNER_FAIL );
546 * Find the attribute with the specified name
548 static const struct attribute *attr_lookup(
549 const struct attribute attr_array[], const char *attr_name )
551 const struct attribute *ap ;
552 const char *func = "attr_lookup" ;
554 for ( ap = &attr_array[ 0 ] ; ap->a_name ; ap++ )
555 if ( EQ( attr_name, ap->a_name ) )
557 if ( attr_array == service_attributes )
558 parsemsg( LOG_WARNING, func, "bad service attribute: %s", attr_name ) ;
560 parsemsg( LOG_WARNING, func,
561 "attribute: %s should not be in default section", attr_name ) ;
567 * Identify the attribute in <attr_name>.
570 * 1) the attribute has been defined already
571 * 2) the value count is correct
572 * 3) the assign op is appropriate
574 * Invoke appropriate parser.
576 * This function will return FAILED only if its in the default section
577 * and an attribute cannot be ID'd. Otherwise, it returns OK.
579 static status_e identify_attribute( entry_e entry_type,
580 struct service_config *scp,
581 const char *attr_name,
585 const struct attribute *ap ;
586 const char *func = "identify_attribute" ;
588 if ( entry_type == SERVICE_ENTRY )
589 ap = attr_lookup( service_attributes, attr_name ) ;
591 ap = attr_lookup( default_attributes, attr_name ) ;
594 return OK; /* We simply ignore keywords not on the list */
596 if ( ! MODIFIABLE( ap ) )
598 if ( SC_SPECIFIED( scp, ap->a_id ) )
600 parsemsg( LOG_WARNING, func, "Service %s: attribute already set: %s",
601 SC_NAME(scp), attr_name ) ;
607 parsemsg( LOG_WARNING, func,
608 "Service %s: operator '%s' cannot be used for attribute '%s'",
609 SC_NAME(scp), ( op == PLUS_EQ ) ? "+=" : "-=", attr_name ) ;
613 else /* modifiable attribute */
616 * For the defaults entry, '=' and '+=' have the same meaning
618 if ( entry_type == DEFAULTS_ENTRY && op == SET_EQ )
622 if ( FIXED_VALUES( ap ) &&
623 (unsigned)ap->a_nvalues != pset_count( attr_values ) )
625 parsemsg( LOG_WARNING, func,
626 "attribute %s expects %d values and %d values were specified",
627 attr_name, ap->a_nvalues, pset_count( attr_values ) ) ;
631 if ( (*ap->a_parser)( attr_values, scp, op ) == OK )
632 { /* This is the normal path. */
633 SC_SPECIFY( scp, ap->a_id ) ;
635 else if ( entry_type == SERVICE_ENTRY )
637 parsemsg( LOG_ERR, func,
638 "Error parsing attribute %s - DISABLING SERVICE", attr_name ) ;
642 * We are in the default section and an error was detected. At
643 * this point, we should terminate since whatever attribute
644 * was trying to be specified cannot be propagated.
646 else if ( !debug.on )
654 * Read the entry line-by-line and add the information in scp
655 * Use defaults to initialize modifiable entry fields.
657 static status_e parse_entry( entry_e entry_type,
659 struct service_config *scp )
661 static pset_h attr_values = NULL;
665 const char *func = "get_attributes" ;
667 if ( ! attr_values && ( attr_values = pset_create( 10, 10 ) ) == NULL )
669 out_of_memory( func ) ;
675 line = next_line( fd ) ;
676 if ( line == CHAR_NULL )
678 parsemsg( LOG_ERR, func, "incomplete entry" ) ;
682 if ( line_has_only_1_char( line, ENTRY_END ) )
685 if ( parse_line( line, &attr_name, &op, attr_values ) == FAILED )
687 pset_clear( attr_values ) ;
691 if (identify_attribute( entry_type,
692 scp, attr_name, op, attr_values ) == FAILED )
695 * An error was detected in the default section. We will terminate
696 * since whatever attribute being specified cannot be propagated.
699 "A fatal error was encountered while parsing the default section."
700 " xinetd will exit.");
704 pset_clear( attr_values ) ;