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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
26 */
27
28#ifndef	_NS_CONNMGMT_H
29#define	_NS_CONNMGMT_H
30
31#ifdef __cplusplus
32extern "C" {
33#endif
34
35#include <thread.h>
36#include "ns_sldap.h"
37#include "ns_internal.h"
38#include "ns_cache_door.h"
39
40struct ns_conn_user; /* connection user, forward definition */
41struct ns_conn_mt;   /* multi-threaded (MT) connection, forward definition */
42struct ns_conn_mgmt; /* connection management, forward definition */
43
44#define	NS_CONN_MT_USER_NO_MAX	-1
45#define	NS_CONN_MT_USER_MAX	NS_CONN_MT_USER_NO_MAX
46#define	NS_LIST_TRY_MAX		3
47
48/*
49 * Structure for handling the waiter of a pending multi-threaded (MT) connection
50 */
51typedef struct ns_conn_waiter {
52	cond_t			waitcv;
53	uint8_t			signaled;
54	struct ns_conn_user	*key;
55	struct ns_conn_waiter	*next, *prev;
56} ns_conn_waiter_t;
57
58/*
59 * type of a connection user
60 */
61typedef enum {
62	NS_CONN_USER_SEARCH	= 1,
63	NS_CONN_USER_WRITE	= 2,
64	NS_CONN_USER_AUTH	= 3,
65	NS_CONN_USER_GETENT	= 4
66} ns_conn_user_type_t;
67
68/*
69 * state of a connection user
70 */
71typedef enum {
72	NS_CONN_USER_UNINITED		= 0,
73	NS_CONN_USER_ALLOCATED		= 1,
74	NS_CONN_USER_FINDING		= 2, /* looking for an MT connection */
75	NS_CONN_USER_WAITING		= 3, /* waiting for an MT connection */
76	NS_CONN_USER_WOKEUP		= 4,
77	NS_CONN_USER_CONNECT_ERROR	= 5,
78	NS_CONN_USER_CONNECTED  	= 6,
79	NS_CONN_USER_DISCONNECTED	= 7,
80	NS_CONN_USER_FREED		= 8
81} ns_conn_user_state_t;
82
83/*
84 * A connection user represents a request processed by libsldap. It
85 * usually is a thread using the same connection from start to end.
86 * Different connection users of the same type can share the same
87 * connection opened for that type. But search and getent users can
88 * share the same connection opened for either search or getent. AUTH
89 * connection are not shareable.
90 *
91 * A getent user may have a longer lifespan and live outside of libsldap.
92 * This is because the associated search cookie is passed back to the caller
93 * via the firstEntry call and used in the subsequent nextEntry or endEntry
94 * calls. Even though the firstEntry and the nextEntry/endEntry calls may
95 * be running in a different thread, the connection being used will be the
96 * same. It is the one assigend during the firstEntry call.
97 */
98struct ns_conn_user {
99	ns_conn_user_type_t	type; /* search, write, auth, getent, ... */
100	ns_conn_user_state_t	state;
101	thread_t		tid;   /* id of the thread starts the request */
102	struct ns_conn_user	*next; /* next conn_user in the linked list */
103	struct ns_conn_mt	*conn_mt; /* the MT connection being used */
104	struct ns_conn_mgmt	*conn_mgmt; /* ref counted conn management */
105	void			*userinfo; /* private data of the request */
106	ns_ldap_return_code	ns_rc; /* error return code */
107	ns_ldap_error_t		*ns_error; /* error info */
108	boolean_t		referral; /* using a referred server ? */
109	boolean_t		retry; /* retry the request on certain error? */
110	boolean_t		keep_conn; /* keep the conn for reuse ? */
111	boolean_t		use_mt_conn; /* using/used an MT connection ? */
112	boolean_t		bad_mt_conn; /* MT connection is not usable ? */
113};
114
115/*
116 * state of an MT connection
117 */
118typedef enum {
119	NS_CONN_MT_UNINITED		= 0,
120	NS_CONN_MT_CONNECTING		= 1,
121	NS_CONN_MT_CONNECT_ERROR	= 2,
122	NS_CONN_MT_CONNECTED		= 3,
123	NS_CONN_MT_CLOSING		= 4
124} ns_conn_mt_state_t;
125
126/*
127 * An ns_conn_mt (or MT connection) represents an ldap connection
128 * that can be shared among multiple threads. It also represents
129 * the set of connection users using the ldap connection. It contains
130 * a pointer to the Connection structure that has the physical info
131 * of the connection (server name, address, ldap handle, etc). It
132 * also contains a linked list of all the conn_user using the ldap
133 * connection. The connection users can wait on an MT connection
134 * to become available or be told to abort and clean up when one of
135 * the connection user detects an error and knows that the connection
136 * is no longer usable. The error info is then saved in the structure
137 * for other users to consume.
138 *
139 * An MT connection is meant to be shared concurrently and persistent.
140 * Even when there's no current user, it will be kept by the connection
141 * management, waiting for the next user. It will be closed when
142 * a connection error is detected, when a better server should be
143 * used, when the Native LDAP configuration change, or when the libsldap
144 * is being unloaded.
145 */
146typedef struct ns_conn_mt {
147	mutex_t			lock;
148	ns_conn_mt_state_t	state;
149	pid_t			pid; /* process creates the connection */
150	thread_t		tid; /* thread creates the connection */
151	struct ns_conn_mt	*next; /* next conn_mt in the linked list */
152	ns_conn_user_t		*cu_head; /* head of conn_user linked list */
153	ns_conn_user_t		*cu_tail; /* tail of conn_user linked list */
154	struct ns_conn_mgmt	*conn_mgmt; /* ref counted conn management */
155	ns_conn_waiter_t	waiter; /* first of the connection waiters */
156	uint_t			cu_cnt; /* number of the using conn_user */
157	int32_t			cu_max; /* max. allowed number of conn_user */
158	uint_t			waiter_cnt; /* number of waiters */
159	ns_conn_user_type_t	opened_for; /* type of conn_user opened for */
160	Connection		*conn; /* name, IP address, ldap handle, etc */
161	time_t			create_time; /* time when connection created */
162	time_t			access_time; /* time when last used */
163	ns_ldap_return_code	ns_rc; /* saved error code */
164	ns_ldap_error_t		*ns_error; /* saved error info */
165	boolean_t		close_when_nouser;  /* close connection when */
166						    /* last user is done ? */
167	boolean_t		detached; /* no longer in connection pool? */
168	boolean_t		referral; /* using a referred server ? */
169} ns_conn_mt_t;
170
171/*
172 * state of a connection management
173 * (a connection pool sharing the same native LDAP configuration)
174 */
175typedef enum {
176	NS_CONN_MGMT_UNINITED	= 0,
177	NS_CONN_MGMT_INACTIVE	= 1, /* conn sharing not yet requested */
178	NS_CONN_MGMT_ACTIVE	= 2, /* connection sharing required/requested */
179	NS_CONN_MGMT_DETACHED	= 3  /* on the way down, no new user allowed */
180} ns_conn_mgmt_state_t;
181
182/*
183 * An ns_conn_mgmt (or connection management) represents the set of MT
184 * connections using the same native LDAP configuration. It is a connection
185 * pool that can adjust the MT connection status and usage based on the
186 * change notifications it receives from the ldap_cachemgr daemon, OR When
187 * the change is detected at config refresh time. When a server status
188 * change (up or down) notification is received or detected, it will
189 * close the MT connections using the server. Or mark them as to-be-closed
190 * and close them when all users are done using them. When a config change
191 * notice is received, it will detach itself and allow a new ns_conn_mgmt be
192 * created for the new configuration. The old config would still be used
193 * by the detached ns_conn_mgmt. Both will be destroyed when all existing
194 * conn_user are done. Any conn_user and MT connection created after the
195 * configuration switch will use the new configuration.
196 *
197 * Note that there's always just one current ns_conn_mgmt. Its usage is
198 * reference counted. Any new conn_user or MT connection referencing
199 * the ns_conn_mgmt adds 1 to the count, any release of the ns_conn_mgmt
200 * decrement the count by 1. The ns_conn_mgmt can not be freed until
201 * the reference count becomes zero.
202 *
203 * Each ns_conn_mgmt references a native LDAP configuration. The config
204 * component of this library always maintains a global configuration. It is
205 * referred to as the current global config. The current ns_conn_mgmt
206 * uses that global config. When an ns_conn_mgmt is detached, or not
207 * longer active/current, the config it uses is no longer the current global
208 * one, which is referred as the per connection management config. When
209 * the ns_conn_mgmt is freed, the config will also be destroyed.
210 */
211
212typedef struct ns_conn_mgmt {
213	mutex_t		lock;
214	ns_conn_mgmt_state_t state;
215	pid_t		pid; /* process creates the conn_mgmt */
216	thread_t	procchg_tid; /* id of the change monitor thread */
217	ns_conn_mt_t	*cm_head; /* head of the conn_mt linked list */
218	ns_conn_mt_t	*cm_tail; /* tail of the conn_mt linked list */
219	mutex_t		cfg_lock; /* lock serializes access to config */
220	ldap_get_chg_cookie_t cfg_cookie; /* used to detect if config changes */
221	ns_config_t	*config; /* the native LDAP config being used */
222	char		**pservers; /* preferred servers defined in config */
223	uint_t		cm_cnt;  /* number of MT connection in the pool */
224	uint_t		ref_cnt; /* number of reference by conn_MT/conn_user */
225	boolean_t	is_nscd; /* running in a nscd ? */
226	boolean_t	is_peruser_nscd; /* running in a per-user nscd ? */
227	boolean_t	ldap_mt; /* libldap supports multi-threaded client ? */
228	boolean_t	do_mt_conn;	/* need and able to do MT conn ? */
229	boolean_t	shutting_down;  /* on the way down ? */
230	boolean_t	cfg_reloaded;   /* config is not current ? */
231	boolean_t	procchg_started; /* change monitor thread started ? */
232	boolean_t	procchg_door_call; /* in door call and waiting ? */
233	boolean_t	pservers_loaded; /* pservers array is set ? */
234} ns_conn_mgmt_t;
235
236/*
237 * For a connection management and the conn_mt connections it manages, it is
238 * very helpful to know exactly when the Native LDAP configuration changes
239 * and when the status of the configured servers change. If the config
240 * changes, new connection management will be created. If servers go up
241 * or down, conn_mt connections being used need to be dropped or switched.
242 * For processes other than the main nscd, the changes has to be detected
243 * in a less efficient way by libsldap. For the main nscd (not including
244 * peruser nscd), the connection management which has active conn_mt
245 * connections can rely on the ldap_cachemgr daemon to report if there's any
246 * change in servers' status or if the native LDAP configuration has changed.
247 *
248 * The mechanism for reporting of the changes is a door call sent from
249 * libsldap to ldap_cachemgr. The call will not be returned until changes
250 * detected by ldap_cachemgr. When the change info is passed back to
251 * libsldap, the change monitor thread will wake up from the door call
252 * and process the notification. For servers went from up to down, the
253 * associated MT connections will be closed, and then all conn_users'
254 * state will be marked as closing. When a conn_user notices it, the
255 * operations represented by that conn_user will be ended with error
256 * info. When a more preferred server is up, MT connections using
257 * less preferred servers will be marked as closed-when-all-user-done,
258 * so that new connection will be opened and using the preferred server.
259 * A configuration change causes the current connection management and
260 * the configuration it uses to become detached but continually being
261 * used by the old MT connections. Any new MT connection opened will
262 * be put in a new connection management and uses the new configuration
263 * immediately.
264 */
265typedef enum {
266	NS_SERVER_UP	= 1,
267	NS_SERVER_DOWN	= 2
268} ns_server_status_t;
269
270typedef struct ns_server_status_change {
271	int			num_server;
272	boolean_t		config_changed;
273	ns_server_status_t	*changes;	/* array of status change */
274	char			**servers;	/* array of server */
275} ns_server_status_change_t;
276
277/*
278 * connection management functions
279 */
280ns_conn_mgmt_t *__s_api_conn_mgmt_init();
281int __s_api_setup_mt_ld(LDAP *ld);
282int __s_api_check_mtckey();
283void __s_api_use_prev_conn_mgmt(int, ns_config_t *);
284ns_conn_user_t *__s_api_conn_user_init(int, void *, boolean_t);
285void __s_api_conn_mt_return(ns_conn_user_t *);
286void __s_api_conn_user_free(ns_conn_user_t *);
287int __s_api_conn_mt_add(Connection *con, ns_conn_user_t *, ns_ldap_error_t **);
288int __s_api_conn_mt_get(const char *, const int, const ns_cred_t *,
289	Connection **, ns_ldap_error_t **, ns_conn_user_t *);
290void __s_api_conn_mt_remove(ns_conn_user_t *, int, ns_ldap_error_t **);
291int __s_api_check_libldap_MT_conn_support(ns_conn_user_t *, LDAP *ld,
292	ns_ldap_error_t **);
293void __s_api_conn_mt_close(ns_conn_user_t *, int, ns_ldap_error_t **);
294void __s_api_reinit_conn_mgmt_new_config(ns_config_t *);
295int __s_api_setup_retry_search(ns_conn_user_t **, ns_conn_user_type_t, int *,
296	int *, ns_ldap_error_t **);
297int __s_api_setup_getnext(ns_conn_user_t *, int *, ns_ldap_error_t **);
298void __s_api_shutdown_conn_mgmt();
299
300#ifdef __cplusplus
301}
302#endif
303
304#endif /* _NS_CONNMGMT_H */
305