1/* $FreeBSD$ */
2/* $NetBSD: citrus_mapper.c,v 1.7 2008/07/25 14:05:25 christos Exp $ */
3
4/*-
5 * Copyright (c)2003 Citrus Project,
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
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
58static pthread_rwlock_t		cm_lock = PTHREAD_RWLOCK_INITIALIZER;
59
60struct _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
70int
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;
108quit:
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
126static int
127lookup_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
173quit:
174	_unmap_file(&r);
175	return (ret);
176}
177
178/*
179 * mapper_close:
180 *	simply close a mapper. (without handling hash)
181 */
182static void
183mapper_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 */
201static int
202mapper_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		goto err;
249
250	/* allocate traits structure */
251	cm->cm_traits = malloc(sizeof(*cm->cm_traits));
252	if (cm->cm_traits == NULL) {
253		ret = errno;
254		goto err;
255	}
256	/* initialize the mapper */
257	ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir,
258	    (const void *)variable, strlen(variable) + 1,
259	    cm->cm_traits, sizeof(*cm->cm_traits));
260	if (ret)
261		goto err;
262
263	*rcm = cm;
264
265	return (0);
266
267err:
268	mapper_close(cm);
269	return (ret);
270}
271
272/*
273 * _citrus_mapper_open_direct:
274 *	open a mapper.
275 */
276int
277_citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma,
278    struct _citrus_mapper * __restrict * __restrict rcm,
279    const char * __restrict module, const char * __restrict variable)
280{
281
282	return (mapper_open(ma, rcm, module, variable));
283}
284
285/*
286 * hash_func
287 */
288static __inline int
289hash_func(const char *key)
290{
291
292	return (_string_hash_func(key, CM_HASH_SIZE));
293}
294
295/*
296 * match_func
297 */
298static __inline int
299match_func(struct _citrus_mapper *cm, const char *key)
300{
301
302	return (strcmp(cm->cm_key, key));
303}
304
305/*
306 * _citrus_mapper_open:
307 *	open a mapper with looking up "mapper.dir".
308 */
309int
310_citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
311    struct _citrus_mapper * __restrict * __restrict rcm,
312    const char * __restrict mapname)
313{
314	struct _citrus_mapper *cm;
315	char linebuf[PATH_MAX];
316	const char *module, *variable;
317	int hashval, ret;
318
319	variable = NULL;
320
321	WLOCK(&cm_lock);
322
323	/* search in the cache */
324	hashval = hash_func(mapname);
325	_CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname,
326	    hashval);
327	if (cm) {
328		/* found */
329		cm->cm_refcount++;
330		*rcm = cm;
331		ret = 0;
332		goto quit;
333	}
334
335	/* search mapper entry */
336	ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf,
337	    (size_t)PATH_MAX, &module, &variable);
338	if (ret)
339		goto quit;
340
341	/* open mapper */
342	UNLOCK(&cm_lock);
343	ret = mapper_open(ma, &cm, module, variable);
344	WLOCK(&cm_lock);
345	if (ret)
346		goto quit;
347	cm->cm_key = strdup(mapname);
348	if (cm->cm_key == NULL) {
349		ret = errno;
350		_mapper_close(cm);
351		goto quit;
352	}
353
354	/* insert to the cache */
355	cm->cm_refcount = 1;
356	_CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval);
357
358	*rcm = cm;
359	ret = 0;
360quit:
361	UNLOCK(&cm_lock);
362
363	return (ret);
364}
365
366/*
367 * _citrus_mapper_close:
368 *	close the specified mapper.
369 */
370void
371_citrus_mapper_close(struct _citrus_mapper *cm)
372{
373
374	if (cm) {
375		WLOCK(&cm_lock);
376		if (cm->cm_refcount == REFCOUNT_PERSISTENT)
377			goto quit;
378		if (cm->cm_refcount > 0) {
379			if (--cm->cm_refcount > 0)
380				goto quit;
381			_CITRUS_HASH_REMOVE(cm, cm_entry);
382			free(cm->cm_key);
383		}
384		UNLOCK(&cm_lock);
385		mapper_close(cm);
386		return;
387quit:
388		UNLOCK(&cm_lock);
389	}
390}
391
392/*
393 * _citrus_mapper_set_persistent:
394 *	set persistent count.
395 */
396void
397_citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
398{
399
400	WLOCK(&cm_lock);
401	cm->cm_refcount = REFCOUNT_PERSISTENT;
402	UNLOCK(&cm_lock);
403}
404