fixes init script for non ipv6 enabled systems #472755
[packages/xinetd.git] / xinetd / parse.c
1 /*
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.
6  */
7
8
9 #include "config.h"
10 #include <sys/types.h>
11 #include <dirent.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <netdb.h>
16 #include <syslog.h>
17 #include <memory.h>
18 #include <fcntl.h>
19 #include <grp.h>
20 #include <pwd.h>
21
22 #include "str.h"
23 #include "parse.h"
24 #include "parsers.h"
25 #include "msg.h"
26 #include "parsesup.h"
27 #include "addr.h"
28 #include "includedir.h"
29 #include "main.h"
30 #include "sio.h"
31
32 #ifndef NAME_MAX
33 #define NAME_MAX 255
34 #endif
35
36
37 /*
38  * A NULL value for the name field marks the end of the table
39  *
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.
45  */
46 static const struct attribute service_attributes[] =
47 {
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            },
63 #ifndef NO_RPC
64    { "rpc_version",    A_RPC_VERSION,    1,  rpc_version_parser     },
65    { "rpc_number",     A_RPC_NUMBER,     1,  rpc_number_parser      },
66 #endif
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      },
83 #ifdef HAVE_LOADAVG
84    { "max_load",       A_MAX_LOAD,       1,  max_load_parser        },
85 #endif
86 #ifdef RLIMIT_AS
87    { "rlimit_as",      A_RLIMIT_AS,      1,  rlim_as_parser         },
88 #endif
89 #ifdef RLIMIT_CPU
90    { "rlimit_cpu",     A_RLIMIT_CPU,     1,  rlim_cpu_parser        },
91 #endif
92 #ifdef RLIMIT_DATA
93    { "rlimit_data",    A_RLIMIT_DATA,    1,  rlim_data_parser       },
94 #endif
95 #ifdef RLIMIT_RSS
96    { "rlimit_rss",     A_RLIMIT_RSS,     1,  rlim_rss_parser        },
97 #endif
98 #ifdef RLIMIT_STACK
99    { "rlimit_stack",   A_RLIMIT_STACK,   1,  rlim_stack_parser      },
100 #endif
101    { "v6only",         A_V6ONLY,         1,  v6only_parser          },
102    { "deny_time",      A_DENY_TIME,      1,  deny_time_parser       },
103    { "umask",          A_UMASK,          1,  umask_parser           },
104 #ifdef HAVE_MDNS
105    { "mdns",           A_MDNS,           1,  mdns_parser            },
106 #endif
107 #ifdef LIBWRAP
108    { "libwrap",        A_LIBWRAP,        1, libwrap_parser          },
109 #endif
110    { NULL,             A_NONE,          -1,  NULL                   }
111 } ;
112
113 static const struct attribute default_attributes[] =
114 {
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        },
132 #ifdef HAVE_LOADAVG
133    { "max_load",        A_MAX_LOAD,        1,   max_load_parser       },
134 #endif
135    { "v6only",          A_V6ONLY,         1,    v6only_parser         },
136    { "umask",           A_UMASK,          1,    umask_parser          },
137 #ifdef HAVE_MDNS
138    { "mdns",            A_MDNS,           1,    mdns_parser           },
139 #endif
140    { NULL,              A_NONE,           0,    NULL                  }
141 } ;
142
143
144 #define MODIFIABLE( ap )       ( (ap)->a_nvalues == -2 )
145 #define FIXED_VALUES( ap )         ( (ap)->a_nvalues > 0 )
146
147 int line_count ;
148 const char *current_file = NULL;
149
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 *) ;
156
157 /*
158  * Given the id, return the name (only the service attributes are searched)
159  */
160 const char *attr_name_lookup( unsigned int id )
161 {
162    const struct attribute *ap ;
163
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 ) ;
168 }
169
170
171 void parse_end(void)
172 {
173    endprotoent() ;
174    endpwent() ;
175    endgrent() ;
176    endnetent() ;
177    endhostent() ;
178 }
179
180
181 /*
182  * Parsing rules and rationale
183  *
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.
187  *
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
195  * them.
196  */
197
198 /*
199  * Read the configuration file (descriptor fd) and place all
200  * services found there in the configuration.
201  */
202 void parse_conf_file( int fd, struct configuration *confp, const char *filename)
203 {
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" ;
209    int                      incfd;
210
211    line_count = 0 ;
212    current_file = filename;
213    CLEAR( default_default_config ) ;
214
215    for ( ;; )
216    {
217       entry_e   entry_type ;
218       char      *service_name  = NULL;
219
220       /*
221        * if find_next_entry is successful, service_name
222        * will point to malloc'ed memory
223        */
224       entry_type = find_next_entry( fd, &service_name ) ;
225       switch ( entry_type )
226       {
227       case INCLUDE_ENTRY:
228          {
229             int saved_line_count = line_count;
230             incfd = open(service_name, O_RDONLY);
231             if( incfd < 0 ) {
232                parsemsg( LOG_ERR, func, 
233                   "Unable to open included configuration file: %s", 
234                   service_name);
235                break;
236             }
237             parsemsg( LOG_DEBUG,func,
238                "Reading included configuration file: %s",service_name);
239             parse_conf_file(incfd, confp, service_name);
240             /*
241              * parse_conf_file eventually calls Srdline, try Sclosing it
242              * to unmmap memory.
243              */
244             Sclose(incfd);
245             /* Restore since we've returned from included file */
246             current_file = filename;
247             line_count = saved_line_count;
248          }
249          break;
250       case INCLUDEDIR_ENTRY:
251          {
252             int saved_line_count = line_count;
253             handle_includedir(service_name, confp);
254             current_file = filename;
255             line_count = saved_line_count;
256          }
257          break;
258       case SERVICE_ENTRY:
259          get_service_entry( fd, sconfs, service_name, default_config ) ; 
260          break ;
261       case DEFAULTS_ENTRY:
262          if ( found_defaults == YES )
263          {
264             parsemsg( LOG_ERR, func,
265             "only 1 defaults entry is allowed. This entry will be ignored" ) ;
266             skip_entry( fd ) ;
267          }
268          else if ( parse_entry( DEFAULTS_ENTRY, fd,
269                            default_config ) == OK ) {
270             found_defaults = YES ;
271             /*
272              * We must check bind_address to see if it was deferred. 
273              */
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 ) ;
277          }
278          break ;
279          
280       case BAD_ENTRY:
281          skip_entry( fd ) ;
282          break ;
283
284       case NO_ENTRY:
285          return ;
286       }
287       if (service_name)
288          free(service_name);
289    }
290 }
291
292
293 /*
294  * Find the next service entry.
295  * Look for a line of the form:
296  *
297  *      <white-space> service <white-space> <service_name>
298  *
299  * followed by a line containing only the ENTRY_BEGIN character
300  */
301 static entry_e find_next_entry( int fd, char **snamep )
302 {
303    char           *p ;
304    str_h           strp ;
305    char           *sname = NULL;
306    entry_e         entry_type=0;
307    char           *line = next_line( fd ) ;
308    const char     *func = "find_next_entry" ;
309
310    if ( line == CHAR_NULL )
311       return( NO_ENTRY ) ;
312
313    strp = str_parse( line, " \t", STR_RETURN_ERROR, INT_NULL ) ;
314    if ( strp == NULL )
315    {
316       parsemsg( LOG_CRIT, func, "str_parse failed" ) ;
317       return( BAD_ENTRY ) ;
318    }
319
320    if ( ( p = str_component( strp ) ) == CHAR_NULL )
321    {
322       /*
323        * This shouldn't happen since it implies that there is a bug
324        * in next_line
325        */
326       parsemsg( LOG_WARNING, func, "empty line" ) ;
327       str_endparse( strp ) ;
328       return( BAD_ENTRY ) ;
329    }
330
331    /*
332     * Look for a keyword
333     */
334    if ( EQ( p, KW_SERVICE ) || EQ( p, KW_INCLUDE ) || EQ(p, KW_INCLUDEDIR))
335    {
336       if ( EQ( p, KW_INCLUDE ))
337          entry_type = INCLUDE_ENTRY;
338       else if ( EQ( p, KW_INCLUDEDIR ))
339          entry_type = INCLUDEDIR_ENTRY;
340
341       /*
342        * Now get the service name
343        */
344       if ( ( p = str_component( strp ) ) == CHAR_NULL )
345       {
346          parsemsg( LOG_ERR, func, "service name missing" ) ;
347          str_endparse( strp ) ;
348          return( BAD_ENTRY ) ;
349       }
350    
351       sname = new_string( p ) ;
352       if ( sname == CHAR_NULL )
353       {
354          out_of_memory( func ) ;
355          str_endparse( strp ) ;
356          return( BAD_ENTRY ) ;
357       }
358       str_endparse( strp ) ;
359
360       if( (entry_type == INCLUDE_ENTRY) || 
361           (entry_type == INCLUDEDIR_ENTRY))
362       {
363          *snamep = sname ;
364          return( entry_type ) ;
365       }
366       else
367          entry_type = SERVICE_ENTRY ;
368    }
369    else if ( EQ( p, KW_DEFAULTS ) )
370    {
371       str_endparse( strp ) ;
372       entry_type = DEFAULTS_ENTRY ;
373    }
374    else
375    {
376       parsemsg( LOG_ERR, func, "missing service keyword" ) ;
377       str_endparse( strp ) ;
378       return( BAD_ENTRY ) ;
379    }
380
381    /*
382     * Now look for ENTRY_BEGIN
383     */
384    line = next_line( fd ) ;
385    if ( line == NULL || ! line_has_only_1_char( line, ENTRY_BEGIN ) )
386    {
387       parsemsg( LOG_ERR, func,
388          "Service %s: missing '%c'", sname, ENTRY_BEGIN ) ;
389       if ( entry_type == SERVICE_ENTRY )
390          free( sname ) ;
391       return( BAD_ENTRY ) ;
392    }
393    *snamep = sname ;
394    return( entry_type ) ;
395 }
396
397
398 /*
399  * Get a service entry. Steps:
400  *
401  *      1. Parse entry attributes
402  *      2. Determine service id
403  *      3. Insert entry in table
404  */
405 static void get_service_entry( int fd, 
406                                 pset_h sconfs,
407                                 const char *name,
408                                 struct service_config *defaults )
409 {
410    struct service_config   *scp ;
411    const char              *func = "get_service_entry" ;
412
413    scp = sc_alloc( name ) ;
414    if ( scp == NULL )
415    {
416       skip_entry( fd ) ;
417       return ;
418    }
419
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 ) ;
448   
449    if ( parse_entry( SERVICE_ENTRY, fd, scp ) == FAILED )
450    {
451       sc_free( scp ) ;
452       skip_entry( fd ) ;
453       return ;
454    }
455
456    /*
457     * If no service id was specified, set it equal to the service name
458     */
459    if ( ! SC_SPECIFIED( scp, A_ID ) ) {
460       if ( (SC_ID(scp) = new_string( SC_NAME(scp) )) )
461          SC_PRESENT( scp, A_ID ) ;
462       else
463       {
464          out_of_memory( func ) ;
465          sc_free( scp ) ;
466          return ;
467       }
468    }
469
470    if ( ! (pset_add( sconfs, scp )) )
471    {
472       out_of_memory( func ) ;
473       sc_free( scp ) ;
474       return ;
475    }
476
477 }
478
479
480 /*
481  * Fill in scp the value of the modifiable attribute attr from def.
482  * These modifiable attributes are:
483  *      log_on_{success,failure}
484  *      only_from
485  *      no_access
486  *      passenv
487  */
488 static void fill_attribute( unsigned attr_id, 
489                              struct service_config *scp, 
490                              struct service_config *def )
491 {
492    switch ( attr_id )
493    {
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 ) ;
497          break ;
498
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 ) ;
502          break ;
503
504       case A_ONLY_FROM:
505          if ( addrlist_copy( SC_ONLY_FROM(def), &SC_ONLY_FROM(scp) ) == OK )
506             SC_PRESENT( scp, A_ONLY_FROM ) ;
507          break ;
508
509       case A_NO_ACCESS:
510          if ( addrlist_copy( SC_NO_ACCESS(def), &SC_NO_ACCESS(scp) ) == OK )
511             SC_PRESENT( scp, A_NO_ACCESS ) ;
512          break ;
513       
514       case A_PASSENV:
515          if ( copy_pset( SC_PASS_ENV_VARS(def),
516                            &SC_PASS_ENV_VARS(scp), 0 ) == OK )
517             SC_PRESENT( scp, A_PASSENV ) ;
518          break ;
519       
520       case A_ACCESS_TIMES:
521          if ( copy_pset( SC_ACCESS_TIMES(def),
522                            &SC_ACCESS_TIMES(scp), 0 ) == OK )
523             SC_PRESENT( scp, A_ACCESS_TIMES ) ;
524          break ;
525
526       case A_BANNER:
527             if ((SC_BANNER(scp) = new_string(SC_BANNER(def))) != NULL)
528                SC_PRESENT( scp, A_BANNER );
529          break ;
530
531       case A_BANNER_SUCCESS:
532             if ((SC_BANNER_SUCCESS(scp) = new_string(SC_BANNER_SUCCESS(def)))
533                   != NULL)
534                SC_PRESENT( scp, A_BANNER_SUCCESS );
535          break ;
536
537       case A_BANNER_FAIL:
538             if ((SC_BANNER_FAIL(scp) = new_string(SC_BANNER_FAIL(def))) != NULL)
539                SC_PRESENT( scp, A_BANNER_FAIL );
540          break ;
541    }
542 }
543
544
545 /*
546  * Find the attribute with the specified name
547  */
548 static const struct attribute *attr_lookup( 
549         const struct attribute attr_array[], const char *attr_name )
550 {
551    const struct attribute *ap ;
552    const char *func = "attr_lookup" ;
553
554    for ( ap = &attr_array[ 0 ] ; ap->a_name ; ap++ )
555       if ( EQ( attr_name, ap->a_name ) )
556          return ap;
557    if ( attr_array == service_attributes )
558       parsemsg( LOG_WARNING, func, "bad service attribute: %s", attr_name ) ;
559    else 
560       parsemsg( LOG_WARNING, func, 
561          "attribute: %s should not be in default section", attr_name ) ;
562    return NULL; 
563 }
564
565
566 /*
567  * Identify the attribute in <attr_name>.
568  *
569  * Check if
570  *      1) the attribute has been defined already
571  *      2) the value count is correct
572  *      3) the assign op is appropriate
573  *
574  * Invoke appropriate parser.
575  *
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.
578  */
579 static status_e identify_attribute( entry_e entry_type, 
580                                  struct service_config *scp, 
581                                  const char *attr_name, 
582                                  enum assign_op op, 
583                                  pset_h attr_values )
584 {
585    const struct attribute   *ap ;
586    const char         *func = "identify_attribute" ;
587
588    if ( entry_type == SERVICE_ENTRY )
589       ap = attr_lookup( service_attributes, attr_name ) ;
590    else
591       ap = attr_lookup( default_attributes, attr_name ) ;
592   
593    if ( ap == NULL )
594       return OK;   /* We simply ignore keywords not on the list */
595
596    if ( ! MODIFIABLE( ap ) )
597    {
598       if ( SC_SPECIFIED( scp, ap->a_id ) )
599       {
600          parsemsg( LOG_WARNING, func, "Service %s: attribute already set: %s",
601                   SC_NAME(scp), attr_name ) ;
602          return OK;
603       }
604
605       if ( op != SET_EQ )
606       {
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 ) ;
610          return OK;
611       }
612    }
613    else      /* modifiable attribute */
614    {
615       /*
616        * For the defaults entry, '=' and '+=' have the same meaning
617        */
618       if ( entry_type == DEFAULTS_ENTRY && op == SET_EQ )
619          op = PLUS_EQ ;
620    }
621
622    if ( FIXED_VALUES( ap ) &&
623         (unsigned)ap->a_nvalues != pset_count( attr_values ) )
624    {
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 ) ) ;
628       return OK;
629    }
630
631    if ( (*ap->a_parser)( attr_values, scp, op ) == OK )
632    {    /* This is the normal path. */
633         SC_SPECIFY( scp, ap->a_id ) ;
634    }
635    else if ( entry_type == SERVICE_ENTRY )
636    {
637       parsemsg( LOG_ERR, func,
638          "Error parsing attribute %s - DISABLING SERVICE", attr_name ) ;
639       SC_DISABLE( scp );
640    }
641    /*
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.
645     */
646    else if ( !debug.on )
647       return FAILED;
648    
649    return OK;
650 }
651
652
653 /*
654  * Read the entry line-by-line and add the information in scp
655  * Use defaults to initialize modifiable entry fields.
656  */
657 static status_e parse_entry( entry_e entry_type, 
658                               int fd, 
659                               struct service_config *scp )
660 {
661    static pset_h      attr_values = NULL;
662    char               *line ;
663    char               *attr_name ;
664    enum assign_op      op ;
665    const char         *func = "get_attributes" ;
666
667    if ( ! attr_values && ( attr_values = pset_create( 10, 10 ) ) == NULL )
668    {
669       out_of_memory( func ) ;
670       return( FAILED ) ;
671    }
672
673    for ( ;; )
674    {
675       line = next_line( fd ) ;
676       if ( line == CHAR_NULL )
677       {
678          parsemsg( LOG_ERR, func, "incomplete entry" ) ;
679          return( FAILED ) ;
680       }
681
682       if ( line_has_only_1_char( line, ENTRY_END ) )
683          return( OK ) ;
684
685       if ( parse_line( line, &attr_name, &op, attr_values ) == FAILED )
686       {
687          pset_clear( attr_values ) ;
688          return( FAILED ) ;
689       }
690
691       if (identify_attribute( entry_type,
692                scp, attr_name, op, attr_values ) == FAILED )
693       {  
694         /*
695          * An error was detected in the default section. We will terminate
696          * since whatever attribute being specified cannot be propagated.
697          */
698          msg(LOG_ERR, func, 
699             "A fatal error was encountered while parsing the default section."
700             " xinetd will exit.");
701          Sclose( fd );
702          terminate_program();
703       }
704       pset_clear( attr_values ) ;
705    }
706 }
707