d22bef811508c6f99a1a17f2cfc8c0609b6009f7
[rsync/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 Realloc realloc
51 #define PTR_DIFF(p1,p2) ((ptrdiff_t)(((char *)(p1)) - (char *)(p2)))
52 #define strequal(a,b) (strcasecmp(a,b)==0)
53 #define BOOLSTR(b) ((b) ? "Yes" : "No")
54 typedef char pstring[1024];
55 #define pstrcpy(a,b) strcpy(a,b)
56
57 /* the following are used by loadparm for option lists */
58 typedef enum
59 {
60   P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,
61   P_STRING,P_GSTRING,P_ENUM,P_SEP
62 } parm_type;
63
64 typedef enum
65 {
66   P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE
67 } parm_class;
68
69 struct enum_list {
70         int value;
71         char *name;
72 };
73
74 struct parm_struct
75 {
76         char *label;
77         parm_type type;
78         parm_class class;
79         void *ptr;
80         struct enum_list *enum_list;
81         unsigned flags;
82 };
83
84 static BOOL bLoaded = False;
85
86 #ifndef GLOBAL_NAME
87 #define GLOBAL_NAME "global"
88 #endif
89
90 /* some helpful bits */
91 #define pSERVICE(i) ServicePtrs[i]
92 #define iSERVICE(i) (*pSERVICE(i))
93 #define LP_SNUM_OK(iService) (((iService) >= 0) && ((iService) < iNumServices))
94
95 /* 
96  * This structure describes global (ie., server-wide) parameters.
97  */
98 typedef struct
99 {
100         char *motd_file;
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         BOOL read_only;
116         BOOL list;
117         char *uid;
118         char *gid;
119 } service;
120
121
122 /* This is a default service used to prime a services structure */
123 static service sDefault = 
124 {
125         NULL,    /* name */
126         NULL,    /* path */
127         NULL,    /* comment */
128         True,    /* read only */
129         True,    /* list */
130         "nobody",/* uid */
131         "nobody",/* gid */
132 };
133
134
135
136 /* local variables */
137 static service **ServicePtrs = NULL;
138 static int iNumServices = 0;
139 static int iServiceIndex = 0;
140 static BOOL bInGlobalSection = True;
141
142 #define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct))
143
144
145 /* note that we do not initialise the defaults union - it is not allowed in ANSI C */
146 static struct parm_struct parm_table[] =
147 {
148   {"motd file",        P_STRING,  P_GLOBAL, &Globals.motd_file,    NULL,   0},
149   {"name",             P_STRING,  P_LOCAL,  &sDefault.name,        NULL,   0},
150   {"comment",          P_STRING,  P_LOCAL,  &sDefault.comment,     NULL,   0},
151   {"path",             P_STRING,  P_LOCAL,  &sDefault.path,        NULL,   0},
152   {"read only",        P_BOOL,    P_LOCAL,  &sDefault.read_only,   NULL,   0},
153   {"list",             P_BOOL,    P_LOCAL,  &sDefault.list,        NULL,   0},
154   {"uid",              P_STRING,  P_LOCAL,  &sDefault.uid,         NULL,   0},
155   {"gid",              P_STRING,  P_LOCAL,  &sDefault.gid,         NULL,   0},
156   {NULL,               P_BOOL,    P_NONE,   NULL,                  NULL,   0}
157 };
158
159
160 /***************************************************************************
161 Initialise the global parameter structure.
162 ***************************************************************************/
163 static void init_globals(void)
164 {
165 }
166
167 /***************************************************************************
168 Initialise the sDefault parameter structure.
169 ***************************************************************************/
170 static void init_locals(void)
171 {
172 }
173
174
175 /*
176    In this section all the functions that are used to access the 
177    parameters from the rest of the program are defined 
178 */
179
180 #define FN_GLOBAL_STRING(fn_name,ptr) \
181  char *fn_name(void) {return(*(char **)(ptr) ? *(char **)(ptr) : "");}
182 #define FN_GLOBAL_BOOL(fn_name,ptr) \
183  BOOL fn_name(void) {return(*(BOOL *)(ptr));}
184 #define FN_GLOBAL_CHAR(fn_name,ptr) \
185  char fn_name(void) {return(*(char *)(ptr));}
186 #define FN_GLOBAL_INTEGER(fn_name,ptr) \
187  int fn_name(void) {return(*(int *)(ptr));}
188
189 #define FN_LOCAL_STRING(fn_name,val) \
190  char *fn_name(int i) {return((LP_SNUM_OK(i)&&pSERVICE(i)->val)?pSERVICE(i)->val : (sDefault.val?sDefault.val:""));}
191 #define FN_LOCAL_BOOL(fn_name,val) \
192  BOOL fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
193 #define FN_LOCAL_CHAR(fn_name,val) \
194  char fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
195 #define FN_LOCAL_INTEGER(fn_name,val) \
196  int fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);}
197
198
199 FN_GLOBAL_STRING(lp_motd_file, &Globals.motd_file)
200 FN_LOCAL_STRING(lp_name, name)
201 FN_LOCAL_STRING(lp_comment, comment)
202 FN_LOCAL_STRING(lp_path, path)
203 FN_LOCAL_BOOL(lp_read_only, read_only)
204 FN_LOCAL_BOOL(lp_list, list)
205 FN_LOCAL_STRING(lp_uid, uid)
206 FN_LOCAL_STRING(lp_gid, gid)
207
208 /* local prototypes */
209 static int    strwicmp( char *psz1, char *psz2 );
210 static int    map_parameter( char *parmname);
211 static BOOL   set_boolean( BOOL *pb, char *parmvalue );
212 static int    getservicebyname(char *name, service *pserviceDest);
213 static void   copy_service( service *pserviceDest, 
214                             service *pserviceSource);
215 static BOOL   do_parameter(char *parmname, char *parmvalue);
216 static BOOL   do_section(char *sectionname);
217
218
219 /***************************************************************************
220 initialise a service to the defaults
221 ***************************************************************************/
222 static void init_service(service *pservice)
223 {
224   bzero((char *)pservice,sizeof(service));
225   copy_service(pservice,&sDefault);
226 }
227
228 static void string_set(char **s, char *v)
229 {
230         if (*s) free(*s);
231         if (!v) {
232                 *s = NULL;
233                 return;
234         }
235         *s = strdup(v);
236         if (!*s) exit_cleanup(1);
237 }
238
239
240 /***************************************************************************
241 add a new service to the services array initialising it with the given 
242 service
243 ***************************************************************************/
244 static int add_a_service(service *pservice, char *name)
245 {
246   int i;
247   service tservice;
248   int num_to_alloc = iNumServices+1;
249
250   tservice = *pservice;
251
252   /* it might already exist */
253   if (name) 
254     {
255       i = getservicebyname(name,NULL);
256       if (i >= 0)
257         return(i);
258     }
259
260   i = iNumServices;
261
262   ServicePtrs = (service **)Realloc(ServicePtrs,sizeof(service *)*num_to_alloc);
263   if (ServicePtrs)
264           pSERVICE(iNumServices) = (service *)malloc(sizeof(service));
265
266   if (!ServicePtrs || !pSERVICE(iNumServices))
267           return(-1);
268
269   iNumServices++;
270
271   init_service(pSERVICE(i));
272   copy_service(pSERVICE(i),&tservice);
273   if (name)
274     string_set(&iSERVICE(i).name,name);  
275
276   return(i);
277 }
278
279 /***************************************************************************
280 Do a case-insensitive, whitespace-ignoring string compare.
281 ***************************************************************************/
282 static int strwicmp(char *psz1, char *psz2)
283 {
284    /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */
285    /* appropriate value. */
286    if (psz1 == psz2)
287       return (0);
288    else
289       if (psz1 == NULL)
290          return (-1);
291       else
292           if (psz2 == NULL)
293               return (1);
294
295    /* sync the strings on first non-whitespace */
296    while (1)
297    {
298       while (isspace(*psz1))
299          psz1++;
300       while (isspace(*psz2))
301          psz2++;
302       if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0' || *psz2 == '\0')
303          break;
304       psz1++;
305       psz2++;
306    }
307    return (*psz1 - *psz2);
308 }
309
310 /***************************************************************************
311 Map a parameter's string representation to something we can use. 
312 Returns False if the parameter string is not recognised, else TRUE.
313 ***************************************************************************/
314 static int map_parameter(char *parmname)
315 {
316    int iIndex;
317
318    if (*parmname == '-')
319      return(-1);
320
321    for (iIndex = 0; parm_table[iIndex].label; iIndex++) 
322       if (strwicmp(parm_table[iIndex].label, parmname) == 0)
323          return(iIndex);
324
325    rprintf(FERROR, "Unknown parameter encountered: \"%s\"\n", parmname);
326    return(-1);
327 }
328
329
330 /***************************************************************************
331 Set a boolean variable from the text value stored in the passed string.
332 Returns True in success, False if the passed string does not correctly 
333 represent a boolean.
334 ***************************************************************************/
335 static BOOL set_boolean(BOOL *pb, char *parmvalue)
336 {
337    BOOL bRetval;
338
339    bRetval = True;
340    if (strwicmp(parmvalue, "yes") == 0 ||
341        strwicmp(parmvalue, "true") == 0 ||
342        strwicmp(parmvalue, "1") == 0)
343       *pb = True;
344    else
345       if (strwicmp(parmvalue, "no") == 0 ||
346           strwicmp(parmvalue, "False") == 0 ||
347           strwicmp(parmvalue, "0") == 0)
348          *pb = False;
349       else
350       {
351          rprintf(FERROR, "Badly formed boolean in configuration file: \"%s\".\n",
352                parmvalue);
353          bRetval = False;
354       }
355    return (bRetval);
356 }
357
358 /***************************************************************************
359 Find a service by name. Otherwise works like get_service.
360 ***************************************************************************/
361 static int getservicebyname(char *name, service *pserviceDest)
362 {
363    int iService;
364
365    for (iService = iNumServices - 1; iService >= 0; iService--)
366       if (strwicmp(iSERVICE(iService).name, name) == 0) 
367       {
368          if (pserviceDest != NULL)
369            copy_service(pserviceDest, pSERVICE(iService));
370          break;
371       }
372
373    return (iService);
374 }
375
376
377
378 /***************************************************************************
379 Copy a service structure to another
380
381 ***************************************************************************/
382 static void copy_service(service *pserviceDest, 
383                          service *pserviceSource)
384 {
385   int i;
386
387   for (i=0;parm_table[i].label;i++)
388     if (parm_table[i].ptr && parm_table[i].class == P_LOCAL) {
389         void *def_ptr = parm_table[i].ptr;
390         void *src_ptr = 
391           ((char *)pserviceSource) + PTR_DIFF(def_ptr,&sDefault);
392         void *dest_ptr = 
393           ((char *)pserviceDest) + PTR_DIFF(def_ptr,&sDefault);
394
395         switch (parm_table[i].type)
396           {
397           case P_BOOL:
398           case P_BOOLREV:
399             *(BOOL *)dest_ptr = *(BOOL *)src_ptr;
400             break;
401
402           case P_INTEGER:
403           case P_ENUM:
404           case P_OCTAL:
405             *(int *)dest_ptr = *(int *)src_ptr;
406             break;
407
408           case P_CHAR:
409             *(char *)dest_ptr = *(char *)src_ptr;
410             break;
411
412           case P_STRING:
413             string_set(dest_ptr,*(char **)src_ptr);
414             break;
415
416           default:
417             break;
418           }
419       }
420 }
421
422
423 /***************************************************************************
424 Process a parameter for a particular service number. If snum < 0
425 then assume we are in the globals
426 ***************************************************************************/
427 static BOOL lp_do_parameter(int snum, char *parmname, char *parmvalue)
428 {
429    int parmnum, i;
430    void *parm_ptr=NULL; /* where we are going to store the result */
431    void *def_ptr=NULL;
432
433    parmnum = map_parameter(parmname);
434
435    if (parmnum < 0)
436      {
437        rprintf(FERROR, "Ignoring unknown parameter \"%s\"\n", parmname);
438        return(True);
439      }
440
441    def_ptr = parm_table[parmnum].ptr;
442
443    /* we might point at a service, the default service or a global */
444    if (snum < 0) {
445      parm_ptr = def_ptr;
446    } else {
447        if (parm_table[parmnum].class == P_GLOBAL) {
448            rprintf(FERROR, "Global parameter %s found in service section!\n",parmname);
449            return(True);
450          }
451        parm_ptr = ((char *)pSERVICE(snum)) + PTR_DIFF(def_ptr,&sDefault);
452    }
453
454    /* now switch on the type of variable it is */
455    switch (parm_table[parmnum].type)
456      {
457      case P_BOOL:
458        set_boolean(parm_ptr,parmvalue);
459        break;
460
461      case P_BOOLREV:
462        set_boolean(parm_ptr,parmvalue);
463        *(BOOL *)parm_ptr = ! *(BOOL *)parm_ptr;
464        break;
465
466      case P_INTEGER:
467        *(int *)parm_ptr = atoi(parmvalue);
468        break;
469
470      case P_CHAR:
471        *(char *)parm_ptr = *parmvalue;
472        break;
473
474      case P_OCTAL:
475        sscanf(parmvalue,"%o",(int *)parm_ptr);
476        break;
477
478      case P_STRING:
479        string_set(parm_ptr,parmvalue);
480        break;
481
482      case P_GSTRING:
483        strcpy((char *)parm_ptr,parmvalue);
484        break;
485
486      case P_ENUM:
487              for (i=0;parm_table[parmnum].enum_list[i].name;i++) {
488                      if (strequal(parmvalue, parm_table[parmnum].enum_list[i].name)) {
489                              *(int *)parm_ptr = parm_table[parmnum].enum_list[i].value;
490                              break;
491                      }
492              }
493              break;
494      case P_SEP:
495              break;
496      }
497
498    return(True);
499 }
500
501 /***************************************************************************
502 Process a parameter.
503 ***************************************************************************/
504 static BOOL do_parameter(char *parmname, char *parmvalue)
505 {
506    return lp_do_parameter(bInGlobalSection?-2:iServiceIndex, parmname, parmvalue);
507 }
508
509 /***************************************************************************
510 Process a new section (service). At this stage all sections are services.
511 Later we'll have special sections that permit server parameters to be set.
512 Returns True on success, False on failure.
513 ***************************************************************************/
514 static BOOL do_section(char *sectionname)
515 {
516    BOOL bRetval;
517    BOOL isglobal = (strwicmp(sectionname, GLOBAL_NAME) == 0);
518    bRetval = False;
519
520    /* if we were in a global section then do the local inits */
521    if (bInGlobalSection && !isglobal)
522      init_locals();
523
524    /* if we've just struck a global section, note the fact. */
525    bInGlobalSection = isglobal;   
526
527    /* check for multiple global sections */
528    if (bInGlobalSection)
529    {
530      return(True);
531    }
532
533    /* if we have a current service, tidy it up before moving on */
534    bRetval = True;
535
536    if (iServiceIndex >= 0)
537      bRetval = True;
538
539    /* if all is still well, move to the next record in the services array */
540    if (bRetval)
541      {
542        /* We put this here to avoid an odd message order if messages are */
543        /* issued by the post-processing of a previous section. */
544
545        if ((iServiceIndex=add_a_service(&sDefault,sectionname)) < 0)
546          {
547            rprintf(FERROR,"Failed to add a new service\n");
548            return(False);
549          }
550      }
551
552    return (bRetval);
553 }
554
555
556 /***************************************************************************
557 Load the services array from the services file. Return True on success, 
558 False on failure.
559 ***************************************************************************/
560 BOOL lp_load(char *pszFname)
561 {
562   pstring n2;
563   BOOL bRetval;
564  
565   bRetval = False;
566
567   bInGlobalSection = True;
568   
569   init_globals();
570
571   pstrcpy(n2,pszFname);
572
573   /* We get sections first, so have to start 'behind' to make up */
574   iServiceIndex = -1;
575   bRetval = pm_process(n2, do_section, do_parameter);
576   
577   bLoaded = True;
578
579   return (bRetval);
580 }
581
582
583 /***************************************************************************
584 return the max number of services
585 ***************************************************************************/
586 int lp_numservices(void)
587 {
588   return(iNumServices);
589 }
590
591 /***************************************************************************
592 Return the number of the service with the given name, or -1 if it doesn't
593 exist. Note that this is a DIFFERENT ANIMAL from the internal function
594 getservicebyname()! This works ONLY if all services have been loaded, and
595 does not copy the found service.
596 ***************************************************************************/
597 int lp_number(char *name)
598 {
599    int iService;
600
601    for (iService = iNumServices - 1; iService >= 0; iService--)
602       if (strequal(lp_name(iService), name)) 
603          break;
604
605    return (iService);
606 }
607