1 /* $NetBSD: citrus_mapper.c,v 1.10 2012/06/08 07:49:42 martin Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c)2003 Citrus Project,
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/queue.h>
34
35 #include <assert.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "citrus_namespace.h"
43 #include "citrus_types.h"
44 #include "citrus_region.h"
45 #include "citrus_lock.h"
46 #include "citrus_memstream.h"
47 #include "citrus_bcs.h"
48 #include "citrus_mmap.h"
49 #include "citrus_module.h"
50 #include "citrus_hash.h"
51 #include "citrus_mapper.h"
52
53 #define _CITRUS_MAPPER_DIR "mapper.dir"
54
55 #define CM_HASH_SIZE 101
56 #define REFCOUNT_PERSISTENT -1
57
58 static pthread_rwlock_t cm_lock = PTHREAD_RWLOCK_INITIALIZER;
59
60 struct _citrus_mapper_area {
61 _CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE) ma_cache;
62 char *ma_dir;
63 };
64
65 /*
66 * _citrus_mapper_create_area:
67 * create mapper area
68 */
69
70 int
_citrus_mapper_create_area(struct _citrus_mapper_area * __restrict * __restrict rma,const char * __restrict area)71 _citrus_mapper_create_area(
72 struct _citrus_mapper_area *__restrict *__restrict rma,
73 const char *__restrict area)
74 {
75 struct _citrus_mapper_area *ma;
76 struct stat st;
77 char path[PATH_MAX];
78 int ret;
79
80 WLOCK(&cm_lock);
81
82 if (*rma != NULL) {
83 ret = 0;
84 goto quit;
85 }
86
87 snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR);
88
89 ret = stat(path, &st);
90 if (ret)
91 goto quit;
92
93 ma = malloc(sizeof(*ma));
94 if (ma == NULL) {
95 ret = errno;
96 goto quit;
97 }
98 ma->ma_dir = strdup(area);
99 if (ma->ma_dir == NULL) {
100 ret = errno;
101 free(ma);
102 goto quit;
103 }
104 _CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE);
105
106 *rma = ma;
107 ret = 0;
108 quit:
109 UNLOCK(&cm_lock);
110
111 return (ret);
112 }
113
114
115 /*
116 * lookup_mapper_entry:
117 * lookup mapper.dir entry in the specified directory.
118 *
119 * line format of iconv.dir file:
120 * mapper module arg
121 * mapper : mapper name.
122 * module : mapper module name.
123 * arg : argument for the module (generally, description file name)
124 */
125
126 static int
lookup_mapper_entry(const char * dir,const char * mapname,void * linebuf,size_t linebufsize,const char ** module,const char ** variable)127 lookup_mapper_entry(const char *dir, const char *mapname, void *linebuf,
128 size_t linebufsize, const char **module, const char **variable)
129 {
130 struct _region r;
131 struct _memstream ms;
132 const char *cp, *cq;
133 char *p;
134 char path[PATH_MAX];
135 size_t len;
136 int ret;
137
138 /* create mapper.dir path */
139 snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR);
140
141 /* open read stream */
142 ret = _map_file(&r, path);
143 if (ret)
144 return (ret);
145
146 _memstream_bind(&ms, &r);
147
148 /* search the line matching to the map name */
149 cp = _memstream_matchline(&ms, mapname, &len, 0);
150 if (!cp) {
151 ret = ENOENT;
152 goto quit;
153 }
154 if (!len || len > linebufsize - 1) {
155 ret = EINVAL;
156 goto quit;
157 }
158
159 p = linebuf;
160 /* get module name */
161 *module = p;
162 cq = _bcs_skip_nonws_len(cp, &len);
163 strlcpy(p, cp, (size_t)(cq - cp + 1));
164 p += cq - cp + 1;
165
166 /* get variable */
167 *variable = p;
168 cp = _bcs_skip_ws_len(cq, &len);
169 strlcpy(p, cp, len + 1);
170
171 ret = 0;
172
173 quit:
174 _unmap_file(&r);
175 return (ret);
176 }
177
178 /*
179 * mapper_close:
180 * simply close a mapper. (without handling hash)
181 */
182 static void
mapper_close(struct _citrus_mapper * cm)183 mapper_close(struct _citrus_mapper *cm)
184 {
185 if (cm->cm_module) {
186 if (cm->cm_ops) {
187 if (cm->cm_closure)
188 (*cm->cm_ops->mo_uninit)(cm);
189 free(cm->cm_ops);
190 }
191 _citrus_unload_module(cm->cm_module);
192 }
193 free(cm->cm_traits);
194 free(cm);
195 }
196
197 /*
198 * mapper_open:
199 * simply open a mapper. (without handling hash)
200 */
201 static int
mapper_open(struct _citrus_mapper_area * __restrict ma,struct _citrus_mapper * __restrict * __restrict rcm,const char * __restrict module,const char * __restrict variable)202 mapper_open(struct _citrus_mapper_area *__restrict ma,
203 struct _citrus_mapper * __restrict * __restrict rcm,
204 const char * __restrict module,
205 const char * __restrict variable)
206 {
207 struct _citrus_mapper *cm;
208 _citrus_mapper_getops_t getops;
209 int ret;
210
211 /* initialize mapper handle */
212 cm = malloc(sizeof(*cm));
213 if (!cm)
214 return (errno);
215
216 cm->cm_module = NULL;
217 cm->cm_ops = NULL;
218 cm->cm_closure = NULL;
219 cm->cm_traits = NULL;
220 cm->cm_refcount = 0;
221 cm->cm_key = NULL;
222
223 /* load module */
224 ret = _citrus_load_module(&cm->cm_module, module);
225 if (ret)
226 goto err;
227
228 /* get operators */
229 getops = (_citrus_mapper_getops_t)
230 _citrus_find_getops(cm->cm_module, module, "mapper");
231 if (!getops) {
232 ret = EOPNOTSUPP;
233 goto err;
234 }
235 cm->cm_ops = malloc(sizeof(*cm->cm_ops));
236 if (!cm->cm_ops) {
237 ret = errno;
238 goto err;
239 }
240 ret = (*getops)(cm->cm_ops);
241 if (ret)
242 goto err;
243
244 if (!cm->cm_ops->mo_init ||
245 !cm->cm_ops->mo_uninit ||
246 !cm->cm_ops->mo_convert ||
247 !cm->cm_ops->mo_init_state) {
248 ret = EINVAL;
249 goto err;
250 }
251
252 /* allocate traits structure */
253 cm->cm_traits = malloc(sizeof(*cm->cm_traits));
254 if (cm->cm_traits == NULL) {
255 ret = errno;
256 goto err;
257 }
258 /* initialize the mapper */
259 ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir,
260 (const void *)variable, strlen(variable) + 1,
261 cm->cm_traits, sizeof(*cm->cm_traits));
262 if (ret)
263 goto err;
264
265 *rcm = cm;
266
267 return (0);
268
269 err:
270 mapper_close(cm);
271 return (ret);
272 }
273
274 /*
275 * _citrus_mapper_open_direct:
276 * open a mapper.
277 */
278 int
_citrus_mapper_open_direct(struct _citrus_mapper_area * __restrict ma,struct _citrus_mapper * __restrict * __restrict rcm,const char * __restrict module,const char * __restrict variable)279 _citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma,
280 struct _citrus_mapper * __restrict * __restrict rcm,
281 const char * __restrict module, const char * __restrict variable)
282 {
283
284 return (mapper_open(ma, rcm, module, variable));
285 }
286
287 /*
288 * hash_func
289 */
290 static __inline int
hash_func(const char * key)291 hash_func(const char *key)
292 {
293
294 return (_string_hash_func(key, CM_HASH_SIZE));
295 }
296
297 /*
298 * match_func
299 */
300 static __inline int
match_func(struct _citrus_mapper * cm,const char * key)301 match_func(struct _citrus_mapper *cm, const char *key)
302 {
303
304 return (strcmp(cm->cm_key, key));
305 }
306
307 /*
308 * _citrus_mapper_open:
309 * open a mapper with looking up "mapper.dir".
310 */
311 int
_citrus_mapper_open(struct _citrus_mapper_area * __restrict ma,struct _citrus_mapper * __restrict * __restrict rcm,const char * __restrict mapname)312 _citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
313 struct _citrus_mapper * __restrict * __restrict rcm,
314 const char * __restrict mapname)
315 {
316 struct _citrus_mapper *cm;
317 char linebuf[PATH_MAX];
318 const char *module, *variable;
319 int hashval, ret;
320
321 variable = NULL;
322
323 WLOCK(&cm_lock);
324
325 /* search in the cache */
326 hashval = hash_func(mapname);
327 _CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname,
328 hashval);
329 if (cm) {
330 /* found */
331 cm->cm_refcount++;
332 *rcm = cm;
333 ret = 0;
334 goto quit;
335 }
336
337 /* search mapper entry */
338 ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf,
339 (size_t)PATH_MAX, &module, &variable);
340 if (ret)
341 goto quit;
342
343 /* open mapper */
344 UNLOCK(&cm_lock);
345 ret = mapper_open(ma, &cm, module, variable);
346 WLOCK(&cm_lock);
347 if (ret)
348 goto quit;
349 cm->cm_key = strdup(mapname);
350 if (cm->cm_key == NULL) {
351 ret = errno;
352 _mapper_close(cm);
353 goto quit;
354 }
355
356 /* insert to the cache */
357 cm->cm_refcount = 1;
358 _CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval);
359
360 *rcm = cm;
361 ret = 0;
362 quit:
363 UNLOCK(&cm_lock);
364
365 return (ret);
366 }
367
368 /*
369 * _citrus_mapper_close:
370 * close the specified mapper.
371 */
372 void
_citrus_mapper_close(struct _citrus_mapper * cm)373 _citrus_mapper_close(struct _citrus_mapper *cm)
374 {
375
376 if (cm) {
377 WLOCK(&cm_lock);
378 if (cm->cm_refcount == REFCOUNT_PERSISTENT)
379 goto quit;
380 if (cm->cm_refcount > 0) {
381 if (--cm->cm_refcount > 0)
382 goto quit;
383 _CITRUS_HASH_REMOVE(cm, cm_entry);
384 free(cm->cm_key);
385 }
386 UNLOCK(&cm_lock);
387 mapper_close(cm);
388 return;
389 quit:
390 UNLOCK(&cm_lock);
391 }
392 }
393
394 /*
395 * _citrus_mapper_set_persistent:
396 * set persistent count.
397 */
398 void
_citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)399 _citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
400 {
401
402 WLOCK(&cm_lock);
403 cm->cm_refcount = REFCOUNT_PERSISTENT;
404 UNLOCK(&cm_lock);
405 }
406