1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 #include <sys/types.h>
  17 #include <sys/conf.h>
  18 #include <sys/sysmacros.h>
  19 #include <sys/cmn_err.h>
  20 #include <sys/stat.h>
  21 #include <sys/ddi.h>
  22 #include <sys/sunddi.h>
  23 #include <sys/proc.h>
  24 #include <sys/class.h>
  25 #include <sys/sdt.h>
  26 
  27 #include <krrp_params.h>
  28 #include <sys/krrp.h>
  29 
  30 #include "krrp_server.h"
  31 
  32 static void krrp_server_enable(krrp_server_t *server);
  33 static void krrp_server_worker(void *void_server);
  34 static int krrp_server_accept(krrp_server_t *server, ksocket_t *result_ks);
  35 static int krrp_server_create_socket(krrp_server_t *server);
  36 static void krrp_server_cleanup_socket(krrp_server_t *server);
  37 
  38 void
  39 krrp_server_create(krrp_server_t **result_server,
  40     krrp_new_ks_cb_t *new_ks_cb, krrp_svr_error_cb_t *on_error_cb)
  41 {
  42         krrp_server_t *server;
  43 
  44         VERIFY(result_server != NULL && *result_server == NULL);
  45         VERIFY(new_ks_cb != NULL);
  46         VERIFY(on_error_cb != NULL);
  47 
  48         server = kmem_zalloc(sizeof (krrp_server_t), KM_SLEEP);
  49 
  50         mutex_init(&server->mtx, NULL, MUTEX_DEFAULT, NULL);
  51         cv_init(&server->cv, NULL, CV_DEFAULT, NULL);
  52 
  53         server->new_ks_cb = new_ks_cb;
  54         server->on_error_cb = on_error_cb;
  55 
  56         *result_server = server;
  57 }
  58 
  59 void
  60 krrp_server_destroy(krrp_server_t *server)
  61 {
  62         mutex_enter(&server->mtx);
  63 
  64         server->running = B_FALSE;
  65         cv_signal(&server->cv);
  66         krrp_server_cleanup_socket(server);
  67         mutex_exit(&server->mtx);
  68 
  69         if (server->t_did != 0)
  70                 thread_join(server->t_did);
  71 
  72         cv_destroy(&server->cv);
  73         mutex_destroy(&server->mtx);
  74 
  75         kmem_free(server, sizeof (krrp_server_t));
  76 }
  77 
  78 boolean_t
  79 krrp_server_is_running(krrp_server_t *server)
  80 {
  81         boolean_t result = B_TRUE;
  82 
  83         mutex_enter(&server->mtx);
  84 
  85         if (!server->running || server->state != KRRP_SRVS_ACTIVE)
  86                 result = B_FALSE;
  87 
  88         mutex_exit(&server->mtx);
  89 
  90         return (result);
  91 }
  92 
  93 int
  94 krrp_server_set_config(krrp_server_t *server, nvlist_t *params,
  95     krrp_error_t *error)
  96 {
  97         int rc = 0;
  98         const char *listening_addr = NULL;
  99         int listening_port = 0;
 100         krrp_server_state_t saved_srv_state;
 101 
 102         mutex_enter(&server->mtx);
 103 
 104         if (server->state == KRRP_SRVS_RECONFIGURE) {
 105                 krrp_error_set(error, KRRP_ERRNO_BUSY, 0);
 106                 mutex_exit(&server->mtx);
 107                 return (-1);
 108         }
 109 
 110         saved_srv_state = server->state;
 111         server->state = KRRP_SRVS_RECONFIGURE;
 112         mutex_exit(&server->mtx);
 113 
 114         (void) krrp_param_get(KRRP_PARAM_LISTENING_ADDRESS,
 115             params, (void *)&listening_addr);
 116 
 117         if (listening_addr != NULL && (listening_addr[0] == '\0' ||
 118             (strlen(listening_addr) >= sizeof (server->listening_addr)))) {
 119                 krrp_error_set(error, KRRP_ERRNO_ADDR, EINVAL);
 120                 rc = -1;
 121                 goto out;
 122         }
 123 
 124         rc = krrp_param_get(KRRP_PARAM_PORT,
 125             params, &listening_port);
 126         if (rc != 0) {
 127                 krrp_error_set(error, KRRP_ERRNO_PORT, ENOENT);
 128                 goto out;
 129         }
 130 
 131         if (listening_port < KRRP_MIN_PORT ||
 132             listening_port > KRRP_MAX_PORT) {
 133                 krrp_error_set(error, KRRP_ERRNO_PORT, EINVAL);
 134                 rc = -1;
 135                 goto out;
 136         }
 137 
 138         mutex_enter(&server->mtx);
 139 
 140         (void) memset(server->listening_addr, 0,
 141             sizeof (server->listening_addr));
 142         if (listening_addr != NULL)
 143                 (void) strncpy(server->listening_addr, listening_addr,
 144                     sizeof (server->listening_addr));
 145 
 146         server->listening_port = listening_port;
 147 
 148         if (!server->running) {
 149                 /* The server is not started yet, so need to enable it */
 150                 krrp_server_enable(server);
 151         } else {
 152                 /*
 153                  * The server is started, so need to close the current socket,
 154                  * to recreate it with new parameters
 155                  */
 156                 krrp_server_cleanup_socket(server);
 157         }
 158 
 159         /*
 160          * To exclude double-error in userspace
 161          */
 162         server->without_event = B_TRUE;
 163 
 164         /*
 165          * The worker thread may waits on cv right after start and
 166          * if an error occured so need to wake up it.
 167          */
 168         cv_signal(&server->cv);
 169 
 170         /*
 171          * We have woken up the worker thread so wait the result of
 172          * socket-create operation
 173          */
 174         while (server->state == KRRP_SRVS_RECONFIGURE)
 175                 cv_wait(&server->cv, &server->mtx);
 176 
 177         if (server->state != KRRP_SRVS_ACTIVE) {
 178                 krrp_error_set(error, server->error.krrp_errno,
 179                     server->error.unix_errno);
 180                 rc = -1;
 181         }
 182 
 183         server->without_event = B_FALSE;
 184         mutex_exit(&server->mtx);
 185 
 186         return (rc);
 187 
 188 out:
 189         mutex_enter(&server->mtx);
 190         server->state = saved_srv_state;
 191         mutex_exit(&server->mtx);
 192         return (-1);
 193 }
 194 
 195 int
 196 krrp_server_get_config(krrp_server_t *server, nvlist_t *result,
 197     krrp_error_t *error)
 198 {
 199         int rc = -1;
 200 
 201         VERIFY(result != NULL);
 202 
 203         mutex_enter(&server->mtx);
 204 
 205         if (!server->running) {
 206                 krrp_error_set(error, KRRP_ERRNO_INVAL, 0);
 207                 goto out;
 208         }
 209 
 210         (void) krrp_param_put(KRRP_PARAM_PORT, result,
 211             &server->listening_port);
 212 
 213         if (server->listening_addr[0] != '\0')
 214                 (void) krrp_param_put(KRRP_PARAM_LISTENING_ADDRESS,
 215                     result, server->listening_addr);
 216 
 217         rc = 0;
 218 
 219 out:
 220         mutex_exit(&server->mtx);
 221 
 222         return (rc);
 223 }
 224 
 225 static void
 226 krrp_server_enable(krrp_server_t *server)
 227 {
 228         VERIFY(MUTEX_HELD(&server->mtx));
 229 
 230         /* thread_create never fails */
 231         (void) thread_create(NULL, 0, &krrp_server_worker,
 232             server, 0, &p0, TS_RUN, minclsyspri);
 233 
 234         while (server->t_did == 0)
 235                 cv_wait(&server->cv, &server->mtx);
 236 }
 237 
 238 static void
 239 krrp_server_worker(void *void_server)
 240 {
 241         krrp_server_t *server = void_server;
 242         ksocket_t ks = NULL;
 243 
 244         mutex_enter(&server->mtx);
 245         server->running = B_TRUE;
 246         server->t_did = curthread->t_did;
 247         cv_signal(&server->cv);
 248         cv_wait(&server->cv, &server->mtx);
 249 
 250         while (server->running) {
 251                 if (krrp_server_create_socket(server) != 0) {
 252                         if (!server->without_event)
 253                                 server->on_error_cb(&server->error);
 254 
 255                         server->state = KRRP_SRVS_IN_ERROR;
 256                         cv_signal(&server->cv);
 257                         cv_wait(&server->cv, &server->mtx);
 258                         continue;
 259                 }
 260 
 261                 server->state = KRRP_SRVS_ACTIVE;
 262                 cv_signal(&server->cv);
 263                 mutex_exit(&server->mtx);
 264 
 265                 /*
 266                  * To be able to unblock ksocket_accept from another
 267                  * thread by closing the ksocket
 268                  */
 269                 ksocket_hold(server->listening_ks);
 270 
 271                 while (krrp_server_accept(server, &ks) == 0)
 272                         server->new_ks_cb(ks);
 273 
 274                 ksocket_rele(server->listening_ks);
 275 
 276                 mutex_enter(&server->mtx);
 277         }
 278 
 279         mutex_exit(&server->mtx);
 280 }
 281 
 282 static int
 283 krrp_server_accept(krrp_server_t *server, ksocket_t *result_ks)
 284 {
 285         int rc = 0;
 286 
 287 repeat:
 288         rc = ksocket_accept(server->listening_ks, NULL, NULL,
 289             result_ks, CRED());
 290         if (rc == ECONNABORTED || rc == EINTR)
 291                 goto repeat;
 292 
 293         /*
 294          * ENOTSOCK means someone call ksocket_close()
 295          * for the listening socket
 296          */
 297         if (rc != 0 && rc != ENOTSOCK) {
 298                 cmn_err(CE_WARN, "Failed to accept new socket "
 299                     "[errno: %d]", rc);
 300         }
 301 
 302         return (rc);
 303 }
 304 
 305 static int
 306 krrp_server_create_socket(krrp_server_t *server)
 307 {
 308         int rc;
 309         uint32_t on = 1;
 310         struct sockaddr_in servaddr;
 311 
 312         krrp_error_init(&server->error);
 313 
 314         krrp_server_cleanup_socket(server);
 315 
 316         rc = ksocket_socket(&server->listening_ks, AF_INET, SOCK_STREAM,
 317             0, KSOCKET_SLEEP, CRED());
 318         if (rc != 0) {
 319                 krrp_error_set(&server->error, KRRP_ERRNO_CREATEFAIL, rc);
 320                 goto out;
 321         }
 322 
 323         (void) ksocket_setsockopt(server->listening_ks, SOL_SOCKET,
 324             SO_REUSEADDR, &on, sizeof (on), CRED());
 325 
 326         servaddr.sin_family = AF_INET;
 327         servaddr.sin_port = htons(server->listening_port);
 328 
 329         if (server->listening_addr[0] != '\0') {
 330                 if (inet_pton(AF_INET, (char *)server->listening_addr,
 331                     &servaddr.sin_addr) != 1) {
 332                         krrp_error_set(&server->error, KRRP_ERRNO_ADDR, EINVAL);
 333                         rc = -1;
 334                         goto fini;
 335                 }
 336         } else
 337                 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 338 
 339         rc = ksocket_bind(server->listening_ks, (struct sockaddr *)&servaddr,
 340             sizeof (servaddr), CRED());
 341         if (rc != 0) {
 342                 krrp_error_set(&server->error, KRRP_ERRNO_BINDFAIL, rc);
 343                 goto fini;
 344         }
 345 
 346         rc = ksocket_listen(server->listening_ks, 5, CRED());
 347         if (rc < 0)
 348                 krrp_error_set(&server->error, KRRP_ERRNO_LISTENFAIL, rc);
 349 
 350 fini:
 351         if (rc != 0)
 352                 krrp_server_cleanup_socket(server);
 353 
 354 out:
 355         return (rc);
 356 }
 357 
 358 static void
 359 krrp_server_cleanup_socket(krrp_server_t *server)
 360 {
 361         if (server->listening_ks != NULL) {
 362                 (void) ksocket_close(server->listening_ks, CRED());
 363                 server->listening_ks = NULL;
 364         }
 365 }