removed ACCESSPERMS mask when transferring a file without perms
[rsync.git] / loadparm.c
blob2d65faafe37647373ef9cb419c2bb5ac30bff337
1 /* This is based on loadparm.c from Samba, written by Andrew Tridgell
2 and Karl Auer */
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.
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.
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.
21 * Load parameters.
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.
27 * To add a parameter:
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
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!
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))
53 /* the following are used by loadparm for option lists */
54 typedef enum
56 P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,
57 P_STRING,P_GSTRING,P_ENUM,P_SEP
58 } parm_type;
60 typedef enum
62 P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE
63 } parm_class;
65 struct enum_list {
66 int value;
67 char *name;
70 struct parm_struct
72 char *label;
73 parm_type type;
74 parm_class class;
75 void *ptr;
76 struct enum_list *enum_list;
77 unsigned flags;
80 static BOOL bLoaded = False;
82 #ifndef GLOBAL_NAME
83 #define GLOBAL_NAME "global"
84 #endif
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))
91 /*
92 * This structure describes global (ie., server-wide) parameters.
94 typedef struct
96 char *motd_file;
97 char *log_file;
98 char *pid_file;
99 int syslog_facility;
100 char *socket_options;
101 } global;
103 static global Globals;
108 * This structure describes a single service.
110 typedef struct
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;
140 /* This is a default service used to prime a services structure */
141 static service sDefault =
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", /* dont compress */
166 0, /* timeout */
167 0 /* max connections */
172 /* local variables */
173 static service **ServicePtrs = NULL;
174 static int iNumServices = 0;
175 static int iServiceIndex = 0;
176 static BOOL bInGlobalSection = True;
178 #define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct))
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 }};
247 /* note that we do not initialise the defaults union - it is not allowed in ANSI C */
248 static struct parm_struct parm_table[] =
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},
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}
285 /***************************************************************************
286 Initialise the global parameter structure.
287 ***************************************************************************/
288 static void init_globals(void)
290 memset(&Globals, 0, sizeof(Globals));
291 #ifdef LOG_DAEMON
292 Globals.syslog_facility = LOG_DAEMON;
293 #endif
296 /***************************************************************************
297 Initialise the sDefault parameter structure.
298 ***************************************************************************/
299 static void init_locals(void)
305 In this section all the functions that are used to access the
306 parameters from the rest of the program are defined
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));}
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);}
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)
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)
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);
371 /***************************************************************************
372 initialise a service to the defaults
373 ***************************************************************************/
374 static void init_service(service *pservice)
376 memset((char *)pservice,0,sizeof(service));
377 copy_service(pservice,&sDefault);
380 static void string_set(char **s, char *v)
382 if (!v) {
383 *s = NULL;
384 return;
386 *s = strdup(v);
387 if (!*s) exit_cleanup(RERR_MALLOC);
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)
397 int i;
398 service tservice;
399 int num_to_alloc = iNumServices+1;
401 tservice = *pservice;
403 /* it might already exist */
404 if (name)
406 i = getservicebyname(name,NULL);
407 if (i >= 0)
408 return(i);
411 i = iNumServices;
413 ServicePtrs = (service **)Realloc(ServicePtrs,sizeof(service *)*num_to_alloc);
415 if (ServicePtrs)
416 pSERVICE(iNumServices) = (service *)malloc(sizeof(service));
418 if (!ServicePtrs || !pSERVICE(iNumServices))
419 return(-1);
421 iNumServices++;
423 init_service(pSERVICE(i));
424 copy_service(pSERVICE(i),&tservice);
425 if (name)
426 string_set(&iSERVICE(i).name,name);
428 return(i);
431 /***************************************************************************
432 Do a case-insensitive, whitespace-ignoring string compare.
433 ***************************************************************************/
434 static int strwicmp(char *psz1, char *psz2)
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);
447 /* sync the strings on first non-whitespace */
448 while (1)
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++;
459 return (*psz1 - *psz2);
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)
468 int iIndex;
470 if (*parmname == '-')
471 return(-1);
473 for (iIndex = 0; parm_table[iIndex].label; iIndex++)
474 if (strwicmp(parm_table[iIndex].label, parmname) == 0)
475 return(iIndex);
477 rprintf(FERROR, "Unknown Parameter encountered: \"%s\"\n", parmname);
478 return(-1);
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)
489 BOOL bRetval;
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
503 rprintf(FERROR, "Badly formed boolean in configuration file: \"%s\".\n",
504 parmvalue);
505 bRetval = False;
507 return (bRetval);
510 /***************************************************************************
511 Find a service by name. Otherwise works like get_service.
512 ***************************************************************************/
513 static int getservicebyname(char *name, service *pserviceDest)
515 int iService;
517 for (iService = iNumServices - 1; iService >= 0; iService--)
518 if (strwicmp(iSERVICE(iService).name, name) == 0)
520 if (pserviceDest != NULL)
521 copy_service(pserviceDest, pSERVICE(iService));
522 break;
525 return (iService);
530 /***************************************************************************
531 Copy a service structure to another
533 ***************************************************************************/
534 static void copy_service(service *pserviceDest,
535 service *pserviceSource)
537 int i;
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);
547 switch (parm_table[i].type)
549 case P_BOOL:
550 case P_BOOLREV:
551 *(BOOL *)dest_ptr = *(BOOL *)src_ptr;
552 break;
554 case P_INTEGER:
555 case P_ENUM:
556 case P_OCTAL:
557 *(int *)dest_ptr = *(int *)src_ptr;
558 break;
560 case P_CHAR:
561 *(char *)dest_ptr = *(char *)src_ptr;
562 break;
564 case P_STRING:
565 string_set(dest_ptr,*(char **)src_ptr);
566 break;
568 default:
569 break;
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)
581 int parmnum, i;
582 void *parm_ptr=NULL; /* where we are going to store the result */
583 void *def_ptr=NULL;
585 parmnum = map_parameter(parmname);
587 if (parmnum < 0)
589 rprintf(FERROR, "IGNORING unknown parameter \"%s\"\n", parmname);
590 return(True);
593 def_ptr = parm_table[parmnum].ptr;
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);
603 parm_ptr = ((char *)pSERVICE(snum)) + PTR_DIFF(def_ptr,&sDefault);
606 /* now switch on the type of variable it is */
607 switch (parm_table[parmnum].type)
609 case P_BOOL:
610 set_boolean(parm_ptr,parmvalue);
611 break;
613 case P_BOOLREV:
614 set_boolean(parm_ptr,parmvalue);
615 *(BOOL *)parm_ptr = ! *(BOOL *)parm_ptr;
616 break;
618 case P_INTEGER:
619 *(int *)parm_ptr = atoi(parmvalue);
620 break;
622 case P_CHAR:
623 *(char *)parm_ptr = *parmvalue;
624 break;
626 case P_OCTAL:
627 sscanf(parmvalue,"%o",(int *)parm_ptr);
628 break;
630 case P_STRING:
631 string_set(parm_ptr,parmvalue);
632 break;
634 case P_GSTRING:
635 strlcpy((char *)parm_ptr,parmvalue,sizeof(pstring));
636 break;
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;
645 if (!parm_table[parmnum].enum_list[i].name) {
646 if (atoi(parmvalue) > 0)
647 *(int *)parm_ptr = atoi(parmvalue);
649 break;
650 case P_SEP:
651 break;
654 return(True);
657 /***************************************************************************
658 Process a parameter.
659 ***************************************************************************/
660 static BOOL do_parameter(char *parmname, char *parmvalue)
662 return lp_do_parameter(bInGlobalSection?-2:iServiceIndex, parmname, parmvalue);
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)
672 BOOL bRetval;
673 BOOL isglobal = (strwicmp(sectionname, GLOBAL_NAME) == 0);
674 bRetval = False;
676 /* if we were in a global section then do the local inits */
677 if (bInGlobalSection && !isglobal)
678 init_locals();
680 /* if we've just struck a global section, note the fact. */
681 bInGlobalSection = isglobal;
683 /* check for multiple global sections */
684 if (bInGlobalSection)
686 return(True);
689 /* if we have a current service, tidy it up before moving on */
690 bRetval = True;
692 if (iServiceIndex >= 0)
693 bRetval = True;
695 /* if all is still well, move to the next record in the services array */
696 if (bRetval)
698 /* We put this here to avoid an odd message order if messages are */
699 /* issued by the post-processing of a previous section. */
701 if ((iServiceIndex=add_a_service(&sDefault,sectionname)) < 0)
703 rprintf(FERROR,"Failed to add a new service\n");
704 return(False);
708 return (bRetval);
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)
718 pstring n2;
719 BOOL bRetval;
721 bRetval = False;
723 bInGlobalSection = True;
725 init_globals();
727 pstrcpy(n2,pszFname);
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);
733 bLoaded = True;
735 return (bRetval);
739 /***************************************************************************
740 return the max number of services
741 ***************************************************************************/
742 int lp_numservices(void)
744 return(iNumServices);
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)
755 int iService;
757 for (iService = iNumServices - 1; iService >= 0; iService--)
758 if (strequal(lp_name(iService), name))
759 break;
761 return (iService);