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