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 <umem.h>
  17 #include <libintl.h>
  18 #include <unistd.h>
  19 #include <string.h>
  20 #include <sys/debug.h>
  21 #include <stdarg.h>
  22 
  23 #include <sys/sysevent/krrp.h>
  24 #include <sys/krrp.h>
  25 #include "libkrrp.h"
  26 #include "libkrrp_impl.h"
  27 #include "libkrrp_error.h"
  28 
  29 /* Make lint happy */
  30 #pragma error_messages(off, E_UNDEFINED_SYMBOL, E_YACC_ERROR, \
  31     E_FUNC_VAR_UNUSED, E_BLOCK_DECL_UNUSED, E_RET_INT_IMPLICITLY)
  32 
  33 static struct {
  34         char *subclass;
  35         libkrrp_ev_type_t ev_type;
  36 } krrp_escs[] = {
  37 #define LIBKRRP_ESC_EV_TYPE_EXPAND(enum_name) \
  38         {ESC_KRRP_##enum_name, LIBKRRP_EV_TYPE_##enum_name},
  39         LIBKRRP_EV_TYPE_MAP(LIBKRRP_ESC_EV_TYPE_EXPAND)
  40 #undef  LIBKRRP_ESC_EV_TYPE_EXPAND
  41 };
  42 
  43 #define LIBKRRP_EV_CB_RETRY_COUNT 5
  44 #define LIBKRRP_EV_CB_RETRY_PAUSE 300000
  45 
  46 static size_t krrp_escs_sz = sizeof (krrp_escs) / sizeof (krrp_escs[0]);
  47 
  48 static libkrrp_ev_type_t
  49 subclass_to_libkrrp_ev_type(const char *subclass)
  50 {
  51         int i;
  52 
  53         ASSERT(subclass != NULL);
  54 
  55         for (i = 0; i < krrp_escs_sz; i++) {
  56                 if (strcmp(subclass, krrp_escs[i].subclass) == 0)
  57                         return (krrp_escs[i].ev_type);
  58         }
  59 
  60         return (LIBKRRP_EV_TYPE_UNKNOWN);
  61 }
  62 
  63 static int
  64 libkrrp_sess_id_parse(libkrrp_event_t *ev, char *sess_id_str,
  65     uuid_t sess_id)
  66 {
  67         ASSERT(sess_id != NULL);
  68 
  69         if ((sess_id_str == NULL) ||
  70             (uuid_parse(sess_id_str, sess_id) != 0)) {
  71                 libkrrp_error_set(&ev->libkrrp_error, LIBKRRP_ERRNO_SESSID,
  72                     EINVAL, 0);
  73 
  74                 return (-1);
  75         }
  76 
  77         return (0);
  78 }
  79 
  80 static void
  81 libkrrp_ev_unpack_sess_send_done(libkrrp_event_t *ev, nvlist_t *attr)
  82 {
  83         char *sess_id_str = NULL;
  84 
  85         ASSERT(ev != NULL);
  86         ASSERT(attr != NULL);
  87 
  88         if (krrp_param_get(KRRP_PARAM_SESS_ID, attr, &sess_id_str) != 0) {
  89                 libkrrp_error_set(&ev->libkrrp_error, LIBKRRP_ERRNO_SESSID,
  90                     ENOENT, 0);
  91                 return;
  92         }
  93 
  94         (void) libkrrp_sess_id_parse(ev, sess_id_str,
  95             ev->ev_data.sess_send_done.sess_id);
  96 }
  97 
  98 static void
  99 libkrrp_ev_unpack_sess_error(libkrrp_event_t *ev, nvlist_t *attr)
 100 {
 101         char *sess_id_str = NULL;
 102 
 103         ASSERT(ev != NULL);
 104         ASSERT(attr != NULL);
 105 
 106         if (krrp_param_get(KRRP_PARAM_SESS_ID, attr, &sess_id_str) != 0) {
 107                 libkrrp_error_set(&ev->libkrrp_error, LIBKRRP_ERRNO_SESSID,
 108                     ENOENT, 0);
 109                 return;
 110         }
 111 
 112         if (libkrrp_sess_id_parse(ev, sess_id_str,
 113             ev->ev_data.sess_error.sess_id) != 0)
 114                 return;
 115 
 116         if (libkrrp_error_from_nvl(attr,
 117             &ev->ev_data.sess_error.libkrrp_error) != 0) {
 118                 libkrrp_error_set(&ev->libkrrp_error, LIBKRRP_ERRNO_SESSERR,
 119                     EINVAL, 0);
 120         }
 121 }
 122 
 123 static void
 124 libkrrp_ev_unpack_server_error(libkrrp_event_t *ev, nvlist_t *attr)
 125 {
 126         ASSERT(ev != NULL);
 127         ASSERT(attr != NULL);
 128 
 129         if (libkrrp_error_from_nvl(attr, &ev->ev_data.server_error) != 0) {
 130                 libkrrp_error_set(&ev->libkrrp_error, LIBKRRP_ERRNO_SESSERR,
 131                     EINVAL, 0);
 132         }
 133 }
 134 
 135 static int
 136 libkrrp_evc_callback(sysevent_t *ev, void *cookie)
 137 {
 138         libkrrp_evc_handle_t *hdl = cookie;
 139         libkrrp_event_t libkrrp_ev;
 140         nvlist_t *attr = NULL;
 141         int rc;
 142 
 143         ASSERT(ev != NULL);
 144         ASSERT(hdl != NULL);
 145 
 146         libkrrp_error_init(&libkrrp_ev.libkrrp_error);
 147         libkrrp_ev.ev_type = subclass_to_libkrrp_ev_type(
 148             sysevent_get_subclass_name(ev));
 149 
 150         rc = sysevent_get_attr_list(ev, &attr);
 151         if (rc == ENOMEM) {
 152                 if (hdl->ev_failure_count++ < LIBKRRP_EV_CB_RETRY_COUNT) {
 153                         (void) usleep(LIBKRRP_EV_CB_RETRY_PAUSE);
 154                         return (EAGAIN);
 155                 }
 156         }
 157 
 158         hdl->ev_failure_count = 0;
 159 
 160         if (rc != 0) {
 161                 libkrrp_error_set(&libkrrp_ev.libkrrp_error,
 162                     LIBKRRP_ERRNO_EVREADFAIL, rc, 0);
 163                 goto callback;
 164         }
 165 
 166         switch (libkrrp_ev.ev_type) {
 167         case LIBKRRP_EV_TYPE_SESS_SEND_DONE:
 168                 libkrrp_ev_unpack_sess_send_done(&libkrrp_ev, attr);
 169                 break;
 170         case LIBKRRP_EV_TYPE_SESS_ERROR:
 171                 libkrrp_ev_unpack_sess_error(&libkrrp_ev, attr);
 172                 break;
 173         case LIBKRRP_EV_TYPE_SERVER_ERROR:
 174                 libkrrp_ev_unpack_server_error(&libkrrp_ev, attr);
 175                 break;
 176         default:
 177                 ASSERT(0);
 178         }
 179 
 180 callback:
 181         fnvlist_free(attr);
 182         rc = hdl->callback(&libkrrp_ev, hdl->cookie);
 183         return (rc);
 184 }
 185 
 186 void
 187 libkrrp_evc_unsubscribe(libkrrp_evc_handle_t *hdl)
 188 {
 189         VERIFY(hdl != NULL);
 190 
 191         if (hdl->evchan)
 192                 (void) sysevent_evc_unbind(hdl->evchan);
 193 
 194         umem_free(hdl, sizeof (libkrrp_evc_handle_t));
 195 }
 196 
 197 int
 198 libkrrp_evc_subscribe(libkrrp_evc_handle_t **res_hdl,
 199     int(*callback)(libkrrp_event_t *, void *), void *cookie)
 200 {
 201         int rc;
 202         char sid[13];
 203         libkrrp_evc_handle_t *hdl;
 204 
 205         hdl = umem_zalloc(sizeof (libkrrp_evc_handle_t), UMEM_DEFAULT);
 206         *res_hdl = hdl;
 207 
 208         if (hdl == NULL)
 209                 return (-1);
 210 
 211         rc = sysevent_evc_bind(KRRP_EVENT_CHANNEL, &hdl->evchan, EVCH_CREAT);
 212 
 213         if (rc != 0) {
 214                 libkrrp_error_set(&hdl->libkrrp_error,
 215                     LIBKRRP_ERRNO_EVBINDFAIL, rc, 0);
 216                 libkrrp_evc_unsubscribe(hdl);
 217                 return (-1);
 218         }
 219 
 220         hdl->callback = callback;
 221         hdl->cookie = cookie;
 222 
 223         (void) sprintf(sid, "libkrrp%d", (int)getpid());
 224         rc = sysevent_evc_subscribe(hdl->evchan, sid, EC_KRRP,
 225             libkrrp_evc_callback, hdl, 0);
 226 
 227         if (rc != 0) {
 228                 libkrrp_error_set(&hdl->libkrrp_error,
 229                     LIBKRRP_ERRNO_EVSUBSRIBEFAIL, rc, 0);
 230                 libkrrp_evc_unsubscribe(hdl);
 231                 return (-1);
 232         }
 233 
 234         return (0);
 235 }
 236 
 237 libkrrp_error_t *
 238 libkrrp_evc_error(libkrrp_evc_handle_t *hdl)
 239 {
 240         VERIFY(hdl != NULL);
 241         return (&hdl->libkrrp_error);
 242 }
 243 
 244 const char *
 245 libkrrp_evc_error_description(libkrrp_evc_handle_t *hdl)
 246 {
 247         /* LINTED: E_FUNC_SET_NOT_USED */
 248         libkrrp_errno_t libkrrp_errno;
 249         /* LINTED: E_FUNC_SET_NOT_USED */
 250         int unix_errno;
 251         /* LINTED: E_FUNC_SET_NOT_USED */
 252         int flags = 0;
 253         char *descr;
 254 
 255         VERIFY(hdl != NULL);
 256 
 257         descr = hdl->libkrrp_error_descr;
 258         descr[0] = '\0';
 259         libkrrp_errno = hdl->libkrrp_error.libkrrp_errno;
 260         unix_errno = hdl->libkrrp_error.unix_errno;
 261 
 262         SET_ERROR_DESCR(LIBKRRP_ERRDESCR_MAP);
 263 
 264         if (descr[0] == '\0') {
 265                 (void) snprintf(descr, sizeof (libkrrp_error_descr_t) - 1,
 266                     dgettext(TEXT_DOMAIN, LIBKRRP_EMSG_UNKNOWN));
 267         }
 268 
 269         return (descr);
 270 }
 271 
 272 libkrrp_ev_type_t
 273 libkrrp_ev_type(libkrrp_event_t *ev)
 274 {
 275         VERIFY(ev != NULL);
 276         return (ev->ev_type);
 277 }
 278 
 279 libkrrp_ev_data_t *
 280 libkrrp_ev_data(libkrrp_event_t *ev)
 281 {
 282         VERIFY(ev != NULL);
 283         return (&ev->ev_data);
 284 }
 285 
 286 libkrrp_error_t *
 287 libkrrp_ev_error(libkrrp_event_t *ev)
 288 {
 289         VERIFY(ev != NULL);
 290         return (&ev->libkrrp_error);
 291 }
 292 
 293 const char *
 294 libkrrp_ev_error_description(libkrrp_event_t *ev)
 295 {
 296         /* LINTED: E_FUNC_SET_NOT_USED */
 297         libkrrp_errno_t libkrrp_errno;
 298         /* LINTED: E_FUNC_SET_NOT_USED */
 299         int unix_errno;
 300         /* LINTED: E_FUNC_SET_NOT_USED */
 301         int flags = 0;
 302         char *descr;
 303 
 304         VERIFY(ev != NULL);
 305 
 306         descr = ev->libkrrp_error_descr;
 307         descr[0] = '\0';
 308         libkrrp_errno = ev->libkrrp_error.libkrrp_errno;
 309         unix_errno = ev->libkrrp_error.unix_errno;
 310 
 311         SET_ERROR_DESCR(LIBKRRP_ERRDESCR_MAP);
 312 
 313         if (descr[0] == '\0') {
 314                 (void) snprintf(descr, sizeof (libkrrp_error_descr_t) - 1,
 315                     dgettext(TEXT_DOMAIN, LIBKRRP_EMSG_UNKNOWN));
 316         }
 317 
 318         return (descr);
 319 }
 320 
 321 void
 322 libkrrp_ev_data_error_description(libkrrp_ev_type_t ev_type,
 323     libkrrp_error_t *error, libkrrp_error_descr_t descr)
 324 {
 325         switch (ev_type) {
 326         case LIBKRRP_EV_TYPE_SERVER_ERROR:
 327                 libkrrp_common_error_description(LIBKRRP_SRV_ERROR, error,
 328                     descr);
 329                 break;
 330         case LIBKRRP_EV_TYPE_SESS_ERROR:
 331                 libkrrp_common_error_description(LIBKRRP_SESS_ERROR, error,
 332                     descr);
 333                 break;
 334         default:
 335                 break;
 336         }
 337 }
 338 
 339 libkrrp_event_t *
 340 libkrrp_ev_dup(libkrrp_event_t *ev)
 341 {
 342         libkrrp_event_t *copy;
 343 
 344         VERIFY(ev != NULL);
 345 
 346         copy = umem_zalloc(sizeof (libkrrp_event_t), UMEM_DEFAULT);
 347 
 348         if (copy == NULL)
 349                 return (NULL);
 350 
 351         (void) memcpy(copy, ev, sizeof (libkrrp_event_t));
 352         return (copy);
 353 }
 354 
 355 void
 356 libkrrp_ev_free(libkrrp_event_t *ev)
 357 {
 358         VERIFY(ev != NULL);
 359         umem_free(ev, sizeof (libkrrp_event_t));
 360 }