1 /*-------------------------------------------------------------------------
2 * Copyright (C) 2000 Caldera Systems, Inc
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of Caldera Systems nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE CALDERA
24 * SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *-------------------------------------------------------------------------*/
32
33 /** Implementation for SLPGetProperty and SLPSetProperty
34 *
35 * These routines represent the internal implementation of the property
36 * management routines. Some really creative string management needs to be
37 * done to properly implement the API, as designed in RFC 2614 because the
38 * SLPGetProperty routine returns reference (pointer) into the property
39 * value storage. Since this is the case, SLPSetProperty can't just delete
40 * old values because an outstanding reference might exist so such a value.
41 *
42 * @par
43 * One possible solution to this problem might be to cache all old values
44 * in a table that may only be freed at the time the program terminates,
45 * but this could get expensive, memory-wise. We may try this approach
46 * anyway - for the few times properties will need to be set at run-time,
47 * the cost may be worth it.
48 *
49 * @file slp_property.c
50 * @author John Calcote (jcalcote@novell.com), Matthew Peterson
51 * @attention Please submit patches to http://www.openslp.org
52 * @ingroup CommonCodeProp
53 */
54
55 #include "slp_types.h"
56 #include "slp_property.h"
57 #include "slp_xmalloc.h"
58 #include "slp_linkedlist.h"
59 #include "slp_thread.h"
60 #include "slp_debug.h"
61 #include "slp_socket.h"
62
63 #define ENV_CONFFILE_VARNAME "OpenSLPConfig"
64
65 /** A property list entry structure.
66 */
67 typedef struct _SLPProperty
68 {
69 SLPListItem listitem; /*!< Make the SLPProperty class list-able. */
70 unsigned attrs; /*!< Property attributes. */
71 char * value; /*!< The value of this property. Points into the name buffer. */
72 char name[1]; /*!< The name/value of this property. The name is zero-terminated */
73 } SLPProperty;
74
75 /** The flag that tells if we've already read configuration files once. */
76 static bool s_PropertiesInitialized = false;
77
78 /** The property list - module static. */
79 static SLPList s_PropertyList = {0, 0, 0};
80
81 /** The (optional) application-specified property file - module static. */
82 static char s_AppPropertyFile[MAX_PATH] = "";
83
84 /** The (optional) environment-specified property file - module static. */
85 static char s_EnvPropertyFile[MAX_PATH] = "";
86
87 /** The (optional) global property file - module static. */
88 static char s_GlobalPropertyFile[MAX_PATH] = "";
89
90 /** The database lock - module static. */
91 static SLPMutexHandle s_PropDbLock;
92
93 /** The global MTU configuration property value. */
94 static int s_GlobalPropertyMTU = 1400;
95
96 /** The internl property which holds global RCVBUF size */
97 static int s_GlobalPropertyInternalRcvBufSize;
98
99 /** The internal property which holds global SNDBUF size */
100 static int s_GlobalPropertyInternalSndBufSize;
101
102 /** Sets all SLP default property values.
103 *
104 * @return Zero on success, or non-zero with errno set on error.
105 *
106 * @internal
107 */
SetDefaultValues(void)108 static int SetDefaultValues(void)
109 {
110 /* The table of default property values - comments following some values
111 * indicate deviations from the default values specified in RFC 2614.
112 */
113 static struct { char * name, * value; unsigned attrs; } defaults[] =
114 {
115 /* Section 2.1.1 DA Configuration */
116 {"net.slp.isDA", "false", 0},
117 {"net.slp.DAHeartBeat", "10800", 0},
118 {"net.slp.DAAttributes", "", 0},
119
120 /* Section 2.1.2 Static Scope Configuration */
121 {"net.slp.useScopes", "DEFAULT", 0},
122 {"net.slp.DAAddresses", "", 0},
123
124 /* Section 2.1.3 Tracing and Logging */
125 {"net.slp.traceDATraffic", "false", 0},
126 {"net.slp.traceMsg", "false", 0},
127 {"net.slp.traceDrop", "false", 0},
128 {"net.slp.traceReg", "false", 0},
129 {"net.slp.appendLog", "true", 0},
130
131 /* Section 2.1.4 Serialized Proxy Registrations */
132 {"net.slp.serializedRegURL", "", 0},
133
134 /* Section 2.1.5 Network Configuration Properties */
135 {"net.slp.isBroadcastOnly", "false", 0},
136 {"net.slp.passiveDADetection", "true", 0}, /* false */
137 {"net.slp.multicastTTL", "255", 0}, /* 8 */
138 {"net.slp.DAActiveDiscoveryInterval", "900", 0}, /* 1 */
139 {"net.slp.multicastMaximumWait", "15000"}, /* 5000 */
140 {"net.slp.multicastTimeouts", "500,750,1000,1500,2000,3000", 0}, /* 500,750,1000,1500,2000,3000 */
141 {"net.slp.DADiscoveryTimeouts", "500,750,1000,1500,2000,3000", 0}, /* 500,750,1000,1500,2000,3000 */
142 {"net.slp.datagramTimeouts", "500,750,1000,1500,2000,3000", 0}, /* I made up these numbers */
143 {"net.slp.randomWaitBound", "1000", 0},
144 {"net.slp.MTU", "1400", 0},
145 {"net.slp.interfaces", "", 0},
146
147 /* Section 2.1.6 SA Configuration */
148 {"net.slp.SAAttributes", "", 0},
149
150 /* Section 2.1.7 UA Configuration */
151 {"net.slp.locale", "en", 0},
152 {"net.slp.maxResults", "-1", 0}, /* 256 */
153 {"net.slp.typeHint", "", 0},
154 {"net.slp.preferSLPv1", "false", 0},
155
156 /* Section 2.1.8 Security */
157 {"net.slp.securityEnabled", "false", 0},
158
159 /* Additional properties that transcend RFC 2614 */
160 {"net.slp.watchRegistrationPID", "true", 0},
161 {"net.slp.OpenSLPVersion", SLP_VERSION, 0},
162 {"net.slp.unicastMaximumWait", "5000", 0},
163 {"net.slp.unicastTimeouts", "500,750,1000,1500,2000,3000", 0},
164 {"net.slp.DADiscoveryMaximumWait", "5000", 0},
165 {"net.slp.activeDADetection", "true", 0},
166 {"net.slp.checkSourceAddr", "true", 0},
167 {"net.slp.broadcastAddr", "255.255.255.255", 0},
168 {"net.slp.port", "427", 0},
169 {"net.slp.useDHCP", "true", 0},
170
171 /* Additional properties that are specific to IPv6 */
172 {"net.slp.useIPv6", "false", 0},
173 {"net.slp.useIPv4", "true", 0},
174 };
175
176 int i;
177
178 for (i = 0; i < sizeof(defaults)/sizeof(*defaults); i++)
179 if (SLPPropertySet(defaults[i].name, defaults[i].value,
180 defaults[i].attrs) != 0)
181 return -1;
182
183 return 0;
184 }
185
186 /**
187 * Initializes the MTU configuration property value. If the user specified
188 * value is more than the value that is allowed by the kernel, MTU value is
189 * adjusted to the actual value set by the kernel. If the default values of
190 * SO_SNDBUF and SO_RCVBUF are greater than the global MTU value, SO_SNDBUF
191 * and SO_RCVBUF are not set explicitly.
192 *
193 * @internal
194 */
InitializeMTUPropertyValue()195 static void InitializeMTUPropertyValue()
196 {
197 #ifndef _WIN32
198 int mtuChanged = 0;
199 int family;
200 sockfd_t sock;
201 int value = 0;
202 socklen_t valSize = sizeof(int);
203 #endif
204 s_GlobalPropertyInternalRcvBufSize = s_GlobalPropertyInternalSndBufSize = 0;
205 s_GlobalPropertyMTU = SLPPropertyAsInteger("net.slp.MTU");
206
207 #ifndef _WIN32
208 family = SLPPropertyAsBoolean("net.slp.useIPv4") ? AF_INET : AF_INET6;
209
210 if ((sock = socket(family, SOCK_DGRAM, 0)) != SLP_INVALID_SOCKET)
211 {
212 if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &value, &valSize) != -1)
213 {
214 if (value < s_GlobalPropertyMTU)
215 {
216 setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
217 &s_GlobalPropertyMTU, sizeof(int));
218 s_GlobalPropertyInternalRcvBufSize = s_GlobalPropertyMTU;
219 }
220 }
221
222 if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &value, &valSize) != -1)
223 {
224 if (value < s_GlobalPropertyMTU)
225 {
226 setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
227 &s_GlobalPropertyMTU, sizeof(int));
228 s_GlobalPropertyInternalSndBufSize = s_GlobalPropertyMTU;
229 }
230 }
231
232 // If the actual value set by the kernel is less than the MTU value,
233 // adjust here.
234 if (s_GlobalPropertyInternalRcvBufSize &&
235 getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &value, &valSize) != -1)
236 {
237 if (value < s_GlobalPropertyMTU)
238 {
239 s_GlobalPropertyInternalRcvBufSize = value;
240 }
241 }
242
243 if (s_GlobalPropertyInternalSndBufSize &&
244 getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &value, &valSize) != -1)
245 {
246 if (value < s_GlobalPropertyMTU)
247 {
248 s_GlobalPropertyInternalSndBufSize = value;
249 }
250 }
251
252 close(sock);
253
254 // If both values are set adjust the s_GlobalPropertyMTU value.
255 if (s_GlobalPropertyInternalRcvBufSize
256 && s_GlobalPropertyInternalSndBufSize)
257 {
258 s_GlobalPropertyMTU = s_GlobalPropertyInternalRcvBufSize;
259 if (s_GlobalPropertyMTU < s_GlobalPropertyInternalSndBufSize)
260 {
261 s_GlobalPropertyMTU = s_GlobalPropertyInternalSndBufSize;
262 }
263 mtuChanged = 1;
264 }
265 }
266
267 if (mtuChanged)
268 {
269 char tmp[13];
270 snprintf(tmp, 13, "%d", s_GlobalPropertyMTU);
271 SLPPropertySet("net.slp.MTU", tmp, 0);
272 }
273 #endif
274 }
275
276 /** Reads a specified configuration file into non-userset properties.
277 *
278 * Reads all values from a specified configuration file into the in-memory
279 * database
280 *
281 * @param[in] conffile - The name of the file to be read.
282 *
283 * @return A Boolean value of true if the file could be opened and read,
284 * or false if the file did not exist, or could not be opened.
285 *
286 * @internal
287 */
ReadFileProperties(char const * conffile)288 static bool ReadFileProperties(char const * conffile)
289 {
290 FILE * fp;
291 char * alloced;
292 bool retval = false;
293
294 #define CONFFILE_RDBUFSZ 4096
295
296 /* allocate a buffer and read the configuration file */
297 if ((alloced = xmalloc(CONFFILE_RDBUFSZ)) == 0)
298 return false;
299
300 /* open configuration file for read - missing file returns false */
301 if ((fp = fopen(conffile, "r")) != 0)
302 {
303 /* read a line at a time - max 4k characters per line */
304 while (fgets(alloced, CONFFILE_RDBUFSZ, fp))
305 {
306 char * line = alloced;
307 char * namestart;
308 char * nameend;
309 char * valuestart;
310 char * valueend;
311
312 /* trim leading white space */
313 while (*line && *line <= 0x20)
314 line++;
315
316 if (*line == 0)
317 continue;
318
319 /* skip all comments: # or ; to EOL */
320 if (*line == '#' || *line == ';')
321 continue;
322
323 /* parse out the property name */
324 namestart = line;
325
326 /* ignore lines not containing '=' */
327 nameend = strchr(namestart, '=');
328 if (nameend == 0)
329 continue;
330
331 /* capture value start for later */
332 valuestart = nameend + 1;
333
334 /* paranoid check immediately to make sure that valuestart
335 * is valid but more importantly that there is a value on this
336 * line to actually be read.
337 */
338 if (!valuestart || *valuestart == 0)
339 continue;
340
341 /* trim trailing white space from name */
342 *nameend-- = 0;
343 while (*nameend <= 0x20)
344 *nameend-- = 0;
345
346 /* parse out the property value - trim leading white space */
347 while (*valuestart && *valuestart <= 0x20)
348 valuestart++;
349
350 /* find end of value, trim trailing white space */
351 valueend = valuestart + strlen(valuestart);
352 while (valueend != valuestart && *valueend <= 0x20)
353 *valueend-- = 0;
354
355 /* set the property (as mutable) */
356 if (*valuestart)
357 SLPPropertySet(namestart, valuestart, 0);
358 }
359 fclose(fp);
360 retval = true;
361 }
362 xfree(alloced);
363
364 return retval;
365 }
366
367 /** Reads the property configuration files.
368 *
369 * Clears all current values from the property table, and then reads and
370 * sets properties from the configuration files. All properties read from
371 * configuration files are considered mutable by the application (except for
372 * the values of the specified configuration files.
373 *
374 * @return Zero on success, or a non-zero value on error. Properties will
375 * be set to default on error, or if not set by one or more of the
376 * configuration files.
377 *
378 * @internal
379 */
ReadPropertyFiles(void)380 static int ReadPropertyFiles(void)
381 {
382 /* load all default values first - override later with file entries */
383 if (SetDefaultValues() != 0)
384 return -1;
385
386 /* read global, and then app configuration files */
387 if (*s_GlobalPropertyFile)
388 if (ReadFileProperties(s_GlobalPropertyFile))
389 SLPPropertySet("net.slp.OpenSLPConfigFile",
390 s_GlobalPropertyFile, SLP_PA_READONLY);
391
392 /* read environment specified configuration file */
393 if (*s_EnvPropertyFile)
394 if (ReadFileProperties(s_EnvPropertyFile))
395 SLPPropertySet("net.slp.EnvConfigFile",
396 s_EnvPropertyFile, SLP_PA_READONLY);
397
398 /* if set, read application-specified configuration file */
399 if (*s_AppPropertyFile)
400 if (ReadFileProperties(s_AppPropertyFile))
401 SLPPropertySet("net.slp.AppConfigFile",
402 s_AppPropertyFile, SLP_PA_READONLY);
403
404 return 0;
405 }
406
407 /** Locate a property entry by name.
408 *
409 * Locates and returns an entry in the property list by property name.
410 *
411 * @param[in] name - The property name.
412 *
413 * @return A pointer to the requested property entry, or NULL if the
414 * requested property entry was not found.
415 *
416 * @remarks Assumes the database lock.
417 *
418 * @internal
419 */
Find(char const * name)420 static SLPProperty * Find(char const * name)
421 {
422 SLPProperty * property = (SLPProperty *)s_PropertyList.head;
423
424 /* property names are case sensitive, so use strcmp */
425 while (property && strcmp(property->name, name))
426 property = (SLPProperty *)property->listitem.next;
427
428 return property;
429 }
430
431 /** Return MTU configuration property value
432 * @return Returns MTU value
433 *
434 * @remarks MTU configuration property value is read from the configuration
435 * file if specified and initialized only once. This special function is
436 * added to access MTU configuration property value in both client and
437 * server code to avoid performance issues.
438 */
SLPPropertyGetMTU()439 int SLPPropertyGetMTU()
440 {
441 return s_GlobalPropertyMTU;
442 }
443
444 /** Gets the SNDBUF and RCVBUF sizes.
445 *
446 * @param[in] sndBufSize - A poniter to the integer to which global SNDBUF
447 * value is assigned
448 * @param[in] sndBufSize - A poniter to the integer to which global RCVBUF
449 * value is assigned
450 */
SLPPropertyInternalGetSndRcvBufSize(int * sndBufSize,int * rcvBufSize)451 void SLPPropertyInternalGetSndRcvBufSize(int *sndBufSize, int *rcvBufSize)
452 {
453 SLP_ASSERT(sndBufSize);
454 SLP_ASSERT(rcvBufSize);
455 *sndBufSize = s_GlobalPropertyInternalSndBufSize;
456 *rcvBufSize = s_GlobalPropertyInternalRcvBufSize;
457 }
458
459 /** Return an allocated copy of a property by name.
460 *
461 * @param[in] name - The name of the property to return.
462 *
463 * @return A pointer to an allocated buffer containing a copy of the property
464 * named by @p name.
465 *
466 * @remarks The caller is responsible for releasing the memory returned by
467 * calling xfree on it.
468 */
SLPPropertyXDup(const char * name)469 char * SLPPropertyXDup(const char * name)
470 {
471 char * retval = 0;
472 SLPProperty * property;
473
474 /* parameter sanity check */
475 SLP_ASSERT(name);
476 if (!name)
477 return 0;
478
479 SLPMutexAcquire(s_PropDbLock);
480
481 if ((property = Find(name)) != 0)
482 retval = xstrdup(property->value);
483
484 SLPMutexRelease(s_PropDbLock);
485
486 return retval;
487 }
488
489 /** Return a property by name.
490 *
491 * This is the internal property access routine. If the @p buffer and @p bufszp
492 * parameters are used, then this routine will return a copy of the internal
493 * value string. Otherwise it returns a pointer to the internal value string.
494 *
495 * @param[in] name - The name of the property to return.
496 * @param[out] buffer - The address of storage for the requested property
497 * value. This parameter is optional, and may be specified as NULL.
498 * @param[in/out] bufszp - On entry, contains the size of @p buffer. On exit,
499 * returns the number of bytes used or required. If @p buffer is too small
500 * then this parameter returns the number of bytes required to return all
501 * of the value. If too large, then this parameter returns the number of
502 * bytes used in @p buffer.
503 *
504 * @return A pointer to the value of the property named by @p name. If the
505 * @p buffer and @p bufszp parameters are specified, returns a pointer to
506 * @p buffer, otherwise a pointer to the internal value buffer is returned.
507 *
508 * @remarks If an internal value string pointer is returned, then OpenSLP
509 * is absolved of all responsibility regarding concurrent access to the
510 * internal property database.
511 *
512 * @remarks The correct way to call this routine with a @p buffer parameter
513 * is to size the buffer as appropriate, or size it to zero. This routine will
514 * return the required size in *bufszp. Then call it again with a @p buffer
515 * parameter of the returned size. If @p bufszp is returned containing any
516 * value less than or equal to the originally specified size, then the caller
517 * knows that the entire value was returned in @p buffer.
518 */
SLPPropertyGet(char const * name,char * buffer,size_t * bufszp)519 char const * SLPPropertyGet(char const * name, char * buffer, size_t * bufszp)
520 {
521 SLPProperty * property;
522 char const * retval = buffer;
523 size_t bufsz = bufszp? *bufszp: 0;
524
525 /* parameter sanity check */
526 SLP_ASSERT(name && (bufsz || !buffer));
527 if (!name || (buffer && !bufsz))
528 return 0;
529
530 if (bufszp) *bufszp = 0;
531
532 SLPMutexAcquire(s_PropDbLock);
533
534 if ((property = Find(name)) != 0)
535 {
536 char const * value = property->value;
537 if (buffer)
538 {
539 size_t valsz = strlen(value);
540 *bufszp = valsz;
541 if (valsz > bufsz)
542 valsz = bufsz;
543 memcpy(buffer, value, valsz - 1);
544 buffer[valsz - 1] = 0;
545 }
546 else
547 retval = value;
548 }
549
550 SLPMutexRelease(s_PropDbLock);
551
552 return retval;
553 }
554
555 /** Set a new value for a property by name.
556 *
557 * If the value is NULL or empty, then simply erase the existing value and
558 * return.
559 *
560 * @param[in] name - The name of the desired property.
561 * @param[in] value - The new value to which @p name should be set or
562 * NULL if the existing value should be removed.
563 * @param[in] attrs - The attributes of this property - zero means no
564 * attributes are assigned, other values include SLP_PA_USERSET and
565 * SLP_PA_READONLY.
566 *
567 * @return Zero on success; -1 on error, with errno set.
568 *
569 * @remarks The @p attrs parameter contains a set of bit flags indicating
570 * various attributes of the property. These attributes control write
571 * permissions mostly. SLP_PA_USERSET means that an attribute may not
572 * be changed by reading a configuration file, except in a complete
573 * re-initialization scenario. SLP_PA_READONLY sounds like the same thing,
574 * but it's not. The difference is that once set, properties with the
575 * SLP_PA_READONLY attribute may NEVER be reset (again, except in a complete
576 * re-initialization scenario), while properties with the SLP_PA_USERSET
577 * attribute may only be reset by passing this same flag in @p attrs,
578 * indicating that the caller is actually a user, and so has the right
579 * to reset the property value.
580 */
SLPPropertySet(char const * name,char const * value,unsigned attrs)581 int SLPPropertySet(char const * name, char const * value, unsigned attrs)
582 {
583 size_t namesz, valuesz;
584 SLPProperty * oldprop;
585 SLPProperty * newprop = 0; /* we may be just removing the old */
586 bool update = true; /* reset if old property exists */
587
588 /* property names must not be null or empty */
589 SLP_ASSERT(name && *name);
590 if (!name || !*name)
591 return -1;
592
593 if (value)
594 {
595 /* allocate property entry for this new value */
596 namesz = strlen(name) + 1;
597 valuesz = strlen(value) + 1;
598 if ((newprop = (SLPProperty*)xmalloc(
599 sizeof(SLPProperty) - 1 + namesz + valuesz)) == 0)
600 {
601 errno = ENOMEM;
602 return -1;
603 }
604
605 /* set internal pointers to trailing buffer space, copy values */
606 newprop->attrs = attrs;
607 memcpy(newprop->name, name, namesz);
608 newprop->value = newprop->name + namesz;
609 memcpy(newprop->value, value, valuesz);
610 }
611
612 SLPMutexAcquire(s_PropDbLock);
613
614 /* locate and possibly remove old property */
615 if ((oldprop = Find(name))!= 0)
616 {
617 /* update ONLY if old is clean, or new and old are user-settable . */
618 update = !oldprop->attrs
619 || (oldprop->attrs == SLP_PA_USERSET && attrs == SLP_PA_USERSET);
620 if (update)
621 SLPListUnlink(&s_PropertyList, (SLPListItem *)oldprop);
622 }
623
624 /* link in new property, if specified and old property was removed */
625 if (newprop && update)
626 SLPListLinkHead(&s_PropertyList, (SLPListItem *)newprop);
627
628 SLPMutexRelease(s_PropDbLock);
629
630 /* if old property was not removed, delete the new one instead */
631 xfree(update? oldprop: newprop);
632
633 return update? 0: ((errno = EACCES), -1);
634 }
635
636 /** Converts a property name into a binary boolean value.
637 *
638 * Returns the value of the specified property name as a binary boolean value.
639 *
640 * @param[in] name - The property name of the value to be returned as boolean.
641 *
642 * @return true if @p name refers to a FALSE boolean string value;
643 * false if @p name refers to a TRUE boolean string value.
644 *
645 * @remarks Ensures that @p name is not a non-existent property, and that it
646 * contains a non-NULL value. If @p name is non-existent, or refers to a
647 * null value, returns false.
648 */
SLPPropertyAsBoolean(char const * name)649 bool SLPPropertyAsBoolean(char const * name)
650 {
651 bool retval = false;
652 SLPProperty * property;
653
654 SLPMutexAcquire(s_PropDbLock);
655
656 if ((property = Find(name)) != 0)
657 {
658 char const * value = property->value;
659 if (*value == 't' || *value == 'T'
660 || *value == 'y' || *value == 'Y'
661 || *value == '1')
662 retval = true;
663 }
664
665 SLPMutexRelease(s_PropDbLock);
666
667 return retval;
668 }
669
670 /** Converts a property name into a binary integer value.
671 *
672 * Returns the specified property value as a binary integer value.
673 *
674 * @param[in] name - The name of the property whose value should be returned
675 * as an integer.
676 *
677 * @return An integer value of the string value associated with
678 * @p value.
679 *
680 * @remarks Ensures that @p name is a true property with a string value before
681 * before attempting to evaluate it. If @p name is a non-existant property,
682 * or has a null value, returns 0.
683 */
SLPPropertyAsInteger(char const * name)684 int SLPPropertyAsInteger(char const * name)
685 {
686 int ivalue = 0;
687 SLPProperty * property;
688
689 SLPMutexAcquire(s_PropDbLock);
690
691 if ((property = Find(name)) != 0)
692 ivalue = atoi(property->value);
693
694 SLPMutexRelease(s_PropDbLock);
695
696 return ivalue;
697 }
698
699 /** Converts a named integer vector property to a binary integer vector.
700 *
701 * Returns the value of the specified property as a binary integer vector.
702 *
703 * @param[in] name - The property name of an integer vector property.
704 * @param[out] ivector - The address of storage for a vector of integers.
705 * @param[in] ivectorsz - The amount of storage in @p ivector.
706 *
707 * @return The number of integer values returned in @p ivector, or zero in
708 * case of any sort of read or conversion error.
709 *
710 * @remarks The array is pre-initialized to zero so that all
711 * un-initialized entries are zero on return.
712 *
713 * @remarks Ensures that @p name is not NULL and that it refers to an existing
714 * property with a non-null value before attempting to evaluate it. If
715 * @p name is null or empty, returns 0.
716 */
SLPPropertyAsIntegerVector(char const * name,int * ivector,int ivectorsz)717 int SLPPropertyAsIntegerVector(char const * name,
718 int * ivector, int ivectorsz)
719 {
720 int i = 0;
721 SLPProperty * property;
722
723 SLPMutexAcquire(s_PropDbLock);
724
725 if ((property = Find(name)) != 0)
726 {
727 char const * value = property->value;
728 char const * end = value + strlen(value);
729 char const * slider1;
730 char const * slider2;
731
732 /* clear caller's vector */
733 memset(ivector, 0, sizeof(int) * ivectorsz);
734
735 slider1 = slider2 = value;
736 for (i = 0; i < ivectorsz && slider2 < end; i++)
737 {
738 while (*slider2 && *slider2 != ',')
739 slider2++;
740
741 /* atoi stops converting at first non-numeric character */
742 ivector[i] = atoi(slider1);
743 slider2++;
744 slider1 = slider2;
745 }
746 }
747
748 SLPMutexRelease(s_PropDbLock);
749
750 return i;
751 }
752
753 /** Configure the property sub-system to read an app conf file on init.
754 *
755 * Configure the property sub-system to read an application-specific property
756 * file at the time the global property file is read. The optional application
757 * configuration file settings override any settings obtained from the global
758 * configuration file.
759 *
760 * @param[in] aconffile - The full path name of an application-specific
761 * configuration file.
762 *
763 * @return Zero on success, or a non-zero error code if the property
764 * sub-system has already been initialized.
765 */
SLPPropertySetAppConfFile(const char * aconffile)766 int SLPPropertySetAppConfFile(const char * aconffile)
767 {
768 if (s_PropertiesInitialized)
769 return -1;
770
771 if (aconffile)
772 {
773 strnenv(s_AppPropertyFile, aconffile, sizeof(s_AppPropertyFile));
774 s_AppPropertyFile[sizeof(s_AppPropertyFile)-1] = 0;
775 }
776 return 0;
777 }
778
779 /** Release all resources held by the property module.
780 *
781 * Free all associated list memory, and reinitialize the global list head
782 * pointer to NULL.
783 *
784 * @internal
785 */
SLPPropertyCleanup(void)786 static void SLPPropertyCleanup(void)
787 {
788 SLPProperty * property;
789 SLPProperty * del;
790
791 SLPMutexAcquire(s_PropDbLock);
792
793 property = (SLPProperty *)s_PropertyList.head;
794 while (property)
795 {
796 del = property;
797 property = (SLPProperty *)property->listitem.next;
798 xfree(del);
799 }
800 memset(&s_PropertyList, 0, sizeof(s_PropertyList));
801
802 SLPMutexRelease(s_PropDbLock);
803 }
804
805 /** Initialize (or reintialize) the property table.
806 *
807 * Cleanup and reinitialize the property module from configuration files.
808 * Since this can happen anytime, we do the entire operation within the lock
809 * to keep changing state from messing up user threads. SLP mutexes are
810 * reentrant, so we can call mutex acquire from within the lock.
811 *
812 * @return Zero on success, or a non-zero value on error.
813 *
814 * @remarks The daemon calls this routine at SIGHUP. Thread-safe.
815 */
SLPPropertyReinit(void)816 int SLPPropertyReinit(void)
817 {
818 int ret;
819 SLPMutexAcquire(s_PropDbLock);
820 SLPPropertyCleanup();
821 ret = ReadPropertyFiles();
822 InitializeMTUPropertyValue();
823 SLPMutexRelease(s_PropDbLock);
824 return ret;
825 }
826
827 /** Initialize (or reintialize) the property table.
828 *
829 * Store the global init file pathname and initialize the property module
830 * from configuration options specified in @p gconffile.
831 *
832 * @param[in] gconffile - The name of the global configuration file to read.
833 *
834 * @return Zero on success, or a non-zero value on error.
835 *
836 * @remarks This routine is NOT reentrant, so steps should be taken by the
837 * caller to ensure that this routine is not called by multiple threads
838 * simultaneously. This routine is designed to be called once by the
839 * client library and the daemon, and before other threads begin accessing
840 * other property sub-system methods.
841 */
SLPPropertyInit(const char * gconffile)842 int SLPPropertyInit(const char * gconffile)
843 {
844 int sts;
845 char const * econffile = getenv(ENV_CONFFILE_VARNAME);
846
847 if (econffile)
848 {
849 strnenv(s_EnvPropertyFile, econffile, sizeof(s_EnvPropertyFile));
850 s_EnvPropertyFile[sizeof(s_EnvPropertyFile)-1] = 0;
851 }
852 if (gconffile)
853 {
854 strnenv(s_GlobalPropertyFile, gconffile, sizeof(s_GlobalPropertyFile));
855 s_GlobalPropertyFile[sizeof(s_GlobalPropertyFile)-1] = 0;
856 }
857 if ((s_PropDbLock = SLPMutexCreate()) == 0)
858 return -1;
859
860 if ((sts = SLPPropertyReinit()) != 0)
861 SLPMutexDestroy(s_PropDbLock);
862 else
863 s_PropertiesInitialized = true;
864
865 return sts;
866 }
867
868 /** Release all globally held resources held by the property module.
869 *
870 * Free all associated property database memory, and destroy the database
871 * mutex.
872 *
873 * @remarks This routine is NOT reentrant, so steps should be taken by the
874 * caller to ensure that it is not called by more than one thread at a time.
875 * It should also not be called while other threads are accessing the property
876 * database through any of the other property sub-system access methods.
877 */
SLPPropertyExit(void)878 void SLPPropertyExit(void)
879 {
880 SLPPropertyCleanup();
881 SLPMutexDestroy(s_PropDbLock);
882 s_PropertiesInitialized = false;
883 }
884
885 /*===========================================================================
886 * TESTING CODE : compile with the following command lines:
887 *
888 * $ gcc -g -O0 -DSLP_PROPERTY_TEST -DDEBUG -DHAVE_CONFIG_H -lpthread
889 * -I .. slp_property.c slp_xmalloc.c slp_linkedlist.c slp_debug.c
890 * slp_thread.c -o slp-prop-test
891 *
892 * C:\> cl -Zi -DSLP_PROPERTY_TEST -DSLP_VERSION=\"2.0\" -DDEBUG
893 * -D_CRT_SECURE_NO_DEPRECATE slp_property.c slp_xmalloc.c
894 * slp_thread.c slp_debug.c slp_linkedlist.c
895 */
896 #ifdef SLP_PROPERTY_TEST
897
898 # define FAIL (printf("FAIL: %s at line %d.\n", __FILE__, __LINE__), (-1))
899 # define PASS (printf("PASS: Success!\n"), (0))
900
901 # define TEST_G_CFG_FILENAME "slp_property_test.global.conf"
902 # define TEST_A_CFG_FILENAME "slp_property_test.app.cfg"
903
904 # ifdef _WIN32
905 # define unlink _unlink
906 # endif
907
main(int argc,char * argv[])908 int main(int argc, char * argv[])
909 {
910 int ec, nval, ival;
911 bool bval;
912 int ivec[10];
913 FILE * fp;
914 char const * pval;
915
916 /* create a global configuration file */
917 fp = fopen(TEST_G_CFG_FILENAME, "w+");
918 if (!fp)
919 return FAIL;
920 fputs("\n", fp);
921 fputs(" \n", fp);
922 fputs("# This is a comment.\n", fp);
923 fputs(" # This is another comment.\n", fp);
924 fputs(" \t\f# This is the last comment.\n", fp);
925 fputs("\t\t \f\tStrange Text with no equals sign\n", fp);
926 fputs("net.slp.isDA=true\n", fp); /* default value is false */
927 fputs("net.slp.DAHeartBeat = 10801\n", fp);
928 fputs("net.slp.DAAttributes=\n", fp);
929 fputs("net.slp.useScopes =DEFAULT\n", fp);
930 fputs("net.slp.DAAddresses\t\t\t= \n", fp);
931 fputs("net.slp.traceDATraffic = true\n", fp);
932 fputs("net.slp.multicastTimeouts=1001,1251,1501,2001,4001\n", fp);
933 fclose(fp);
934
935 /* create an application configuration file */
936 fp = fopen(TEST_A_CFG_FILENAME, "w+");
937 if (!fp)
938 return FAIL;
939 fputs("net.slp.DAHeartBeat = 10802\n", fp);
940 fclose(fp);
941
942 /* specify app configuration file */
943 ec = SLPPropertySetAppConfFile(TEST_A_CFG_FILENAME);
944 if (ec != 0)
945 return FAIL;
946
947 /* specify global configuration file - initialize */
948 ec = SLPPropertyInit(TEST_G_CFG_FILENAME);
949 if (ec != 0)
950 return FAIL;
951
952 /* set a mutable value */
953 ec = SLPPropertySet("net.slp.traceDATraffic", "false", 0);
954 if (ec != 0)
955 return FAIL;
956
957 /* set a user-only settable value */
958 ec = SLPPropertySet("net.slp.isDA", "false", SLP_PA_USERSET);
959 if (ec != 0)
960 return FAIL;
961
962 pval = SLPPropertyGet("net.slp.traceDATraffic", 0, 0);
963 if (pval == 0 || strcmp(pval, "false") != 0)
964 return FAIL;
965
966 ival = SLPPropertyAsInteger("net.slp.DAHeartBeat");
967 if (ival != 10802)
968 return FAIL;
969
970 bval = SLPPropertyAsBoolean("net.slp.isDA");
971 if (bval != false)
972 return FAIL;
973
974 nval = SLPPropertyAsIntegerVector("net.slp.multicastTimeouts", ivec, 10);
975 if (nval != 5 || ivec[0] != 1001 || ivec[1] != 1251 || ivec[2] != 1501
976 || ivec[3] != 2001 || ivec[4] != 4001)
977 return FAIL;
978
979 ival = SLPPropertyAsInteger("net.slp.fake");
980 if (ival != 0)
981 return FAIL;
982
983 bval = SLPPropertyAsBoolean("net.slp.fake");
984 if (bval != false)
985 return FAIL;
986
987 nval = SLPPropertyAsIntegerVector("net.slp.fake", ivec, 10);
988 if (nval != 0)
989 return FAIL;
990
991 pval = SLPPropertyGet("net.slp.OpenSLPConfigFile", 0, 0);
992 if (pval == 0 || strcmp(pval, TEST_G_CFG_FILENAME) != 0)
993 return FAIL;
994
995 /* reset a user-only settable value - indicate non-user is setting */
996 ec = SLPPropertySet("net.slp.isDA", "true", 0);
997 if (ec == 0)
998 return FAIL;
999
1000 /* reset a user-only settable value - indicate user is setting */
1001 ec = SLPPropertySet("net.slp.isDA", "true", SLP_PA_USERSET);
1002 if (ec != 0)
1003 return FAIL;
1004
1005 SLPPropertyExit();
1006
1007 unlink(TEST_A_CFG_FILENAME);
1008 unlink(TEST_G_CFG_FILENAME);
1009
1010 return PASS;
1011 }
1012
1013 #endif
1014
1015 /*=========================================================================*/
1016