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