home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / unix / volume26 / xinetd21 / part24 < prev    next >
Encoding:
Text File  |  1993-06-26  |  16.6 KB  |  636 lines

  1. Newsgroups: comp.sources.unix
  2. From: panos@cs.colorado.edu (Panos Tsirigotis)
  3. Subject: v26i268: xinetd-2.1.1 - inetd replacement with access control and logging, Part24/31
  4. Sender: unix-sources-moderator@gw.home.vix.com
  5. Approved: vixie@gw.home.vix.com
  6.  
  7. Submitted-By: panos@cs.colorado.edu (Panos Tsirigotis)
  8. Posting-Number: Volume 26, Issue 268
  9. Archive-Name: xinetd-2.1.1/part24
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 24 (of 31)."
  18. # Contents:  xinetd/parse.c
  19. # Wrapped by panos@mystique on Mon Jun 21 14:51:27 1993
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'xinetd/parse.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'xinetd/parse.c'\"
  23. else
  24. echo shar: Extracting \"'xinetd/parse.c'\" \(14596 characters\)
  25. sed "s/^X//" >'xinetd/parse.c' <<'END_OF_FILE'
  26. X/*
  27. X * (c) Copyright 1992 by Panagiotis Tsirigotis
  28. X * All rights reserved.  The file named COPYRIGHT specifies the terms 
  29. X * and conditions for redistribution.
  30. X */
  31. X
  32. Xstatic char RCSid[] = "$Id" ;
  33. X
  34. X#include <sys/types.h>
  35. X#include <netdb.h>
  36. X#include <string.h>
  37. X#include <syslog.h>
  38. X#include <memory.h>
  39. X
  40. X#include "misc.h"
  41. X#include "str.h"
  42. X#include "pset.h"
  43. X#include "sio.h"
  44. X
  45. X#include "defs.h"
  46. X#include "sconf.h"
  47. X#include "conf.h"
  48. X#include "attr.h"
  49. X#include "parse.h"
  50. X#include "addr.h"
  51. X
  52. Xchar *malloc() ;
  53. Xvoid endprotoent() ;
  54. Xvoid endpwent() ;
  55. Xvoid endgrent() ;
  56. Xint endnetent() ;
  57. Xvoid endhostent() ;
  58. X
  59. X
  60. Xvoid parsemsg() ;
  61. Xvoid msg() ;
  62. Xvoid out_of_memory() ;
  63. X
  64. Xchar *next_line() ;
  65. Xint line_has_only_1_char() ;
  66. Xvoid skip_entry() ;
  67. X
  68. X/*
  69. X * Parser functions
  70. X */
  71. Xstatus_e service_parser() ;
  72. Xstatus_e socket_type_parser() ;
  73. Xstatus_e protocol_parser() ;
  74. Xstatus_e wait_parser() ;
  75. Xstatus_e user_parser() ;
  76. Xstatus_e group_parser() ;
  77. Xstatus_e server_parser() ;
  78. Xstatus_e server_args_parser() ;
  79. Xstatus_e instances_parser() ;
  80. Xstatus_e log_on_success_parser() ;
  81. Xstatus_e log_on_failure_parser() ;
  82. Xstatus_e log_type_parser() ;
  83. Xstatus_e only_from_parser() ;
  84. Xstatus_e no_access_parser() ;
  85. Xstatus_e access_times_parser() ;
  86. Xstatus_e type_parser() ;
  87. Xstatus_e id_parser() ;
  88. Xstatus_e env_parser() ;
  89. Xstatus_e port_parser() ;
  90. Xstatus_e rpc_version_parser() ;
  91. Xstatus_e passenv_parser() ;
  92. Xstatus_e flags_parser() ;
  93. Xstatus_e disabled_parser() ;
  94. Xstatus_e rpc_number_parser() ;
  95. Xstatus_e nice_parser() ;
  96. X
  97. X/*
  98. X * A NULL value for the name field marks the end of the table
  99. X *
  100. X * The 3rd value is the number of attribute values.
  101. X * If the number is positive, exactly that many values must be specified.
  102. X * If the number is -1, 0 or more values may be specified.
  103. X * If the number is -2, 0 or more values may be specified and the operators
  104. X * '+=' and '-=' may be used.
  105. X */
  106. Xstatic struct attribute service_attributes[] =
  107. X    {
  108. X        { "socket_type",         A_SOCKET_TYPE,        1,        socket_type_parser         },
  109. X        { "protocol",            A_PROTOCOL,            1,        protocol_parser             },
  110. X        { "wait",                A_WAIT,                1,        wait_parser                    },
  111. X        { "user",                 A_USER,                 1,        user_parser                    },
  112. X        { "group",                 A_GROUP,                1,        group_parser                 },
  113. X        { "server",                A_SERVER,            1,        server_parser                },
  114. X        { "server_args",        A_SERVER_ARGS,        -1,    server_args_parser        },
  115. X        { "instances",            A_INSTANCES,        1,        instances_parser            },
  116. X        { "log_on_success",    A_LOG_ON_SUCCESS,    -2,    log_on_success_parser    },
  117. X        { "log_on_failure",    A_LOG_ON_FAILURE,    -2,    log_on_failure_parser    },
  118. X        { "log_type",            A_LOG_TYPE,            -1,    log_type_parser            },
  119. X        { "only_from",            A_ONLY_FROM,        -2,    only_from_parser            },
  120. X        { "no_access",            A_NO_ACCESS,        -2,    no_access_parser            },
  121. X        { "access_times",        A_ACCESS_TIMES,    -1,    access_times_parser        },
  122. X        { "type",                A_TYPE,                -1,    type_parser                    },
  123. X#ifndef NO_RPC
  124. X        { "rpc_version",        A_RPC_VERSION,        1,        rpc_version_parser        },
  125. X        { "rpc_number",        A_RPC_NUMBER,        1,        rpc_number_parser            },
  126. X#endif
  127. X        { "id",                    A_ID,                    1,        id_parser                    },
  128. X        { "env",                    A_ENV,                -2,    env_parser                    },
  129. X        { "port",                A_PORT,                1,        port_parser                    },
  130. X        { "passenv",            A_PASSENV,            -2,    passenv_parser                },
  131. X        { "flags",                A_FLAGS,                -1,    flags_parser                },
  132. X        { "nice",                A_NICE,                1,        nice_parser                    },
  133. X        { NULL,                    A_NONE,                -1,    NULL                            }
  134. X    } ;
  135. X
  136. Xstatic struct attribute default_attributes[] =
  137. X   {
  138. X      { "log_type",        A_LOG_TYPE,       -2,   log_type_parser       },
  139. X      { "log_on_success",  A_LOG_ON_SUCCESS, -2,   log_on_success_parser },
  140. X      { "log_on_failure",  A_LOG_ON_FAILURE, -2,   log_on_failure_parser },
  141. X      { "disabled",        A_DISABLED,       -2,   disabled_parser       },
  142. X      { "no_access",       A_NO_ACCESS,      -2,   no_access_parser      },
  143. X      { "only_from",       A_ONLY_FROM,      -2,   only_from_parser      },
  144. X      { "instances",       A_INSTANCES,      1,    instances_parser      },
  145. X      { "passenv",         A_PASSENV,        -2,   passenv_parser        },
  146. X      { NULL,              A_NONE,           0,    NULL                      }
  147. X   } ;
  148. X
  149. X
  150. X#define MODIFIABLE( ap )                ( (ap)->a_nvalues == -2 )
  151. X#define VAR_VALUES( ap )                ( (ap)->a_nvalues < 0 )
  152. X#define FIXED_VALUES( ap )                ( (ap)->a_nvalues > 0 )
  153. X
  154. Xint line_count ;
  155. X
  156. XPRIVATE void        get_service_entry() ;
  157. XPRIVATE entry_e    find_next_entry() ;
  158. XPRIVATE status_e     parse_entry() ;
  159. X
  160. X
  161. X/*
  162. X * Given the id, return the name (only the service attributes are searched)
  163. X */
  164. Xchar *attr_name_lookup( id )
  165. X    register int id ;
  166. X{
  167. X    register struct attribute *ap ;
  168. X
  169. X    for ( ap = &service_attributes[ 0 ] ; ap->a_name ; ap++ )
  170. X        if ( id == ap->a_id )
  171. X            return( ap->a_name ) ;
  172. X    return( CHAR_NULL ) ;
  173. X}
  174. X
  175. X
  176. Xvoid parse_end()
  177. X{
  178. X    endprotoent() ;
  179. X    endpwent() ;
  180. X    endgrent() ;
  181. X    endnetent() ;
  182. X    endhostent() ;
  183. X}
  184. X
  185. X
  186. X/*
  187. X * Parsing rules and rationale
  188. X *
  189. X * The parse_conf_file function parses a configuration file identified
  190. X * by a file descriptor and fills the service table and defaults of
  191. X * the configuration argument.
  192. X *
  193. X * The configuration information for a service comes from 2 sources: the
  194. X * service entry and, possibly, the defaults entry.
  195. X * Attributes specified in the defaults entry can be overriden or
  196. X * modified by the service entry. Modifiable attributes can be identified
  197. X * by the value -2 for the 'a_nvalues' field of the struct attribute. Those
  198. X * attributes with a different value for 'a_nvalues' are overridable ones.
  199. X * The modifiable attributes are filled in only if the entry tries to modify
  200. X * them.
  201. X */
  202. X
  203. X/*
  204. X * Read the configuration file (descriptor fd) and place all
  205. X * services found there in the configuration.
  206. X */
  207. Xvoid parse_conf_file( fd, confp )
  208. X    int fd ;
  209. X    struct configuration *confp ;
  210. X{
  211. X    pset_h                        sconfs                = CNF_SERVICE_CONFS( confp ) ;
  212. X    struct service_config    *default_config    = CNF_DEFAULTS( confp ) ;
  213. X    boolean_e                    found_defaults        = NO ;
  214. X    struct service_config    default_default_config ;
  215. X    char                            *func                    = "parse_conf_file" ;
  216. X
  217. X    line_count = 0 ;
  218. X    CLEAR( default_default_config ) ;
  219. X
  220. X    for ( ;; )
  221. X    {
  222. X        entry_e    entry_type ;
  223. X        char        *service_name ;
  224. X
  225. X        /*
  226. X         * if find_next_entry is successful, service_name
  227. X         * will point to malloc'ed memory
  228. X         */
  229. X        entry_type = find_next_entry( fd, &service_name ) ;
  230. X
  231. X        switch ( entry_type )
  232. X        {
  233. X            case SERVICE_ENTRY:
  234. X                get_service_entry( fd, sconfs, service_name, default_config ) ;
  235. X                break ;
  236. X
  237. X            case DEFAULTS_ENTRY:
  238. X                if ( found_defaults == YES )
  239. X                {
  240. X                    parsemsg( LOG_ERR, func,
  241. X                 "only 1 defaults entry is allowed. This entry will be ignored" ) ;
  242. X                    skip_entry( fd ) ;
  243. X                }
  244. X                else if ( parse_entry( DEFAULTS_ENTRY, fd,
  245. X                                        default_config, &default_default_config ) == OK )
  246. X                    found_defaults = YES ;
  247. X                break ;
  248. X            
  249. X            case BAD_ENTRY:
  250. X                skip_entry( fd ) ;
  251. X                break ;
  252. X
  253. X            case NO_ENTRY:
  254. X                return ;
  255. X        }
  256. X    }
  257. X}
  258. X
  259. X
  260. X
  261. X/*
  262. X * Find the next service entry.
  263. X * Look for a line of the form:
  264. X *
  265. X *        <white-space> service <white-space> <service_name>
  266. X *
  267. X * followed by a line containing only the ENTRY_BEGIN character
  268. X */
  269. XPRIVATE entry_e find_next_entry( fd, snamep )
  270. X    int    fd ;
  271. X    char    **snamep ;                        /* service name pointer */
  272. X{
  273. X    register char    *p ;
  274. X    str_h                strp ;
  275. X    char                *sname ;
  276. X    entry_e            entry_type ;
  277. X    register char    *line = next_line( fd ) ;
  278. X    char                *func = "find_next_entry" ;
  279. X
  280. X    if ( line == CHAR_NULL )
  281. X        return( NO_ENTRY ) ;
  282. X    
  283. X    strp = str_parse( line, " \t", STR_RETURN_ERROR, INT_NULL ) ;
  284. X    if ( strp == NULL )
  285. X    {
  286. X        parsemsg( LOG_CRIT, func, "str_parse failed" ) ;
  287. X        return( BAD_ENTRY ) ;
  288. X    }
  289. X
  290. X    if ( ( p = str_component( strp ) ) == CHAR_NULL )
  291. X    {
  292. X        /*
  293. X         * This shouldn't happen since it implies that there is a bug
  294. X         * in next_line
  295. X         */
  296. X        parsemsg( LOG_WARNING, func, "empty line" ) ;
  297. X        str_endparse( strp ) ;
  298. X        return( BAD_ENTRY ) ;
  299. X    }
  300. X
  301. X    /*
  302. X     * Look for a keyword
  303. X     */
  304. X    if ( EQ( p, KW_SERVICE ) )
  305. X    {
  306. X        /*
  307. X         * Now get the service name
  308. X         */
  309. X        if ( ( p = str_component( strp ) ) == CHAR_NULL )
  310. X        {
  311. X            parsemsg( LOG_ERR, func, "service name missing" ) ;
  312. X            str_endparse( strp ) ;
  313. X            return( BAD_ENTRY ) ;
  314. X        }
  315. X    
  316. X        sname = make_string( 1, p ) ;
  317. X        if ( sname == CHAR_NULL )
  318. X        {
  319. X            out_of_memory( func ) ;
  320. X            str_endparse( strp ) ;
  321. X            return( BAD_ENTRY ) ;
  322. X        }
  323. X        str_endparse( strp ) ;
  324. X        entry_type = SERVICE_ENTRY ;
  325. X    }
  326. X    else if ( EQ( p, KW_DEFAULTS ) )
  327. X    {
  328. X        str_endparse( strp ) ;
  329. X        entry_type = DEFAULTS_ENTRY ;
  330. X    }
  331. X    else
  332. X    {
  333. X        parsemsg( LOG_ERR, func, "missing service keyword" ) ;
  334. X        str_endparse( strp ) ;
  335. X        return( BAD_ENTRY ) ;
  336. X    }
  337. X
  338. X    /*
  339. X     * Now look for ENTRY_BEGIN
  340. X     */
  341. X    line = next_line( fd ) ;
  342. X    if ( line == NULL || ! line_has_only_1_char( line, ENTRY_BEGIN ) )
  343. X    {
  344. X        parsemsg( LOG_ERR, func,
  345. X            "Service %s: missing '%c'", sname, ENTRY_BEGIN ) ;
  346. X        if ( entry_type == SERVICE_ENTRY )
  347. X            free( sname ) ;
  348. X        return( BAD_ENTRY ) ;
  349. X    }
  350. X    *snamep = sname ;
  351. X    return( entry_type ) ;
  352. X}
  353. X
  354. X
  355. X
  356. X/*
  357. X * Get a service entry. Steps:
  358. X *
  359. X *        1. Parse entry attributes
  360. X *        2. Determine service id
  361. X *        3. Insert entry in table
  362. X */
  363. XPRIVATE void get_service_entry( fd, sconfs, name, defaults )
  364. X    int                            fd ;
  365. X    pset_h                        sconfs ;
  366. X    char                            *name ;
  367. X    struct service_config    *defaults ;
  368. X{
  369. X    register struct service_config    *scp ;
  370. X    unsigned                                    u ;
  371. X    char                                        *func = "get_service_entry" ;
  372. X
  373. X    scp = sc_alloc( name ) ;
  374. X    if ( scp == NULL )
  375. X    {
  376. X        free( name ) ;
  377. X        skip_entry( fd ) ;
  378. X        return ;
  379. X    }
  380. X
  381. X    if ( parse_entry( SERVICE_ENTRY, fd, scp, defaults ) == FAILED )
  382. X    {
  383. X        sc_free( scp ) ;
  384. X        skip_entry( fd ) ;
  385. X        return ;
  386. X    }
  387. X
  388. X   /*
  389. X    * If no service id was specified, set it equal to the service name
  390. X    */
  391. X   if ( ! SC_SPECIFIED( scp, A_ID ) )
  392. X      if ( scp->sc_id = make_string( 1, scp->sc_name ) )
  393. X         SC_PRESENT( scp, A_ID ) ;
  394. X      else
  395. X      {
  396. X            out_of_memory( func ) ;
  397. X         sc_free( scp ) ;
  398. X         return ;
  399. X      }
  400. X
  401. X    /*
  402. X     * Make sure the service id is unique
  403. X     */
  404. X    for ( u = 0 ; u < pset_count( sconfs ) ; u++ )
  405. X        if ( EQ( SCP( pset_pointer( sconfs, u ) )->sc_id, scp->sc_id ) )
  406. X        {
  407. X         parsemsg( LOG_ERR, func, "id not unique: %s", scp->sc_id ) ;
  408. X            sc_free( scp ) ;
  409. X            return ;
  410. X        }
  411. X
  412. X    if ( ! pset_add( sconfs, scp ) )
  413. X    {
  414. X        out_of_memory( func ) ;
  415. X        sc_free( scp ) ;
  416. X        return ;
  417. X    }
  418. X}
  419. X
  420. X
  421. X
  422. X
  423. X/*
  424. X * Fill in scp the value of the modifiable attribute attr from def.
  425. X * These modifiable attributes are:
  426. X *        log_on_{success,failure}
  427. X *        only_from
  428. X *        no_access
  429. X *        passenv
  430. X */
  431. XPRIVATE void fill_attribute( attr_id, scp, def )
  432. X    unsigned                        attr_id ;
  433. X    struct service_config    *scp ;
  434. X    struct service_config    *def ;
  435. X{
  436. X    status_e copy_pset() ;
  437. X
  438. X    switch ( attr_id )
  439. X    {
  440. X        case A_LOG_ON_SUCCESS:
  441. X            M_ASSIGN( scp->sc_log_on_success, def->sc_log_on_success ) ;
  442. X            SC_PRESENT( scp, A_LOG_ON_SUCCESS ) ;
  443. X            break ;
  444. X
  445. X        case A_LOG_ON_FAILURE:
  446. X            M_ASSIGN( scp->sc_log_on_failure, def->sc_log_on_failure ) ;
  447. X            SC_PRESENT( scp, A_LOG_ON_FAILURE ) ;
  448. X            break ;
  449. X
  450. X        case A_ONLY_FROM:
  451. X            if ( addrlist_copy( def->sc_only_from, &scp->sc_only_from ) == OK )
  452. X                SC_PRESENT( scp, A_ONLY_FROM ) ;
  453. X            break ;
  454. X
  455. X        case A_NO_ACCESS:
  456. X            if ( addrlist_copy( def->sc_no_access, &scp->sc_no_access ) == OK )
  457. X                SC_PRESENT( scp, A_NO_ACCESS ) ;
  458. X            break ;
  459. X        
  460. X        case A_PASSENV:
  461. X            if ( copy_pset( def->sc_pass_env_vars,
  462. X                                    &scp->sc_pass_env_vars, 0 ) == OK )
  463. X                SC_PRESENT( scp, A_PASSENV ) ;
  464. X            break ;
  465. X    }
  466. X}
  467. X
  468. X
  469. X
  470. X/*
  471. X * Find the attribute with the specified name
  472. X */
  473. XPRIVATE struct attribute *attr_lookup( attr_array, attr_name )
  474. X    struct attribute attr_array[] ;
  475. X    char *attr_name ;
  476. X{
  477. X    register struct attribute *ap ;
  478. X    char *func = "attr_lookup" ;
  479. X
  480. X    for ( ap = &attr_array[ 0 ] ; ap->a_name ; ap++ )
  481. X        if ( EQ( attr_name, ap->a_name ) )
  482. X            return( ap ) ;
  483. X    parsemsg( LOG_WARNING, func, "bad attribute: %s", attr_name ) ;
  484. X    return( NULL ) ;
  485. X}
  486. X
  487. X
  488. X/*
  489. X * Identify the attribute in <attr_name>.
  490. X *
  491. X * Check if
  492. X *        1) the attribute has been defined already
  493. X *        2) the value count is correct
  494. X *        3) the assign op is appropriate
  495. X *
  496. X * Invoke appropriate parser
  497. X */
  498. XPRIVATE void identify_attribute( entry_type, scp, defaults,
  499. X                                                        attr_name, op, attr_values )
  500. X    entry_e                        entry_type ;
  501. X    struct service_config    *scp ;
  502. X    struct service_config    *defaults ;
  503. X    register char                *attr_name ;
  504. X    enum assign_op                op ;
  505. X    pset_h                        attr_values ;
  506. X{
  507. X    register struct attribute    *ap ;
  508. X    char                                *func = "identify_attribute" ;
  509. X
  510. X    if ( entry_type == SERVICE_ENTRY )
  511. X        ap = attr_lookup( service_attributes, attr_name ) ;
  512. X    else
  513. X        ap = attr_lookup( default_attributes, attr_name ) ;
  514. X    
  515. X    if ( ap == NULL )
  516. X        return ;
  517. X
  518. X    if ( ! MODIFIABLE( ap ) )
  519. X    {
  520. X        if ( SC_SPECIFIED( scp, ap->a_id ) )
  521. X        {
  522. X            parsemsg( LOG_WARNING, func, "Service %s: attribute already set: %s",
  523. X                        scp->sc_name, attr_name ) ;
  524. X            return ;
  525. X        }
  526. X
  527. X        if ( op != SET_EQ )
  528. X        {
  529. X            parsemsg( LOG_WARNING, func,
  530. X                "Service %s: operator '%s' cannot be used for attribute '%s'",
  531. X                    scp->sc_name, ( op == PLUS_EQ ) ? "+=" : "-=", attr_name ) ;
  532. X            return ;
  533. X        }
  534. X    }
  535. X    else        /* modifiable attribute */
  536. X    {
  537. X        /*
  538. X         * For the defaults entry, '=' and '+=' have the same meaning
  539. X         */
  540. X        if ( entry_type == DEFAULTS_ENTRY && op == SET_EQ )
  541. X            op = PLUS_EQ ;
  542. X
  543. X        /*
  544. X         * If this is the first time we see this attribute, and a default
  545. X         * for it is available, copy that default.
  546. X         */
  547. X        if ( ! SC_IS_PRESENT( scp, ap->a_id ) &&
  548. X                                            SC_SPECIFIED( defaults, ap->a_id ) )
  549. X            fill_attribute( ap->a_id, scp, defaults ) ;
  550. X    }
  551. X
  552. X    if ( FIXED_VALUES( ap ) && ap->a_nvalues != pset_count( attr_values ) )
  553. X    {
  554. X        parsemsg( LOG_WARNING, func,
  555. X            "attribute %s expects %d values and %d values were specified",
  556. X            attr_name, ap->a_nvalues, pset_count( attr_values ) ) ;
  557. X        return ;
  558. X    }
  559. X
  560. X    if ( (*ap->a_parser)( attr_values, scp, op ) == OK )
  561. X        SC_SPECIFY( scp, ap->a_id ) ;
  562. X}
  563. X
  564. X
  565. X/*
  566. X * Read the entry line-by-line and add the information in scp
  567. X * Use defaults to initialize modifiable entry fields.
  568. X */
  569. XPRIVATE status_e parse_entry( entry_type, fd, scp, defaults )
  570. X    entry_e                        entry_type ;
  571. X    int                            fd ;
  572. X    struct service_config    *scp ;
  573. X    struct service_config    *defaults ;
  574. X{
  575. X   static pset_h        attr_values ;
  576. X   register char        *line ;
  577. X   char                    *attr_name ;
  578. X   enum assign_op        op ;
  579. X   void                    identify_attribute() ;
  580. X   status_e                parse_line() ;
  581. X   char                    *func = "get_attributes" ;
  582. X
  583. X    if ( ! attr_values && ( attr_values = pset_create( 10, 10 ) ) == NULL )
  584. X    {
  585. X        out_of_memory( func ) ;
  586. X        return( FAILED ) ;
  587. X    }
  588. X
  589. X   for ( ;; )
  590. X   {
  591. X      line = next_line( fd ) ;
  592. X      if ( line == CHAR_NULL )
  593. X      {
  594. X         parsemsg( LOG_ERR, func, "incomplete entry" ) ;
  595. X         return( FAILED ) ;
  596. X      }
  597. X
  598. X      if ( line_has_only_1_char( line, ENTRY_END ) )
  599. X         return( OK ) ;
  600. X
  601. X      if ( parse_line( line, &attr_name, &op, attr_values ) == FAILED )
  602. X      {
  603. X         pset_clear( attr_values ) ;
  604. X         return( FAILED ) ;
  605. X      }
  606. X
  607. X        identify_attribute( entry_type,
  608. X                    scp, defaults, attr_name, op, attr_values ) ;
  609. X      pset_clear( attr_values ) ;
  610. X   }
  611. X}
  612. X
  613. END_OF_FILE
  614. if test 14596 -ne `wc -c <'xinetd/parse.c'`; then
  615.     echo shar: \"'xinetd/parse.c'\" unpacked with wrong size!
  616. fi
  617. # end of 'xinetd/parse.c'
  618. fi
  619. echo shar: End of archive 24 \(of 31\).
  620. cp /dev/null ark24isdone
  621. MISSING=""
  622. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ; do
  623.     if test ! -f ark${I}isdone ; then
  624.     MISSING="${MISSING} ${I}"
  625.     fi
  626. done
  627. if test "${MISSING}" = "" ; then
  628.     echo You have unpacked all 31 archives.
  629.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  630. else
  631.     echo You still need to unpack the following archives:
  632.     echo "        " ${MISSING}
  633. fi
  634. ##  End of shell archive.
  635. exit 0
  636.