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