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 /* 23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Subscription event access interfaces. 28 */ 29 30 #include <sys/types.h> 31 #include <pthread.h> 32 #include <umem.h> 33 #include <fm/libfmevent.h> 34 35 #include "fmev_impl.h" 36 37 static pthread_key_t fmev_tsdkey = PTHREAD_ONCE_KEY_NP; 38 static int key_inited; 39 40 /* 41 * Thread and handle specific data. 42 */ 43 struct fmev_tsd { 44 fmev_err_t ts_lasterr; 45 }; 46 47 static void 48 fmev_tsd_destructor(void *data) 49 { 50 umem_free(data, sizeof (struct fmev_tsd)); 51 } 52 53 /* 54 * Called only from fmev_shdl_init. Check we are opening a valid version 55 * of the ABI. 56 */ 57 int 58 fmev_api_init(struct fmev_hdl_cmn *hc) 59 { 60 uint32_t v = hc->hc_api_vers; 61 int rc; 62 63 if (!fmev_api_enter((struct fmev_hdl_cmn *)fmev_api_init, 0)) 64 return (0); 65 66 switch (v) { 67 case LIBFMEVENT_VERSION_1: 68 case LIBFMEVENT_VERSION_2: 69 rc = 1; 70 break; 71 72 default: 73 if (key_inited) 74 (void) fmev_seterr(FMEVERR_VERSION_MISMATCH); 75 rc = 0; 76 break; 77 } 78 79 return (rc); 80 } 81 82 /* 83 * On entry to other libfmevent API members we call fmev_api_enter. 84 * Some thread-specific data is used to keep a per-thread error value. 85 * The version opened must be no greater than the latest version but can 86 * be older. The ver_intro is the api version at which the interface 87 * was added - the caller must have opened at least this version. 88 */ 89 int 90 fmev_api_enter(struct fmev_hdl_cmn *hc, uint32_t ver_intro) 91 { 92 uint32_t v; 93 struct fmev_tsd *tsd; 94 95 /* Initialize key on first visit */ 96 if (!key_inited) { 97 (void) pthread_key_create_once_np(&fmev_tsdkey, 98 fmev_tsd_destructor); 99 key_inited = 1; 100 } 101 102 /* 103 * Allocate TSD for error value for this thread. It is only 104 * freed if/when the thread exits. 105 */ 106 if ((tsd = pthread_getspecific(fmev_tsdkey)) == NULL) { 107 if ((tsd = umem_alloc(sizeof (*tsd), UMEM_DEFAULT)) == NULL || 108 pthread_setspecific(fmev_tsdkey, (const void *)tsd) != 0) { 109 if (tsd) 110 umem_free(tsd, sizeof (*tsd)); 111 return (0); /* no error set, but what can we do */ 112 } 113 } 114 115 tsd->ts_lasterr = 0; 116 117 if (hc == (struct fmev_hdl_cmn *)fmev_api_init) 118 return (1); /* special case from fmev_api_init only */ 119 120 if (hc == NULL || hc->hc_magic != _FMEV_SHMAGIC) { 121 tsd->ts_lasterr = FMEVERR_API; 122 return (0); 123 } 124 125 v = hc->hc_api_vers; /* API version opened */ 126 127 /* Enforce version adherence. */ 128 if (ver_intro > v || v > LIBFMEVENT_VERSION_LATEST || 129 ver_intro > LIBFMEVENT_VERSION_LATEST) { 130 tsd->ts_lasterr = FMEVERR_VERSION_MISMATCH; 131 return (0); 132 } 133 134 return (1); 135 } 136 137 /* 138 * Called on any fmev_shdl_fini. Free the TSD for this thread. If this 139 * thread makes other API calls for other open handles, or opens a new 140 * handle, then TSD will be allocated again in fmev_api_enter. 141 */ 142 void 143 fmev_api_freetsd(void) 144 { 145 struct fmev_tsd *tsd; 146 147 if ((tsd = pthread_getspecific(fmev_tsdkey)) != NULL) { 148 (void) pthread_setspecific(fmev_tsdkey, NULL); 149 fmev_tsd_destructor((void *)tsd); 150 } 151 } 152 153 /* 154 * To return an error condition an API member first sets the error type 155 * with a call to fmev_seterr and then returns NULL or whatever it wants. 156 * The caller can then retrieve the per-thread error type using fmev_errno 157 * or format it with fmev_strerr. 158 */ 159 fmev_err_t 160 fmev_seterr(fmev_err_t error) 161 { 162 struct fmev_tsd *tsd; 163 164 ASSERT(key_inited); 165 166 if ((tsd = pthread_getspecific(fmev_tsdkey)) != NULL) 167 tsd->ts_lasterr = error; 168 169 return (error); 170 } 171 172 /* 173 * fmev_errno is a macro defined in terms of the following function. It 174 * can be used to dereference the last error value on the current thread; 175 * it must not be used to assign to fmev_errno. 176 */ 177 178 const fmev_err_t apierr = FMEVERR_API; 179 const fmev_err_t unknownerr = FMEVERR_UNKNOWN; 180 181 const fmev_err_t * 182 __fmev_errno(void) 183 { 184 struct fmev_tsd *tsd; 185 186 if (!key_inited) 187 return (&apierr); 188 189 if ((tsd = pthread_getspecific(fmev_tsdkey)) == NULL) 190 return (&unknownerr); 191 192 return ((const fmev_err_t *)&tsd->ts_lasterr); 193 } 194 195 void * 196 dflt_alloc(size_t sz) 197 { 198 return (umem_alloc(sz, UMEM_DEFAULT)); 199 } 200 201 void * 202 dflt_zalloc(size_t sz) 203 { 204 return (umem_zalloc(sz, UMEM_DEFAULT)); 205 } 206 207 void 208 dflt_free(void *buf, size_t sz) 209 { 210 umem_free(buf, sz); 211 } 212