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
  32 extern "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 
  40 struct ns_conn_user; /* connection user, forward definition */
  41 struct ns_conn_mt;   /* multi-threaded (MT) connection, forward definition */
  42 struct 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  */
  51 typedef 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  */
  61 typedef 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  */
  71 typedef 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  */
  98 struct 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  */
 118 typedef 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  */
 146 typedef 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  */
 175 typedef 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 
 212 typedef 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  */
 265 typedef enum {
 266         NS_SERVER_UP    = 1,
 267         NS_SERVER_DOWN  = 2
 268 } ns_server_status_t;
 269 
 270 typedef 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  */
 280 ns_conn_mgmt_t *__s_api_conn_mgmt_init();
 281 int __s_api_setup_mt_ld(LDAP *ld);
 282 int __s_api_check_mtckey();
 283 void __s_api_use_prev_conn_mgmt(int, ns_config_t *);
 284 ns_conn_user_t *__s_api_conn_user_init(int, void *, boolean_t);
 285 void __s_api_conn_mt_return(ns_conn_user_t *);
 286 void __s_api_conn_user_free(ns_conn_user_t *);
 287 int __s_api_conn_mt_add(Connection *con, ns_conn_user_t *, ns_ldap_error_t **);
 288 int __s_api_conn_mt_get(const char *, const int, const ns_cred_t *,
 289         Connection **, ns_ldap_error_t **, ns_conn_user_t *);
 290 void __s_api_conn_mt_remove(ns_conn_user_t *, int, ns_ldap_error_t **);
 291 int __s_api_check_libldap_MT_conn_support(ns_conn_user_t *, LDAP *ld,
 292         ns_ldap_error_t **);
 293 void __s_api_conn_mt_close(ns_conn_user_t *, int, ns_ldap_error_t **);
 294 void __s_api_reinit_conn_mgmt_new_config(ns_config_t *);
 295 int __s_api_setup_retry_search(ns_conn_user_t **, ns_conn_user_type_t, int *,
 296         int *, ns_ldap_error_t **);
 297 int __s_api_setup_getnext(ns_conn_user_t *, int *, ns_ldap_error_t **);
 298 void __s_api_shutdown_conn_mgmt();
 299 
 300 #ifdef __cplusplus
 301 }
 302 #endif
 303 
 304 #endif /* _NS_CONNMGMT_H */