made the "max connections" and "lock file" local rather than global
[rsync.git] / loadparm.c
1 /* This is based on loadparm.c from Samba, written by Andrew Tridgell
2    and Karl Auer */
3
4 /* 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14    
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 /*
21  *  Load parameters.
22  *
23  *  This module provides suitable callback functions for the params
24  *  module. It builds the internal table of service details which is
25  *  then used by the rest of the server.
26  *
27  * To add a parameter:
28  *
29  * 1) add it to the global or service structure definition
30  * 2) add it to the parm_table
31  * 3) add it to the list of available functions (eg: using FN_GLOBAL_STRING())
32  * 4) If it's a global then initialise it in init_globals. If a local
33  *    (ie. service) parameter then initialise it in the sDefault structure
34  *  
35  *
36  * Notes:
37  *   The configuration file is processed sequentially for speed. It is NOT
38  *   accessed randomly as happens in 'real' Windows. For this reason, there
39  *   is a fair bit of sequence-dependent code here - ie., code which assumes
40  *   that certain things happen before others. In particular, the code which
41  *   happens at the boundary between sections is delicately poised, so be
42  *   careful!
43  *
44  */
45
46 #include "rsync.h"
47 #define BOOL int
48 #define False 0
49 #define True 1
50 #define PTR_DIFF(p1,p2) ((ptrdiff_t)(((char *)(p1)) - (char *)(p2)))
51 #define strequal(a,b) (strcasecmp(a,b)==0)
52 #define BOOLSTR(b) ((b) ? "Yes" : "No")
53 typedef char pstring[1024];
54 #define pstrcpy(a,b) strlcpy(a,b,sizeof(pstring))
55
56 /* the following are used by loadparm for option lists */
57 typedef enum
58 {
59         P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,
60         P_STRING,P_GSTRING,P_ENUM,P_SEP
61 } parm_type;
62
63 typedef enum
64 {
65         P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE
66 } parm_class;
67
68 struct enum_list {
69         int value;
70         char *name;
71 };
72
73 struct parm_struct
74 {
75         char *label;
76         parm_type type;
77         parm_class class;
78         void *ptr;
79         struct enum_list *enum_list;
80         unsigned flags;
81 };
82
83 static BOOL bLoaded = False;
84
85 #ifndef GLOBAL_NAME
86 #define GLOBAL_NAME "global"
87 #endif
88
89 /* some helpful bits */
90 #define pSERVICE(i) ServicePtrs[i]
91 #define iSERVICE(i) (*pSERVICE(i))
92 #define LP_SNUM_OK(iService) (((iService) >= 0) && ((iService) < iNumServices))
93
94 /* 
95  * This structure describes global (ie., server-wide) parameters.
96  */
97 typedef struct
98 {
99         char *motd_file;
100         char *log_file;
101         char *pid_file;
102         int syslog_facility;
103         char *socket_options;
104 } global;
105
106 static global Globals;
107
108
109
110 /* 
111  * This structure describes a single service. 
112  */
113 typedef struct
114 {
115         char *name;
116         char *path;
117         char *comment;
118         char *lock_file;
119         BOOL read_only;
120         BOOL list;
121         BOOL use_chroot;
122         BOOL transfer_logging;
123         char *uid;
124         char *gid;
125         char *hosts_allow;
126         char *hosts_deny;
127         char *auth_users;
128         char *secrets_file;
129         char *exclude;
130         char *exclude_from;
131         char *include;
132         char *include_from;
133         char *log_format;
134         char *refuse_options;
135         char *dont_compress;
136         int timeout;
137         int max_connections;
138 } service;
139
140
141 /* This is a default service used to prime a services structure */
142 static service sDefault = 
143 {
144         NULL,    /* name */
145         NULL,    /* path */
146         NULL,    /* comment */
147         DEFAULT_LOCK_FILE,    /* lock file */
148         True,    /* read only */
149         True,    /* list */
150         True,    /* use chroot */
151         False,   /* transfer logging */
152         "nobody",/* uid */
153         "nobody",/* gid */
154         NULL,    /* hosts allow */
155         NULL,    /* hosts deny */
156         NULL,    /* auth users */
157         NULL,    /* secrets file */
158         NULL,    /* exclude */
159         NULL,    /* exclude from */
160         NULL,    /* include */
161         NULL,    /* include from */
162         "%o %h [%a] %m (%u) %f %l",    /* log format */
163         NULL,    /* refuse options */
164         "*.gz *.tgz *.zip *.z *.rpm *.deb",    /* dont compress */
165         0,        /* timeout */
166         0        /* max connections */
167 };
168
169
170
171 /* local variables */
172 static service **ServicePtrs = NULL;
173 static int iNumServices = 0;
174 static int iServiceIndex = 0;
175 static BOOL bInGlobalSection = True;
176
177 #define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct))
178
179 static struct enum_list enum_facilities[] = {
180 #ifdef LOG_AUTH
181         { LOG_AUTH, "auth" },
182 #endif
183 #ifdef LOG_AUTHPRIV
184         { LOG_AUTHPRIV, "authpriv" },
185 #endif
186 #ifdef LOG_CRON
187         { LOG_CRON, "cron" },
188 #endif
189 #ifdef LOG_DAEMON
190         { LOG_DAEMON, "daemon" },
191 #endif
192 #ifdef LOG_FTP
193         { LOG_FTP, "ftp" },
194 #endif
195 #ifdef LOG_KERN
196         { LOG_KERN, "kern" },
197 #endif
198 #ifdef LOG_LPR
199         { LOG_LPR, "lpr" },
200 #endif
201 #ifdef LOG_MAIL
202         { LOG_MAIL, "mail" },
203 #endif
204 #ifdef LOG_NEWS
205         { LOG_NEWS, "news" },
206 #endif
207 #ifdef LOG_AUTH
208         { LOG_AUTH, "security" },               
209 #endif
210 #ifdef LOG_SYSLOG
211         { LOG_SYSLOG, "syslog" },
212 #endif
213 #ifdef LOG_USER
214         { LOG_USER, "user" },
215 #endif
216 #ifdef LOG_UUCP
217         { LOG_UUCP, "uucp" },
218 #endif
219 #ifdef LOG_LOCAL0
220         { LOG_LOCAL0, "local0" },
221 #endif
222 #ifdef LOG_LOCAL1
223         { LOG_LOCAL1, "local1" },
224 #endif
225 #ifdef LOG_LOCAL2
226         { LOG_LOCAL2, "local2" },
227 #endif
228 #ifdef LOG_LOCAL3
229         { LOG_LOCAL3, "local3" },
230 #endif
231 #ifdef LOG_LOCAL4
232         { LOG_LOCAL4, "local4" },
233 #endif
234 #ifdef LOG_LOCAL5
235         { LOG_LOCAL5, "local5" },
236 #endif
237 #ifdef LOG_LOCAL6
238         { LOG_LOCAL6, "local6" },
239 #endif
240 #ifdef LOG_LOCAL7
241         { LOG_LOCAL7, "local7" },
242 #endif
243         { -1, NULL }};
244
245
246 /* note that we do not initialise the defaults union - it is not allowed in ANSI C */
247 static struct parm_struct parm_table[] =
248 {
249   {"motd file",        P_STRING,  P_GLOBAL, &Globals.motd_file,    NULL,   0},
250   {"syslog facility",  P_ENUM,    P_GLOBAL, &Globals.syslog_facility, enum_facilities,0},
251   {"socket options",   P_STRING,  P_GLOBAL, &Globals.socket_options,NULL,  0},
252   {"log file",         P_STRING,  P_GLOBAL, &Globals.log_file,      NULL,  0},
253   {"pid file",         P_STRING,  P_GLOBAL, &Globals.pid_file,      NULL,  0},
254
255   {"timeout",          P_INTEGER, P_LOCAL,  &sDefault.timeout,     NULL,  0},
256   {"max connections",  P_INTEGER, P_LOCAL,  &sDefault.max_connections,NULL, 0},
257   {"name",             P_STRING,  P_LOCAL,  &sDefault.name,        NULL,   0},
258   {"comment",          P_STRING,  P_LOCAL,  &sDefault.comment,     NULL,   0},
259   {"lock file",        P_STRING,  P_LOCAL,  &sDefault.lock_file,   NULL,   0},
260   {"path",             P_STRING,  P_LOCAL,  &sDefault.path,        NULL,   0},
261   {"read only",        P_BOOL,    P_LOCAL,  &sDefault.read_only,   NULL,   0},
262   {"list",             P_BOOL,    P_LOCAL,  &sDefault.list,        NULL,   0},
263   {"use chroot",       P_BOOL,    P_LOCAL,  &sDefault.use_chroot,  NULL,   0},
264   {"uid",              P_STRING,  P_LOCAL,  &sDefault.uid,         NULL,   0},
265   {"gid",              P_STRING,  P_LOCAL,  &sDefault.gid,         NULL,   0},
266   {"hosts allow",      P_STRING,  P_LOCAL,  &sDefault.hosts_allow, NULL,   0},
267   {"hosts deny",       P_STRING,  P_LOCAL,  &sDefault.hosts_deny,  NULL,   0},
268   {"auth users",       P_STRING,  P_LOCAL,  &sDefault.auth_users,  NULL,   0},
269   {"secrets file",     P_STRING,  P_LOCAL,  &sDefault.secrets_file,NULL,   0},
270   {"exclude",          P_STRING,  P_LOCAL,  &sDefault.exclude,     NULL,   0},
271   {"exclude from",     P_STRING,  P_LOCAL,  &sDefault.exclude_from,NULL,   0},
272   {"include",          P_STRING,  P_LOCAL,  &sDefault.include,     NULL,   0},
273   {"include from",     P_STRING,  P_LOCAL,  &sDefault.include_from,NULL,   0},
274   {"transfer logging", P_BOOL,    P_LOCAL,  &sDefault.transfer_logging,NULL,0},
275   {"log format",       P_STRING,  P_LOCAL,  &sDefault.log_format,  NULL,   0},
276   {"refuse options",   P_STRING,  P_LOCAL,  &sDefault.refuse_options,NULL, 0},
277   {"dont compress",    P_STRING,  P_LOCAL,  &sDefault.dont_compress,NULL,  0},
278   {NULL,               P_BOOL,    P_NONE,   NULL,                  NULL,   0}
279 };
280
281
282 /***************************************************************************
283 Initialise the global parameter structure.
284 ***************************************************************************/
285 static void init_globals(void)
286 {
287         memset(&Globals, 0, sizeof(Globals));
288 #ifdef LOG_DAEMON
289         Globals.syslog_facility = LOG_DAEMON;
290 #endif
291 }
292
293 /***************************************************************************
294 Initialise the sDefault parameter structure.
295 ***************************************************************************/
296 static void init_locals(void)
297 {
298 }
299
300
301 /*
302    In this section all the functions that are used to access the 
303    parameters from the rest of the program are defined 
304 */
305
306 #define FN_GLOBAL_STRING(fn_name,ptr) \
307  char *fn_name(void) {return(*(char **)(ptr) ? *(char **)(ptr) : "");}
308 #define FN_GLOBAL_BOOL(fn_name,ptr) \
309  BOOL fn_name(void) {return(*(BOOL *)(ptr));}
310 #define FN_GLOBAL_CHAR(fn_name,ptr) \
311  char fn_name(void) {return(*(char *)(ptr));}
312 #define FN_GLOBAL_INTEGER(fn_name,ptr) \
313  int fn_name(void) {return(*(int *)(ptr));}
314
315 #define FN_LOCAL_STRING(fn_name,val) \
316  char *fn_name(int i) {return((LP_SNUM_OK(i)&&pSERVICE(i)->val)?pSERVICE(i)->val : (sDefault.val?sDefault.val:""));}
317 #define FN_LOCAL_BOOL(fn_name,val) \
318  BOOL fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
319 #define FN_LOCAL_CHAR(fn_name,val) \
320  char fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
321 #define FN_LOCAL_INTEGER(fn_name,val) \
322  int fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
323
324
325 FN_GLOBAL_STRING(lp_motd_file, &Globals.motd_file)
326 FN_GLOBAL_STRING(lp_log_file, &Globals.log_file)
327 FN_GLOBAL_STRING(lp_pid_file, &Globals.pid_file)
328 FN_GLOBAL_STRING(lp_socket_options, &Globals.socket_options)
329 FN_GLOBAL_INTEGER(lp_syslog_facility, &Globals.syslog_facility)
330
331 FN_LOCAL_STRING(lp_name, name)
332 FN_LOCAL_STRING(lp_comment, comment)
333 FN_LOCAL_STRING(lp_path, path)
334 FN_LOCAL_STRING(lp_lock_file, lock_file)
335 FN_LOCAL_BOOL(lp_read_only, read_only)
336 FN_LOCAL_BOOL(lp_list, list)
337 FN_LOCAL_BOOL(lp_use_chroot, use_chroot)
338 FN_LOCAL_BOOL(lp_transfer_logging, transfer_logging)
339 FN_LOCAL_STRING(lp_uid, uid)
340 FN_LOCAL_STRING(lp_gid, gid)
341 FN_LOCAL_STRING(lp_hosts_allow, hosts_allow)
342 FN_LOCAL_STRING(lp_hosts_deny, hosts_deny)
343 FN_LOCAL_STRING(lp_auth_users, auth_users)
344 FN_LOCAL_STRING(lp_secrets_file, secrets_file)
345 FN_LOCAL_STRING(lp_exclude, exclude)
346 FN_LOCAL_STRING(lp_exclude_from, exclude_from)
347 FN_LOCAL_STRING(lp_include, include)
348 FN_LOCAL_STRING(lp_include_from, include_from)
349 FN_LOCAL_STRING(lp_log_format, log_format)
350 FN_LOCAL_STRING(lp_refuse_options, refuse_options)
351 FN_LOCAL_STRING(lp_dont_compress, dont_compress)
352 FN_LOCAL_INTEGER(lp_timeout, timeout)
353 FN_LOCAL_INTEGER(lp_max_connections, max_connections)
354
355 /* local prototypes */
356 static int    strwicmp( char *psz1, char *psz2 );
357 static int    map_parameter( char *parmname);
358 static BOOL   set_boolean( BOOL *pb, char *parmvalue );
359 static int    getservicebyname(char *name, service *pserviceDest);
360 static void   copy_service( service *pserviceDest, 
361                             service *pserviceSource);
362 static BOOL   do_parameter(char *parmname, char *parmvalue);
363 static BOOL   do_section(char *sectionname);
364
365
366 /***************************************************************************
367 initialise a service to the defaults
368 ***************************************************************************/
369 static void init_service(service *pservice)
370 {
371         memset((char *)pservice,0,sizeof(service));
372         copy_service(pservice,&sDefault);
373 }
374
375 static void string_set(char **s, char *v)
376 {
377         if (!v) {
378                 *s = NULL;
379                 return;
380         }
381         *s = strdup(v);
382         if (!*s) exit_cleanup(RERR_MALLOC);
383 }
384
385
386 /***************************************************************************
387 add a new service to the services array initialising it with the given 
388 service
389 ***************************************************************************/
390 static int add_a_service(service *pservice, char *name)
391 {
392   int i;
393   service tservice;
394   int num_to_alloc = iNumServices+1;
395
396   tservice = *pservice;
397
398   /* it might already exist */
399   if (name) 
400     {
401       i = getservicebyname(name,NULL);
402       if (i >= 0)
403         return(i);
404     }
405
406   i = iNumServices;
407
408   ServicePtrs = (service **)Realloc(ServicePtrs,sizeof(service *)*num_to_alloc);
409
410   if (ServicePtrs)
411           pSERVICE(iNumServices) = (service *)malloc(sizeof(service));
412
413   if (!ServicePtrs || !pSERVICE(iNumServices))
414           return(-1);
415
416   iNumServices++;
417
418   init_service(pSERVICE(i));
419   copy_service(pSERVICE(i),&tservice);
420   if (name)
421     string_set(&iSERVICE(i).name,name);  
422
423   return(i);
424 }
425
426 /***************************************************************************
427 Do a case-insensitive, whitespace-ignoring string compare.
428 ***************************************************************************/
429 static int strwicmp(char *psz1, char *psz2)
430 {
431    /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */
432    /* appropriate value. */
433    if (psz1 == psz2)
434       return (0);
435    else
436       if (psz1 == NULL)
437          return (-1);
438       else
439           if (psz2 == NULL)
440               return (1);
441
442    /* sync the strings on first non-whitespace */
443    while (1)
444    {
445       while (isspace(*psz1))
446          psz1++;
447       while (isspace(*psz2))
448          psz2++;
449       if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0' || *psz2 == '\0')
450          break;
451       psz1++;
452       psz2++;
453    }
454    return (*psz1 - *psz2);
455 }
456
457 /***************************************************************************
458 Map a parameter's string representation to something we can use. 
459 Returns False if the parameter string is not recognised, else TRUE.
460 ***************************************************************************/
461 static int map_parameter(char *parmname)
462 {
463    int iIndex;
464
465    if (*parmname == '-')
466      return(-1);
467
468    for (iIndex = 0; parm_table[iIndex].label; iIndex++) 
469       if (strwicmp(parm_table[iIndex].label, parmname) == 0)
470          return(iIndex);
471
472    rprintf(FERROR, "Unknown Parameter encountered: \"%s\"\n", parmname);
473    return(-1);
474 }
475
476
477 /***************************************************************************
478 Set a boolean variable from the text value stored in the passed string.
479 Returns True in success, False if the passed string does not correctly 
480 represent a boolean.
481 ***************************************************************************/
482 static BOOL set_boolean(BOOL *pb, char *parmvalue)
483 {
484    BOOL bRetval;
485
486    bRetval = True;
487    if (strwicmp(parmvalue, "yes") == 0 ||
488        strwicmp(parmvalue, "true") == 0 ||
489        strwicmp(parmvalue, "1") == 0)
490       *pb = True;
491    else
492       if (strwicmp(parmvalue, "no") == 0 ||
493           strwicmp(parmvalue, "False") == 0 ||
494           strwicmp(parmvalue, "0") == 0)
495          *pb = False;
496       else
497       {
498          rprintf(FERROR, "Badly formed boolean in configuration file: \"%s\".\n",
499                parmvalue);
500          bRetval = False;
501       }
502    return (bRetval);
503 }
504
505 /***************************************************************************
506 Find a service by name. Otherwise works like get_service.
507 ***************************************************************************/
508 static int getservicebyname(char *name, service *pserviceDest)
509 {
510    int iService;
511
512    for (iService = iNumServices - 1; iService >= 0; iService--)
513       if (strwicmp(iSERVICE(iService).name, name) == 0) 
514       {
515          if (pserviceDest != NULL)
516            copy_service(pserviceDest, pSERVICE(iService));
517          break;
518       }
519
520    return (iService);
521 }
522
523
524
525 /***************************************************************************
526 Copy a service structure to another
527
528 ***************************************************************************/
529 static void copy_service(service *pserviceDest, 
530                          service *pserviceSource)
531 {
532   int i;
533
534   for (i=0;parm_table[i].label;i++)
535     if (parm_table[i].ptr && parm_table[i].class == P_LOCAL) {
536         void *def_ptr = parm_table[i].ptr;
537         void *src_ptr = 
538           ((char *)pserviceSource) + PTR_DIFF(def_ptr,&sDefault);
539         void *dest_ptr = 
540           ((char *)pserviceDest) + PTR_DIFF(def_ptr,&sDefault);
541
542         switch (parm_table[i].type)
543           {
544           case P_BOOL:
545           case P_BOOLREV:
546             *(BOOL *)dest_ptr = *(BOOL *)src_ptr;
547             break;
548
549           case P_INTEGER:
550           case P_ENUM:
551           case P_OCTAL:
552             *(int *)dest_ptr = *(int *)src_ptr;
553             break;
554
555           case P_CHAR:
556             *(char *)dest_ptr = *(char *)src_ptr;
557             break;
558
559           case P_STRING:
560             string_set(dest_ptr,*(char **)src_ptr);
561             break;
562
563           default:
564             break;
565           }
566       }
567 }
568
569
570 /***************************************************************************
571 Process a parameter for a particular service number. If snum < 0
572 then assume we are in the globals
573 ***************************************************************************/
574 static BOOL lp_do_parameter(int snum, char *parmname, char *parmvalue)
575 {
576    int parmnum, i;
577    void *parm_ptr=NULL; /* where we are going to store the result */
578    void *def_ptr=NULL;
579
580    parmnum = map_parameter(parmname);
581
582    if (parmnum < 0)
583      {
584        rprintf(FERROR, "IGNORING unknown parameter \"%s\"\n", parmname);
585        return(True);
586      }
587
588    def_ptr = parm_table[parmnum].ptr;
589
590    /* we might point at a service, the default service or a global */
591    if (snum < 0) {
592      parm_ptr = def_ptr;
593    } else {
594        if (parm_table[parmnum].class == P_GLOBAL) {
595            rprintf(FERROR, "Global parameter %s found in service section!\n",parmname);
596            return(True);
597          }
598        parm_ptr = ((char *)pSERVICE(snum)) + PTR_DIFF(def_ptr,&sDefault);
599    }
600
601    /* now switch on the type of variable it is */
602    switch (parm_table[parmnum].type)
603      {
604      case P_BOOL:
605        set_boolean(parm_ptr,parmvalue);
606        break;
607
608      case P_BOOLREV:
609        set_boolean(parm_ptr,parmvalue);
610        *(BOOL *)parm_ptr = ! *(BOOL *)parm_ptr;
611        break;
612
613      case P_INTEGER:
614        *(int *)parm_ptr = atoi(parmvalue);
615        break;
616
617      case P_CHAR:
618        *(char *)parm_ptr = *parmvalue;
619        break;
620
621      case P_OCTAL:
622        sscanf(parmvalue,"%o",(int *)parm_ptr);
623        break;
624
625      case P_STRING:
626        string_set(parm_ptr,parmvalue);
627        break;
628
629      case P_GSTRING:
630        strlcpy((char *)parm_ptr,parmvalue,sizeof(pstring));
631        break;
632
633      case P_ENUM:
634              for (i=0;parm_table[parmnum].enum_list[i].name;i++) {
635                      if (strequal(parmvalue, parm_table[parmnum].enum_list[i].name)) {
636                              *(int *)parm_ptr = parm_table[parmnum].enum_list[i].value;
637                              break;
638                      }
639              }
640              if (!parm_table[parmnum].enum_list[i].name) {
641                      if (atoi(parmvalue) > 0)
642                              *(int *)parm_ptr = atoi(parmvalue);
643              }
644              break;
645      case P_SEP:
646              break;
647      }
648
649    return(True);
650 }
651
652 /***************************************************************************
653 Process a parameter.
654 ***************************************************************************/
655 static BOOL do_parameter(char *parmname, char *parmvalue)
656 {
657    return lp_do_parameter(bInGlobalSection?-2:iServiceIndex, parmname, parmvalue);
658 }
659
660 /***************************************************************************
661 Process a new section (service). At this stage all sections are services.
662 Later we'll have special sections that permit server parameters to be set.
663 Returns True on success, False on failure.
664 ***************************************************************************/
665 static BOOL do_section(char *sectionname)
666 {
667    BOOL bRetval;
668    BOOL isglobal = (strwicmp(sectionname, GLOBAL_NAME) == 0);
669    bRetval = False;
670
671    /* if we were in a global section then do the local inits */
672    if (bInGlobalSection && !isglobal)
673      init_locals();
674
675    /* if we've just struck a global section, note the fact. */
676    bInGlobalSection = isglobal;   
677
678    /* check for multiple global sections */
679    if (bInGlobalSection)
680    {
681      return(True);
682    }
683
684    /* if we have a current service, tidy it up before moving on */
685    bRetval = True;
686
687    if (iServiceIndex >= 0)
688      bRetval = True;
689
690    /* if all is still well, move to the next record in the services array */
691    if (bRetval)
692      {
693        /* We put this here to avoid an odd message order if messages are */
694        /* issued by the post-processing of a previous section. */
695
696        if ((iServiceIndex=add_a_service(&sDefault,sectionname)) < 0)
697          {
698            rprintf(FERROR,"Failed to add a new service\n");
699            return(False);
700          }
701      }
702
703    return (bRetval);
704 }
705
706
707 /***************************************************************************
708 Load the services array from the services file. Return True on success, 
709 False on failure.
710 ***************************************************************************/
711 BOOL lp_load(char *pszFname, int globals_only)
712 {
713         pstring n2;
714         BOOL bRetval;
715  
716         bRetval = False;
717
718         bInGlobalSection = True;
719   
720         init_globals();
721
722         pstrcpy(n2,pszFname);
723
724         /* We get sections first, so have to start 'behind' to make up */
725         iServiceIndex = -1;
726         bRetval = pm_process(n2, globals_only?NULL:do_section, do_parameter);
727   
728         bLoaded = True;
729
730         return (bRetval);
731 }
732
733
734 /***************************************************************************
735 return the max number of services
736 ***************************************************************************/
737 int lp_numservices(void)
738 {
739   return(iNumServices);
740 }
741
742 /***************************************************************************
743 Return the number of the service with the given name, or -1 if it doesn't
744 exist. Note that this is a DIFFERENT ANIMAL from the internal function
745 getservicebyname()! This works ONLY if all services have been loaded, and
746 does not copy the found service.
747 ***************************************************************************/
748 int lp_number(char *name)
749 {
750    int iService;
751
752    for (iService = iNumServices - 1; iService >= 0; iService--)
753       if (strequal(lp_name(iService), name)) 
754          break;
755
756    return (iService);
757 }
758