xref: /illumos-gate/usr/src/cmd/nscd/getprof.c (revision 03831d35)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Routines to handle getprof* calls in nscd
31  */
32 
33 #include <assert.h>
34 #include <errno.h>
35 #include <memory.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/door.h>
41 #include <sys/stat.h>
42 #include <sys/time.h>
43 #include <sys/types.h>
44 #include <sys/wait.h>
45 #include <thread.h>
46 #include <unistd.h>
47 #include <ucred.h>
48 #include <nss_common.h>
49 
50 #include <prof_attr.h>
51 
52 #include <getxby_door.h>
53 #include "server_door.h"
54 #include "nscd.h"
55 
56 extern profstr_t *_getprofnam(const char *, profstr_t *, char *, int, int *);
57 
58 static hash_t *nam_hash;
59 static mutex_t  db_lock = DEFAULTMUTEX;
60 static waiter_t db_wait;
61 
62 static void getprof_namekeepalive(int keep, int interval);
63 static void update_prof_bucket(nsc_bucket_t **old, nsc_bucket_t *new,
64     int callnumber);
65 static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen);
66 static void do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam);
67 static void do_invalidate(nsc_bucket_t **ptr, int callnumber);
68 static void getprof_invalidate_unlocked(void);
69 
70 void
71 getprof_init(void)
72 {
73 	nam_hash = make_hash(current_admin.prof.nsc_suggestedsize);
74 }
75 
76 static void
77 do_invalidate(nsc_bucket_t ** ptr, int callnumber)
78 {
79 	if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) {
80 		/* leave pending calls alone */
81 		update_prof_bucket(ptr, NULL, callnumber);
82 	}
83 }
84 
85 static void
86 do_findgnams(nsc_bucket_t *ptr, int *table, char *gnam)
87 {
88 
89 	/*
90 	 * be careful with ptr - it may be -1 or NULL.
91 	 */
92 
93 	if (ptr != NULL && ptr != (nsc_bucket_t *)-1) {
94 		char *tmp = (char *)insertn(table, ptr->nsc_hits,
95 		    (int)strdup(gnam));
96 		if (tmp != (char *)-1)
97 			free(tmp);
98 	}
99 }
100 
101 void
102 getprof_revalidate(void)
103 {
104 	for (;;) {
105 		int slp;
106 		int interval;
107 		int count;
108 
109 		slp = current_admin.prof.nsc_pos_ttl;
110 
111 		if (slp < 60) {
112 			slp = 60;
113 		}
114 
115 		if ((count = current_admin.prof.nsc_keephot) != 0) {
116 			interval = (slp / 2)/count;
117 			if (interval == 0) interval = 1;
118 			sleep(slp * 2 / 3);
119 			getprof_namekeepalive(count, interval);
120 		} else {
121 			sleep(slp);
122 		}
123 	}
124 }
125 
126 static void
127 getprof_namekeepalive(int keep, int interval)
128 {
129 	int *table;
130 	union {
131 		nsc_data_t  ping;
132 		char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN];
133 	} u;
134 
135 	int i;
136 
137 	if (!keep)
138 		return;
139 
140 	table = maken(keep);
141 	mutex_lock(&db_lock);
142 	operate_hash(nam_hash, do_findgnams, (char *)table);
143 	mutex_unlock(&db_lock);
144 
145 	for (i = 1; i <= keep; i++) {
146 		char *tmp;
147 		u.ping.nsc_call.nsc_callnumber = GETPROFNAM;
148 
149 		if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1)
150 			continue; /* unused slot in table */
151 
152 		strcpy(u.ping.nsc_call.nsc_u.name, tmp);
153 
154 		launch_update(&u.ping.nsc_call);
155 		sleep(interval);
156 	}
157 
158 	for (i = 1; i <= keep; i++) {
159 		char *tmp;
160 		if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1)
161 			free(tmp);
162 	}
163 
164 	free(table);
165 }
166 
167 
168 /*
169  *   This routine marks all entries as invalid
170  *
171  */
172 
173 void
174 getprof_invalidate(void)
175 {
176 	mutex_lock(&db_lock);
177 	getprof_invalidate_unlocked();
178 	mutex_unlock(&db_lock);
179 }
180 
181 static void
182 getprof_invalidate_unlocked(void)
183 {
184 	operate_hash_addr(nam_hash, do_invalidate, (char *)GETPROFNAM);
185 	current_admin.prof.nsc_invalidate_count++;
186 }
187 
188 void
189 getprof_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now)
190 {
191 	int		out_of_date;
192 	nsc_bucket_t	*retb;
193 	char 		**bucket;
194 
195 	static time_t	lastmod;
196 
197 	int bufferspace = maxsize - sizeof (nsc_return_t);
198 
199 	if (current_admin.prof.nsc_enabled == 0) {
200 		out->nsc_return_code = NOSERVER;
201 		out->nsc_bufferbytesused = sizeof (*out);
202 		return;
203 	}
204 
205 	mutex_lock(&db_lock);
206 
207 	if (current_admin.prof.nsc_check_files) {
208 		struct stat buf;
209 
210 		if (stat(PROFATTR_FILENAME, &buf) < 0) {
211 			/*EMPTY*/;
212 		} else if (lastmod == 0) {
213 			lastmod = buf.st_mtime;
214 		} else if (lastmod < buf.st_mtime) {
215 			getprof_invalidate_unlocked();
216 			lastmod = buf.st_mtime;
217 		}
218 	}
219 
220 	if (current_admin.debug_level >= DBG_ALL) {
221 		logit("getprof_lookup: looking for name %s\n",
222 				in->nsc_u.name);
223 	}
224 
225 	for (;;) {
226 		if (attr_strlen(in->nsc_u.name) > NSCDMAXNAMELEN) {
227 			ucred_t *uc = NULL;
228 
229 			if (door_ucred(&uc) != 0) {
230 				logit("getprof_lookup: Name too long, "
231 				    "but no user credential: %s\n",
232 				    strerror(errno));
233 			} else {
234 				logit("getprof_lookup: Name too long "
235 				    "from pid %d uid %d\n",
236 				    ucred_getpid(uc),
237 				    ucred_getruid(uc));
238 				ucred_free(uc);
239 			}
240 
241 			out->nsc_errno = NSS_NOTFOUND;
242 			out->nsc_return_code = NOTFOUND;
243 			out->nsc_bufferbytesused = sizeof (*out);
244 			goto getout;
245 		}
246 		bucket = get_hash(nam_hash, in->nsc_u.name);
247 
248 		if (*bucket == (char *)-1) {	/* pending lookup */
249 			if (get_clearance(in->nsc_callnumber) != 0) {
250 			    /* no threads available */
251 				out->nsc_return_code = NOSERVER;
252 				    /* cannot process now */
253 				out->nsc_bufferbytesused =
254 				    sizeof (*out);
255 				current_admin.prof.nsc_throttle_count++;
256 				goto getout;
257 			}
258 			nscd_wait(&db_wait, &db_lock, bucket);
259 			release_clearance(in->nsc_callnumber);
260 			continue; /* go back and relookup hash bucket */
261 		}
262 		break;
263 	}
264 
265 	/*
266 	 * check for no name_service mode
267 	 */
268 
269 	if (*bucket == NULL && current_admin.avoid_nameservice) {
270 		out->nsc_return_code = NOTFOUND;
271 		out->nsc_bufferbytesused = sizeof (*out);
272 	} else if ((*bucket == NULL) || /* New entry in name service */
273 	    (in->nsc_callnumber & UPDATEBIT) || /* needs updating */
274 	    (out_of_date = (!current_admin.avoid_nameservice &&
275 	    (current_admin.prof.nsc_old_data_ok == 0) &&
276 	    (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) {
277 		/* time has expired */
278 		int saved_errno;
279 		int saved_hits = 0;
280 		profstr_t *p;
281 
282 		if (get_clearance(in->nsc_callnumber) != 0) {
283 		    /* no threads available */
284 			out->nsc_return_code = NOSERVER;
285 			    /* cannot process now */
286 			out->nsc_bufferbytesused = sizeof (*out);
287 			current_admin.prof.nsc_throttle_count++;
288 			goto getout;
289 		}
290 
291 		if (*bucket != NULL) {
292 			saved_hits =
293 			    ((nsc_bucket_t *)*bucket)->nsc_hits;
294 		}
295 
296 		/*
297 		 * block any threads accessing this bucket if data is
298 		 * non-existent out of date
299 		 */
300 
301 		if (*bucket == NULL || out_of_date) {
302 			update_prof_bucket((nsc_bucket_t **)bucket,
303 			    (nsc_bucket_t *)-1, in->nsc_callnumber);
304 		} else {
305 		/*
306 		 * if still not -1 bucket we are doing update...
307 		 * mark to prevent
308 		 * pileups of threads if the name service is hanging....
309 		 */
310 			((nsc_bucket_t *)(*bucket))->nsc_status |=
311 			    ST_UPDATE_PENDING;
312 			/* cleared by deletion of old data */
313 		}
314 		mutex_unlock(&db_lock);
315 
316 		/*
317 		 * Call non-caching version in libnsl.
318 		 */
319 		p = _getprofnam(in->nsc_u.name, &out->nsc_u.prof,
320 		    out->nsc_u.buff + sizeof (profstr_t),
321 		    bufferspace, &errno);
322 		saved_errno = errno;
323 
324 		mutex_lock(&db_lock);
325 
326 		release_clearance(in->nsc_callnumber);
327 
328 		if (p == NULL) { /* data not found */
329 
330 			if (current_admin.debug_level >= DBG_CANT_FIND) {
331 		logit("getprof_lookup: nscd COULDN'T FIND prof_attr name %s\n",
332 						in->nsc_u.name);
333 			}
334 
335 			if (!(UPDATEBIT & in->nsc_callnumber))
336 			    current_admin.prof.nsc_neg_cache_misses++;
337 
338 			retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t));
339 
340 			retb->nsc_refcount = 1;
341 			retb->nsc_data.nsc_bufferbytesused =
342 				sizeof (nsc_return_t);
343 			retb->nsc_data.nsc_return_code = NOTFOUND;
344 			retb->nsc_data.nsc_errno = saved_errno;
345 			memcpy(out, &retb->nsc_data,
346 			    retb->nsc_data.nsc_bufferbytesused);
347 			update_prof_bucket((nsc_bucket_t **)bucket,
348 			    retb, in->nsc_callnumber);
349 			goto getout;
350 		} else {
351 			if (current_admin.debug_level >= DBG_ALL) {
352 		logit("getprof_lookup: nscd FOUND prof_attr name %s\n",
353 						in->nsc_u.name);
354 			}
355 			if (!(UPDATEBIT & in->nsc_callnumber))
356 			    current_admin.prof.nsc_pos_cache_misses++;
357 
358 			retb = fixbuffer(out, bufferspace);
359 			update_prof_bucket((nsc_bucket_t **)bucket,
360 			    retb, in->nsc_callnumber);
361 			if (saved_hits)
362 				retb->nsc_hits = saved_hits;
363 		}
364 	} else { 	/* found entry in cache */
365 		retb = (nsc_bucket_t *)*bucket;
366 
367 		retb->nsc_hits++;
368 
369 		memcpy(out, &(retb->nsc_data),
370 		    retb->nsc_data.nsc_bufferbytesused);
371 
372 		if (out->nsc_return_code == SUCCESS) {
373 			if (!(UPDATEBIT & in->nsc_callnumber))
374 			    current_admin.prof.nsc_pos_cache_hits++;
375 			if (current_admin.debug_level >= DBG_ALL) {
376 			logit("getprof_lookup: found name %s in cache\n",
377 						in->nsc_u.name);
378 			}
379 		} else {
380 			if (!(UPDATEBIT & in->nsc_callnumber))
381 			    current_admin.prof.nsc_neg_cache_hits++;
382 			if (current_admin.debug_level >= DBG_ALL) {
383 		logit("getprof_lookup: %s marked as NOT FOUND in cache.\n",
384 						in->nsc_u.name);
385 			}
386 		}
387 
388 		if ((retb->nsc_timestamp < now) &&
389 		    !(in->nsc_callnumber & UPDATEBIT) &&
390 		    !(retb->nsc_status & ST_UPDATE_PENDING)) {
391 		logit("launch update since time = %d\n", retb->nsc_timestamp);
392 			retb->nsc_status |= ST_UPDATE_PENDING;
393 			/* cleared by deletion of old data */
394 			launch_update(in);
395 		}
396 	}
397 
398 getout:
399 	mutex_unlock(&db_lock);
400 }
401 
402 /*ARGSUSED*/
403 static void
404 update_prof_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber)
405 {
406 	if (*old != NULL && *old != (nsc_bucket_t *)-1) { /* old data exists */
407 		free(*old);
408 		current_admin.prof.nsc_entries--;
409 	}
410 
411 	/*
412 	 *  we can do this before reseting *old since we're holding the lock
413 	 */
414 
415 	else if (*old == (nsc_bucket_t *)-1) {
416 		nscd_signal(&db_wait, (char **)old);
417 	}
418 
419 	*old = new;
420 
421 	if ((new != NULL) && (new != (nsc_bucket_t *)-1)) {
422 		/* real data, not just update pending or invalidate */
423 		new->nsc_hits = 1;
424 		new->nsc_status = 0;
425 		new->nsc_refcount = 1;
426 		current_admin.prof.nsc_entries++;
427 
428 		if (new->nsc_data.nsc_return_code == SUCCESS) {
429 			new->nsc_timestamp = time(NULL) +
430 			    current_admin.prof.nsc_pos_ttl;
431 		} else {
432 			new->nsc_timestamp = time(NULL) +
433 			    current_admin.prof.nsc_neg_ttl;
434 		}
435 	}
436 }
437 
438 /*ARGSUSED*/
439 static nsc_bucket_t *
440 fixbuffer(nsc_return_t *in, int maxlen)
441 {
442 	nsc_bucket_t *retb;
443 	nsc_return_t *out;
444 	char 	*dest;
445 	int 	offset;
446 	int 	strs;
447 
448 	/*
449 	 * find out the size of the data block we're going to need
450 	 */
451 
452 	strs = attr_strlen(in->nsc_u.prof.name) +
453 	    attr_strlen(in->nsc_u.prof.res1) +
454 	    attr_strlen(in->nsc_u.prof.res2) +
455 	    attr_strlen(in->nsc_u.prof.desc) +
456 	    attr_strlen(in->nsc_u.prof.attr) + PROFATTR_DB_NCOL;
457 
458 	/*
459 	 * allocate it and copy it in
460 	 * code doesn't assume packing order in original buffer
461 	 */
462 
463 	if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) {
464 		return (NULL);
465 	}
466 
467 	out = &(retb->nsc_data);
468 	out->nsc_bufferbytesused = strs + ((int)&out->nsc_u.prof - (int)out) +
469 	    sizeof (profstr_t);
470 	out->nsc_return_code 	= SUCCESS;
471 	out->nsc_errno 		= 0;
472 
473 	dest = retb->nsc_data.nsc_u.buff + sizeof (profstr_t);
474 	offset = (int)dest;
475 
476 	attr_strcpy(dest, in->nsc_u.prof.name);
477 	strs = 1 + attr_strlen(in->nsc_u.prof.name);
478 	out->nsc_u.prof.name = dest - offset;
479 	dest += strs;
480 
481 	attr_strcpy(dest, in->nsc_u.prof.res1);
482 	strs = 1 + attr_strlen(in->nsc_u.prof.res1);
483 	out->nsc_u.prof.res1 = dest - offset;
484 	dest += strs;
485 
486 	attr_strcpy(dest, in->nsc_u.prof.res2);
487 	strs = 1 + attr_strlen(in->nsc_u.prof.res2);
488 	out->nsc_u.prof.res2 = dest - offset;
489 	dest += strs;
490 
491 	attr_strcpy(dest, in->nsc_u.prof.desc);
492 	strs = 1 + attr_strlen(in->nsc_u.prof.desc);
493 	out->nsc_u.prof.desc = dest - offset;
494 	dest += strs;
495 
496 	attr_strcpy(dest, in->nsc_u.prof.attr);
497 	out->nsc_u.prof.attr = dest - offset;
498 
499 	memcpy(in, out, out->nsc_bufferbytesused);
500 
501 	return (retb);
502 }
503 
504 void
505 getprof_reaper(void)
506 {
507 	nsc_reaper("getprof", nam_hash, &current_admin.prof, &db_lock);
508 }
509