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
fmev_tsd_destructor(void * data)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
fmev_api_init(struct fmev_hdl_cmn * hc)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
fmev_api_enter(struct fmev_hdl_cmn * hc,uint32_t ver_intro)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
fmev_api_freetsd(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
fmev_seterr(fmev_err_t error)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 *
__fmev_errno(void)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 *
dflt_alloc(size_t sz)196 dflt_alloc(size_t sz)
197 {
198 return (umem_alloc(sz, UMEM_DEFAULT));
199 }
200
201 void *
dflt_zalloc(size_t sz)202 dflt_zalloc(size_t sz)
203 {
204 return (umem_zalloc(sz, UMEM_DEFAULT));
205 }
206
207 void
dflt_free(void * buf,size_t sz)208 dflt_free(void *buf, size_t sz)
209 {
210 umem_free(buf, sz);
211 }
212