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