1/* $FreeBSD$ */
2/*	$NetBSD: citrus_mapper.c,v 1.10 2012/06/08 07:49:42 martin 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		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
269err:
270	mapper_close(cm);
271	return (ret);
272}
273
274/*
275 * _citrus_mapper_open_direct:
276 *	open a mapper.
277 */
278int
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 */
290static __inline int
291hash_func(const char *key)
292{
293
294	return (_string_hash_func(key, CM_HASH_SIZE));
295}
296
297/*
298 * match_func
299 */
300static __inline int
301match_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 */
311int
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;
362quit:
363	UNLOCK(&cm_lock);
364
365	return (ret);
366}
367
368/*
369 * _citrus_mapper_close:
370 *	close the specified mapper.
371 */
372void
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;
389quit:
390		UNLOCK(&cm_lock);
391	}
392}
393
394/*
395 * _citrus_mapper_set_persistent:
396 *	set persistent count.
397 */
398void
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