1/* thread.c - deal with thread subsystem */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 2001-2011 The OpenLDAP Foundation.
6 * Portions Copyright 2001-2003 Pierangelo Masarati.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17/* ACKNOWLEDGEMENTS:
18 * This work was initially developed by Pierangelo Masarati for inclusion
19 * in OpenLDAP Software.
20 */
21
22#include "portable.h"
23
24#include <stdio.h>
25#include <ac/string.h>
26
27#include "slap.h"
28#include "back-monitor.h"
29
30#include <ldap_rq.h>
31
32#ifndef NO_THREADS
33typedef enum {
34	MT_UNKNOWN,
35	MT_RUNQUEUE,
36	MT_TASKLIST,
37
38	MT_LAST
39} monitor_thread_t;
40
41static struct {
42	struct berval			rdn;
43	struct berval			desc;
44	struct berval			nrdn;
45	ldap_pvt_thread_pool_param_t	param;
46	monitor_thread_t		mt;
47}		mt[] = {
48	{ BER_BVC( "cn=Max" ),
49		BER_BVC("Maximum number of threads as configured"),
50		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_MAX,		MT_UNKNOWN },
51	{ BER_BVC( "cn=Max Pending" ),
52		BER_BVC("Maximum number of pending threads"),
53		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_MAX_PENDING,	MT_UNKNOWN },
54	{ BER_BVC( "cn=Open" ),
55		BER_BVC("Number of open threads"),
56		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_OPEN,	MT_UNKNOWN },
57	{ BER_BVC( "cn=Starting" ),
58		BER_BVC("Number of threads being started"),
59		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_STARTING,	MT_UNKNOWN },
60	{ BER_BVC( "cn=Active" ),
61		BER_BVC("Number of active threads"),
62		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_ACTIVE,	MT_UNKNOWN },
63	{ BER_BVC( "cn=Pending" ),
64		BER_BVC("Number of pending threads"),
65		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_PENDING,	MT_UNKNOWN },
66	{ BER_BVC( "cn=Backload" ),
67		BER_BVC("Number of active plus pending threads"),
68		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD,	MT_UNKNOWN },
69#if 0	/* not meaningful right now */
70	{ BER_BVC( "cn=Active Max" ),
71		BER_BVNULL,
72		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_ACTIVE_MAX,	MT_UNKNOWN },
73	{ BER_BVC( "cn=Pending Max" ),
74		BER_BVNULL,
75		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_PENDING_MAX,	MT_UNKNOWN },
76	{ BER_BVC( "cn=Backload Max" ),
77		BER_BVNULL,
78		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD_MAX,MT_UNKNOWN },
79#endif
80	{ BER_BVC( "cn=State" ),
81		BER_BVC("Thread pool state"),
82		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_STATE,	MT_UNKNOWN },
83
84	{ BER_BVC( "cn=Runqueue" ),
85		BER_BVC("Queue of running threads - besides those handling operations"),
86		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN,	MT_RUNQUEUE },
87	{ BER_BVC( "cn=Tasklist" ),
88		BER_BVC("List of running plus standby threads - besides those handling operations"),
89		BER_BVNULL,	LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN,	MT_TASKLIST },
90
91	{ BER_BVNULL }
92};
93
94static int
95monitor_subsys_thread_update(
96	Operation		*op,
97	SlapReply		*rs,
98	Entry 			*e );
99#endif /* ! NO_THREADS */
100
101/*
102 * initializes log subentry
103 */
104int
105monitor_subsys_thread_init(
106	BackendDB       	*be,
107	monitor_subsys_t	*ms )
108{
109#ifndef NO_THREADS
110	monitor_info_t	*mi;
111	monitor_entry_t	*mp;
112	Entry		*e, **ep, *e_thread;
113	int		i;
114
115	ms->mss_update = monitor_subsys_thread_update;
116
117	mi = ( monitor_info_t * )be->be_private;
118
119	if ( monitor_cache_get( mi, &ms->mss_ndn, &e_thread ) ) {
120		Debug( LDAP_DEBUG_ANY,
121			"monitor_subsys_thread_init: unable to get entry \"%s\"\n",
122			ms->mss_dn.bv_val,
123			0, 0 );
124		return( -1 );
125	}
126
127	mp = ( monitor_entry_t * )e_thread->e_private;
128	mp->mp_children = NULL;
129	ep = &mp->mp_children;
130
131	for ( i = 0; !BER_BVISNULL( &mt[ i ].rdn ); i++ ) {
132		static char	buf[ BACKMONITOR_BUFSIZE ];
133		int		count = -1;
134		char		*state = NULL;
135		struct berval	bv = BER_BVNULL;
136
137		/*
138		 * Max
139		 */
140		e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn,
141			&mt[ i ].rdn,
142			mi->mi_oc_monitoredObject, mi, NULL, NULL );
143		if ( e == NULL ) {
144			Debug( LDAP_DEBUG_ANY,
145				"monitor_subsys_thread_init: "
146				"unable to create entry \"%s,%s\"\n",
147				mt[ i ].rdn.bv_val,
148				ms->mss_ndn.bv_val, 0 );
149			return( -1 );
150		}
151
152		/* NOTE: reference to the normalized DN of the entry,
153		 * under the assumption it's not modified */
154		dnRdn( &e->e_nname, &mt[ i ].nrdn );
155
156		switch ( mt[ i ].param ) {
157		case LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN:
158			break;
159
160		case LDAP_PVT_THREAD_POOL_PARAM_STATE:
161			if ( ldap_pvt_thread_pool_query( &connection_pool,
162				mt[ i ].param, (void *)&state ) == 0 )
163			{
164				ber_str2bv( state, 0, 0, &bv );
165
166			} else {
167				BER_BVSTR( &bv, "unknown" );
168			}
169			break;
170
171		default:
172			/* NOTE: in case of error, it'll be set to -1 */
173			(void)ldap_pvt_thread_pool_query( &connection_pool,
174				mt[ i ].param, (void *)&count );
175			bv.bv_val = buf;
176			bv.bv_len = snprintf( buf, sizeof( buf ), "%d", count );
177			break;
178		}
179
180		if ( !BER_BVISNULL( &bv ) ) {
181			attr_merge_normalize_one( e, mi->mi_ad_monitoredInfo, &bv, NULL );
182		}
183
184		if ( !BER_BVISNULL( &mt[ i ].desc ) ) {
185			attr_merge_normalize_one( e,
186				slap_schema.si_ad_description,
187				&mt[ i ].desc, NULL );
188		}
189
190		mp = monitor_entrypriv_create();
191		if ( mp == NULL ) {
192			return -1;
193		}
194		e->e_private = ( void * )mp;
195		mp->mp_info = ms;
196		mp->mp_flags = ms->mss_flags \
197			| MONITOR_F_SUB | MONITOR_F_PERSISTENT;
198
199		if ( monitor_cache_add( mi, e ) ) {
200			Debug( LDAP_DEBUG_ANY,
201				"monitor_subsys_thread_init: "
202				"unable to add entry \"%s,%s\"\n",
203				mt[ i ].rdn.bv_val,
204				ms->mss_dn.bv_val, 0 );
205			return( -1 );
206		}
207
208		*ep = e;
209		ep = &mp->mp_next;
210	}
211
212	monitor_cache_release( mi, e_thread );
213
214#endif /* ! NO_THREADS */
215	return( 0 );
216}
217
218#ifndef NO_THREADS
219static int
220monitor_subsys_thread_update(
221	Operation		*op,
222	SlapReply		*rs,
223	Entry 			*e )
224{
225	monitor_info_t	*mi = ( monitor_info_t * )op->o_bd->be_private;
226	Attribute		*a;
227	BerVarray		vals = NULL;
228	char 			buf[ BACKMONITOR_BUFSIZE ];
229	struct berval		rdn, bv;
230	int			which, i;
231	struct re_s		*re;
232	int			count = -1;
233	char			*state = NULL;
234
235	assert( mi != NULL );
236
237	dnRdn( &e->e_nname, &rdn );
238
239	for ( i = 0; !BER_BVISNULL( &mt[ i ].nrdn ); i++ ) {
240		if ( dn_match( &mt[ i ].nrdn, &rdn ) ) {
241			break;
242		}
243	}
244
245	which = i;
246	if ( BER_BVISNULL( &mt[ which ].nrdn ) ) {
247		return SLAP_CB_CONTINUE;
248	}
249
250	a = attr_find( e->e_attrs, mi->mi_ad_monitoredInfo );
251
252	switch ( mt[ which ].param ) {
253	case LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN:
254		switch ( mt[ which ].mt ) {
255		case MT_RUNQUEUE:
256			if ( a != NULL ) {
257				if ( a->a_nvals != a->a_vals ) {
258					ber_bvarray_free( a->a_nvals );
259				}
260				ber_bvarray_free( a->a_vals );
261				a->a_vals = NULL;
262				a->a_nvals = NULL;
263				a->a_numvals = 0;
264			}
265
266			i = 0;
267			bv.bv_val = buf;
268			ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
269			LDAP_STAILQ_FOREACH( re, &slapd_rq.run_list, rnext ) {
270				bv.bv_len = snprintf( buf, sizeof( buf ), "{%d}%s(%s)",
271					i, re->tname, re->tspec );
272				if ( bv.bv_len < sizeof( buf ) ) {
273					value_add_one( &vals, &bv );
274				}
275				i++;
276			}
277			ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
278
279			if ( vals ) {
280				attr_merge_normalize( e, mi->mi_ad_monitoredInfo, vals, NULL );
281				ber_bvarray_free( vals );
282
283			} else {
284				attr_delete( &e->e_attrs, mi->mi_ad_monitoredInfo );
285			}
286			break;
287
288		case MT_TASKLIST:
289			if ( a != NULL ) {
290				if ( a->a_nvals != a->a_vals ) {
291					ber_bvarray_free( a->a_nvals );
292				}
293				ber_bvarray_free( a->a_vals );
294				a->a_vals = NULL;
295				a->a_nvals = NULL;
296				a->a_numvals = 0;
297			}
298
299			i = 0;
300			bv.bv_val = buf;
301			ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
302			LDAP_STAILQ_FOREACH( re, &slapd_rq.task_list, tnext ) {
303				bv.bv_len = snprintf( buf, sizeof( buf ), "{%d}%s(%s)",
304					i, re->tname, re->tspec );
305				if ( bv.bv_len < sizeof( buf ) ) {
306					value_add_one( &vals, &bv );
307				}
308				i++;
309			}
310			ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
311
312			if ( vals ) {
313				attr_merge_normalize( e, mi->mi_ad_monitoredInfo, vals, NULL );
314				ber_bvarray_free( vals );
315
316			} else {
317				attr_delete( &e->e_attrs, mi->mi_ad_monitoredInfo );
318			}
319			break;
320
321		default:
322			assert( 0 );
323		}
324		break;
325
326	case LDAP_PVT_THREAD_POOL_PARAM_STATE:
327		if ( a == NULL ) {
328			return rs->sr_err = LDAP_OTHER;
329		}
330		if ( ldap_pvt_thread_pool_query( &connection_pool,
331			mt[ i ].param, (void *)&state ) == 0 )
332		{
333			ber_str2bv( state, 0, 0, &bv );
334			ber_bvreplace( &a->a_vals[ 0 ], &bv );
335		}
336		break;
337
338	default:
339		if ( a == NULL ) {
340			return rs->sr_err = LDAP_OTHER;
341		}
342		if ( ldap_pvt_thread_pool_query( &connection_pool,
343			mt[ i ].param, (void *)&count ) == 0 )
344		{
345			bv.bv_val = buf;
346			bv.bv_len = snprintf( buf, sizeof( buf ), "%d", count );
347			if ( bv.bv_len < sizeof( buf ) ) {
348				ber_bvreplace( &a->a_vals[ 0 ], &bv );
349			}
350		}
351		break;
352	}
353
354	/* FIXME: touch modifyTimestamp? */
355
356	return SLAP_CB_CONTINUE;
357}
358#endif /* ! NO_THREADS */
359