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