8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / ldapcachemgr / cachemgr_discovery.c
blobbb352ae7c43ef6575bfb77bc53c1170a00804786
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #ifdef SLP
29 * This file contains all the dynamic server discovery functionality
30 * for ldap_cachemgr. SLP is used to query the network for any changes
31 * in the set of deployed LDAP servers.
33 * The algorithm used is outlined here:
35 * 1. Find all naming contexts with SLPFindAttrs. (See
36 * find_all_contexts())
37 * 2. For each context, find all servers which serve that context
38 * with SLPFindSrvs. (See foreach_context())
39 * 3. For each server, retrieve that server's attributes with
40 * SLPFindAttributes. (See foreach_server())
41 * 4. Aggregate the servers' attributes into a config object. There
42 * is one config object associated with each context found in
43 * step 1. (See aggregate_attrs())
44 * 5. Update the global config cache for each found context and its
45 * associated servers and attributes. (See update_config())
47 * The entry point for ldap_cachemgr is discover(). The actual entry
48 * point into the discovery routine is find_all_contexts(); the
49 * code thereafter is actually not specific to LDAP, and could also
50 * be used to discover YP, or any other server which conforms
51 * to the SLP Naming and Directory abstract service type.
53 * find_all_attributes() takes as parameters three callback routines
54 * which are used to report all information back to the caller. The
55 * signatures and synopses of these routines are:
57 * void *get_cfghandle(const char *domain);
59 * Returns an opaque handle to a configuration object specific
60 * to the 'domain' parameter. 'domain' will be a naming context
61 * string, i.e. foo.bar.sun.com ( i.e. a secure-RPC domain-
62 * name).
64 * void aggregate(void *handle, const char *tag, const char *value);
66 * Adds this tag / value pair to the set of aggregated attributes
67 * associated with the given handle.
69 * void set_cfghandle(void *handle);
71 * Sets and destroys the config object; SLP will no longer attempt
72 * to use this handle after this call. Thus, this call marks the
73 * end of configuration information for this handle.
76 #include <stdio.h>
77 #include <slp.h>
78 #include <stdlib.h>
79 #include <string.h>
80 #include <door.h>
81 #include <unistd.h>
82 #include "ns_sldap.h"
83 #include "ns_internal.h"
84 #include "cachemgr.h"
86 #define ABSTYPE "service:naming-directory"
87 #define CONTEXT_ATTR "naming-context"
88 #define LDAP_DOMAIN_ATTR "x-sun-rpcdomain"
90 /* The configuration cookie passed along through all SLP callbacks. */
91 struct config_cookie {
92 SLPHandle h; /* An open SLPHandle */
93 const char *type; /* The full service type to use */
94 char *scopes; /* A list of scopes to use */
95 const char *context_attr; /* Which attr to use for the ctx */
96 void *cache_cfg; /* caller-supplied config object */
97 void *(*get_cfghandle)(const char *);
98 void (*aggregate)(void *, const char *, const char *);
99 void (*set_cfghandle)(void *);
102 extern admin_t current_admin; /* ldap_cachemgr's admin struct */
105 * Utility routine: getlocale():
106 * Returns the locale specified by the SLP locale property, or just
107 * returns the default SLP locale if the property was not set.
109 static const char *getlocale() {
110 const char *locale = SLPGetProperty("net.slp.locale");
111 return (locale ? locale : "en");
115 * Utility routine: next_attr():
116 * Parses an SLP attribute string. On the first call, *type
117 * must be set to 0, and *s_inout must point to the beginning
118 * of the attr string. The following results are possible:
120 * If the term is of the form 'tag' only, *t_inout is set to tag,
121 * and *v_inout is set to NULL.
122 * If the term is of the form '(tag=val)', *t_inout and *v_inout
123 * are set to the tag and val strings, respectively.
124 * If the term is of the form '(tag=val1,val2,..,valN)', on each
125 * successive call, next_attr will return the next value. On the
126 * first invocation, tag is set to 'tag'; on successive invocations,
127 * tag is set to *t_inout.
129 * The string passed in *s_inout is destructively modified; all values
130 * returned simply point into the initial string. Hence the caller
131 * is responsible for all memory management. The type parameter is
132 * for internal use only and should be set to 0 by the caller only
133 * on the first invocation.
135 * If more attrs are available, returns SLP_TRUE, otherwise returns
136 * SLP_FALSE. If SLP_FALSE is returned, all value-result parameters
137 * will be undefined, and should not be used.
139 static SLPBoolean next_attr(char **t_inout, char **v_inout,
140 char **s_inout, int *type) {
141 char *end = NULL;
142 char *tag = NULL;
143 char *val = NULL;
144 char *state = NULL;
146 if (!t_inout || !v_inout)
147 return (SLP_FALSE);
149 if (!s_inout || !*s_inout || !**s_inout)
150 return (SLP_FALSE);
152 state = *s_inout;
154 /* type: 0 = start, 1 = '(tag=val)' type, 2 = 'tag' type */
155 switch (*type) {
156 case 0:
157 switch (*state) {
158 case '(':
159 *type = 1;
160 break;
161 case ',':
162 state++;
163 *type = 0;
164 break;
165 default:
166 *type = 2;
168 *s_inout = state;
169 return (next_attr(t_inout, v_inout, s_inout, type));
170 break;
171 case 1:
172 switch (*state) {
173 case '(':
174 /* start of attr of the form (tag=val[,val]) */
175 state++;
176 tag = state;
177 end = strchr(state, ')'); /* for sanity checking */
178 if (!end)
179 return (SLP_FALSE); /* fatal parse error */
181 state = strchr(tag, '=');
182 if (state) {
183 if (state > end)
184 return (SLP_FALSE); /* fatal parse err */
185 *state++ = 0;
186 } else {
187 return (SLP_FALSE); /* fatal parse error */
189 /* fallthru to default case, which handles multivals */
190 default:
191 /* somewhere in a multivalued attr */
192 if (!end) { /* did not fallthru from '(' case */
193 tag = *t_inout; /* leave tag as it was */
194 end = strchr(state, ')');
195 if (!end)
196 return (SLP_FALSE); /* fatal parse error */
199 val = state;
200 state = strchr(val, ','); /* is this attr multivalued? */
201 if (!state || state > end) {
202 /* no, so skip to the next attr */
203 state = end;
204 *type = 0;
205 } /* else attr is multivalued */
206 *state++ = 0;
207 break;
209 break;
210 case 2:
211 /* attr term with tag only */
212 tag = state;
213 state = strchr(tag, ',');
214 if (state) {
215 *state++ = 0;
217 val = NULL;
218 *type = 0;
219 break;
220 default:
221 return (SLP_FALSE);
224 *t_inout = tag;
225 *v_inout = val;
226 *s_inout = state;
228 return (SLP_TRUE);
232 * The SLP callback routine for foreach_server(). Aggregates each
233 * server's attributes into the caller-specified config object.
235 /*ARGSUSED*/
236 static SLPBoolean aggregate_attrs(SLPHandle h, const char *attrs_in,
237 SLPError errin, void *cookie) {
238 char *tag, *val, *state;
239 char *unesc_tag, *unesc_val;
240 int type = 0;
241 char *attrs;
242 SLPError err;
243 struct config_cookie *cfg = (struct config_cookie *)cookie;
245 if (errin != SLP_OK) {
246 return (SLP_TRUE);
249 attrs = strdup(attrs_in);
250 state = attrs;
252 while (next_attr(&tag, &val, &state, &type)) {
253 unesc_tag = unesc_val = NULL;
255 if (tag) {
256 if ((err = SLPUnescape(tag, &unesc_tag, SLP_TRUE)) != SLP_OK) {
257 unesc_tag = NULL;
258 if (current_admin.debug_level >= DBG_ALL) {
259 (void) logit("aggregate_attrs: ",
260 "could not unescape attr tag %s:%s\n",
261 tag, slp_strerror(err));
265 if (val) {
266 if ((err = SLPUnescape(val, &unesc_val, SLP_FALSE))
267 != SLP_OK) {
268 unesc_val = NULL;
269 if (current_admin.debug_level >= DBG_ALL) {
270 (void) logit("aggregate_attrs: ",
271 "could not unescape attr val %s:%s\n",
272 val, slp_strerror(err));
277 if (current_admin.debug_level >= DBG_ALL) {
278 (void) logit("discovery:\t\t%s=%s\n",
279 (unesc_tag ? unesc_tag : "NULL"),
280 (unesc_val ? unesc_val : "NULL"));
283 cfg->aggregate(cfg->cache_cfg, unesc_tag, unesc_val);
285 if (unesc_tag) free(unesc_tag);
286 if (unesc_val) free(unesc_val);
289 if (attrs) free(attrs);
291 return (SLP_TRUE);
295 * The SLP callback routine for update_config(). For each
296 * server found, retrieves that server's attributes.
298 /*ARGSUSED*/
299 static SLPBoolean foreach_server(SLPHandle hin, const char *u,
300 unsigned short life,
301 SLPError errin, void *cookie) {
302 SLPError err;
303 struct config_cookie *cfg = (struct config_cookie *)cookie;
304 SLPHandle h = cfg->h; /* an open handle */
305 SLPSrvURL *surl = NULL;
306 char *url = NULL;
308 if (errin != SLP_OK) {
309 return (SLP_TRUE);
312 /* dup url so we can slice 'n dice */
313 if (!(url = strdup(u))) {
314 (void) logit("foreach_server: no memory");
315 return (SLP_FALSE);
318 if ((err = SLPParseSrvURL(url, &surl)) != SLP_OK) {
319 free(url);
320 if (current_admin.debug_level >= DBG_NETLOOKUPS) {
321 (void) logit("foreach_server: ",
322 "dropping unparsable URL %s: %s\n",
323 url, slp_strerror(err));
324 return (SLP_TRUE);
328 if (current_admin.debug_level >= DBG_ALL) {
329 (void) logit("discovery:\tserver: %s\n", surl->s_pcHost);
332 /* retrieve all attrs for this server */
333 err = SLPFindAttrs(h, u, cfg->scopes, "", aggregate_attrs, cookie);
334 if (err != SLP_OK) {
335 if (current_admin.debug_level >= DBG_NETLOOKUPS) {
336 (void) logit("foreach_server: FindAttrs failed: %s\n",
337 slp_strerror(err));
339 goto cleanup;
342 /* add this server and its attrs to the config object */
343 cfg->aggregate(cfg->cache_cfg, "_,_xservers_,_", surl->s_pcHost);
345 cleanup:
346 if (url) free(url);
347 if (surl) SLPFree(surl);
349 return (SLP_TRUE);
353 * This routine does the dirty work of finding all servers for a
354 * given domain and injecting this information into the caller's
355 * configuration namespace via callbacks.
357 static void update_config(const char *context, struct config_cookie *cookie) {
358 SLPHandle h = NULL;
359 SLPHandle persrv_h = NULL;
360 SLPError err;
361 char *search = NULL;
362 char *unesc_domain = NULL;
364 /* Unescape the naming context string */
365 if ((err = SLPUnescape(context, &unesc_domain, SLP_FALSE)) != SLP_OK) {
366 if (current_admin.debug_level >= DBG_ALL) {
367 (void) logit("update_config: ",
368 "dropping unparsable domain: %s: %s\n",
369 context, slp_strerror(err));
371 return;
374 cookie->cache_cfg = cookie->get_cfghandle(unesc_domain);
376 /* Open a handle which all attrs calls can use */
377 if ((err = SLPOpen(getlocale(), SLP_FALSE, &persrv_h)) != SLP_OK) {
378 if (current_admin.debug_level >= DBG_NETLOOKUPS) {
379 (void) logit("update_config: SLPOpen failed: %s\n",
380 slp_strerror(err));
382 goto cleanup;
385 cookie->h = persrv_h;
387 if (current_admin.debug_level >= DBG_ALL) {
388 (void) logit("discovery: found naming context %s\n", context);
391 /* (re)construct the search filter form the input context */
392 search = malloc(strlen(cookie->context_attr) +
393 strlen(context) +
394 strlen("(=)") + 1);
395 if (!search) {
396 (void) logit("update_config: no memory\n");
397 goto cleanup;
399 (void) sprintf(search, "(%s=%s)", cookie->context_attr, context);
401 /* Find all servers which serve this context */
402 if ((err = SLPOpen(getlocale(), SLP_FALSE, &h)) != SLP_OK) {
403 if (current_admin.debug_level >= DBG_NETLOOKUPS) {
404 (void) logit("upate_config: SLPOpen failed: %s\n",
405 slp_strerror(err));
407 goto cleanup;
410 err = SLPFindSrvs(h, cookie->type, cookie->scopes,
411 search, foreach_server, cookie);
412 if (err != SLP_OK) {
413 if (current_admin.debug_level >= DBG_NETLOOKUPS) {
414 (void) logit("update_config: SLPFindSrvs failed: %s\n",
415 slp_strerror(err));
417 goto cleanup;
420 /* update the config cache with the new info */
421 cookie->set_cfghandle(cookie->cache_cfg);
423 cleanup:
424 if (h) SLPClose(h);
425 if (persrv_h) SLPClose(persrv_h);
426 if (search) free(search);
427 if (unesc_domain) free(unesc_domain);
431 * The SLP callback routine for find_all_contexts(). For each context
432 * found, finds all the servers and their attributes.
434 /*ARGSUSED*/
435 static SLPBoolean foreach_context(SLPHandle h, const char *attrs_in,
436 SLPError err, void *cookie) {
437 char *attrs, *tag, *val, *state;
438 int type = 0;
440 if (err != SLP_OK) {
441 return (SLP_TRUE);
445 * Parse out each context. Attrs will be of the following form:
446 * (naming-context=dc\3deng\2c dc\3dsun\2c dc\3dcom)
447 * Note that ',' and '=' are reserved in SLP, so they are escaped.
449 attrs = strdup(attrs_in); /* so we can slice'n'dice */
450 if (!attrs) {
451 (void) logit("foreach_context: no memory\n");
452 return (SLP_FALSE);
454 state = attrs;
456 while (next_attr(&tag, &val, &state, &type)) {
457 update_config(val, cookie);
460 free(attrs);
462 return (SLP_TRUE);
466 * Initiates server and attribute discovery for the concrete type
467 * 'type'. Currently the only useful type is "ldap", but perhaps
468 * "nis" and "nisplus" will also be useful in the future.
470 * get_cfghandle, aggregate, and set_cfghandle are callback routines
471 * used to pass any discovered configuration information back to the
472 * caller. See the introduction at the top of this file for more info.
474 static void find_all_contexts(const char *type,
475 void *(*get_cfghandle)(const char *),
476 void (*aggregate)(
477 void *, const char *, const char *),
478 void (*set_cfghandle)(void *)) {
479 SLPHandle h = NULL;
480 SLPError err;
481 struct config_cookie cookie[1];
482 char *fulltype = NULL;
483 char *scope = (char *)SLPGetProperty("net.slp.useScopes");
485 if (!scope || !*scope) {
486 scope = "default";
489 /* construct the full type from the partial type parameter */
490 fulltype = malloc(strlen(ABSTYPE) + strlen(type) + 2);
491 if (!fulltype) {
492 (void) logit("find_all_contexts: no memory");
493 goto done;
495 (void) sprintf(fulltype, "%s:%s", ABSTYPE, type);
497 /* set up the cookie for this discovery operation */
498 memset(cookie, 0, sizeof (*cookie));
499 cookie->type = fulltype;
500 cookie->scopes = scope;
501 if (strcasecmp(type, "ldap") == 0) {
502 /* Sun LDAP is special */
503 cookie->context_attr = LDAP_DOMAIN_ATTR;
504 } else {
505 cookie->context_attr = CONTEXT_ATTR;
507 cookie->get_cfghandle = get_cfghandle;
508 cookie->aggregate = aggregate;
509 cookie->set_cfghandle = set_cfghandle;
511 if ((err = SLPOpen(getlocale(), SLP_FALSE, &h)) != SLP_OK) {
512 if (current_admin.debug_level >= DBG_CANT_FIND) {
513 (void) logit("discover: %s",
514 "Aborting discovery: SLPOpen failed: %s\n",
515 slp_strerror(err));
517 goto done;
520 /* use find attrs to get a list of all available contexts */
521 err = SLPFindAttrs(h, fulltype, scope, cookie->context_attr,
522 foreach_context, cookie);
523 if (err != SLP_OK) {
524 if (current_admin.debug_level >= DBG_CANT_FIND) {
525 (void) logit(
526 "discover: Aborting discovery: SLPFindAttrs failed: %s\n",
527 slp_strerror(err));
529 goto done;
532 done:
533 if (h) SLPClose(h);
534 if (fulltype) free(fulltype);
538 * This is the ldap_cachemgr entry point into SLP dynamic discovery. The
539 * parameter 'r' should be a pointer to an unsigned int containing
540 * the requested interval at which the network should be queried.
542 void discover(void *r) {
543 unsigned short reqrefresh = *((unsigned int *)r);
545 for (;;) {
546 find_all_contexts("ldap",
547 __cache_get_cfghandle,
548 __cache_aggregate_params,
549 __cache_set_cfghandle);
551 if (current_admin.debug_level >= DBG_ALL) {
552 (void) logit(
553 "dynamic discovery: using refresh interval %d\n",
554 reqrefresh);
557 (void) sleep(reqrefresh);
561 #endif /* SLP */