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
58struct _citrus_mapper_area {
59	_CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE)	 ma_cache;
60	char							*ma_dir;
61};
62
63/*
64 * _citrus_mapper_create_area:
65 *	create mapper area
66 */
67
68int
69_citrus_mapper_create_area(
70    struct _citrus_mapper_area *__restrict *__restrict rma,
71    const char *__restrict area)
72{
73	struct _citrus_mapper_area *ma;
74	struct stat st;
75	char path[PATH_MAX];
76	int ret;
77
78	WLOCK;
79
80	if (*rma != NULL) {
81		ret = 0;
82		goto quit;
83	}
84
85	snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR);
86
87	ret = stat(path, &st);
88	if (ret)
89		goto quit;
90
91	ma = malloc(sizeof(*ma));
92	if (ma == NULL) {
93		ret = errno;
94		goto quit;
95	}
96	ma->ma_dir = strdup(area);
97	if (ma->ma_dir == NULL) {
98		ret = errno;
99		free(ma->ma_dir);
100		goto quit;
101	}
102	_CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE);
103
104	*rma = ma;
105	ret = 0;
106quit:
107	UNLOCK;
108
109	return (ret);
110}
111
112
113/*
114 * lookup_mapper_entry:
115 *	lookup mapper.dir entry in the specified directory.
116 *
117 * line format of iconv.dir file:
118 *	mapper	module	arg
119 * mapper : mapper name.
120 * module : mapper module name.
121 * arg    : argument for the module (generally, description file name)
122 */
123
124static int
125lookup_mapper_entry(const char *dir, const char *mapname, void *linebuf,
126    size_t linebufsize, const char **module, const char **variable)
127{
128	struct _region r;
129	struct _memstream ms;
130	const char *cp, *cq;
131	char *p;
132	char path[PATH_MAX];
133	size_t len;
134	int ret;
135
136	/* create mapper.dir path */
137	snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR);
138
139	/* open read stream */
140	ret = _map_file(&r, path);
141	if (ret)
142		return (ret);
143
144	_memstream_bind(&ms, &r);
145
146	/* search the line matching to the map name */
147	cp = _memstream_matchline(&ms, mapname, &len, 0);
148	if (!cp) {
149		ret = ENOENT;
150		goto quit;
151	}
152	if (!len || len > linebufsize - 1) {
153		ret = EINVAL;
154		goto quit;
155	}
156
157	p = linebuf;
158	/* get module name */
159	*module = p;
160	cq = _bcs_skip_nonws_len(cp, &len);
161	strlcpy(p, cp, (size_t)(cq - cp + 1));
162	p += cq - cp + 1;
163
164	/* get variable */
165	*variable = p;
166	cp = _bcs_skip_ws_len(cq, &len);
167	strlcpy(p, cp, len + 1);
168
169	ret = 0;
170
171quit:
172	_unmap_file(&r);
173	return (ret);
174}
175
176/*
177 * mapper_close:
178 *	simply close a mapper. (without handling hash)
179 */
180static void
181mapper_close(struct _citrus_mapper *cm)
182{
183	if (cm->cm_module) {
184		if (cm->cm_ops) {
185			if (cm->cm_closure)
186				(*cm->cm_ops->mo_uninit)(cm);
187			free(cm->cm_ops);
188		}
189		_citrus_unload_module(cm->cm_module);
190	}
191	free(cm->cm_traits);
192	free(cm);
193}
194
195/*
196 * mapper_open:
197 *	simply open a mapper. (without handling hash)
198 */
199static int
200mapper_open(struct _citrus_mapper_area *__restrict ma,
201    struct _citrus_mapper * __restrict * __restrict rcm,
202    const char * __restrict module,
203    const char * __restrict variable)
204{
205	struct _citrus_mapper *cm;
206	_citrus_mapper_getops_t getops;
207	int ret;
208
209	/* initialize mapper handle */
210	cm = malloc(sizeof(*cm));
211	if (!cm)
212		return (errno);
213
214	cm->cm_module = NULL;
215	cm->cm_ops = NULL;
216	cm->cm_closure = NULL;
217	cm->cm_traits = NULL;
218	cm->cm_refcount = 0;
219	cm->cm_key = NULL;
220
221	/* load module */
222	ret = _citrus_load_module(&cm->cm_module, module);
223	if (ret)
224		goto err;
225
226	/* get operators */
227	getops = (_citrus_mapper_getops_t)
228	    _citrus_find_getops(cm->cm_module, module, "mapper");
229	if (!getops) {
230		ret = EOPNOTSUPP;
231		goto err;
232	}
233	cm->cm_ops = malloc(sizeof(*cm->cm_ops));
234	if (!cm->cm_ops) {
235		ret = errno;
236		goto err;
237	}
238	ret = (*getops)(cm->cm_ops);
239	if (ret)
240		goto err;
241
242	if (!cm->cm_ops->mo_init ||
243	    !cm->cm_ops->mo_uninit ||
244	    !cm->cm_ops->mo_convert ||
245	    !cm->cm_ops->mo_init_state)
246		goto err;
247
248	/* allocate traits structure */
249	cm->cm_traits = malloc(sizeof(*cm->cm_traits));
250	if (cm->cm_traits == NULL) {
251		ret = errno;
252		goto err;
253	}
254	/* initialize the mapper */
255	ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir,
256	    (const void *)variable, strlen(variable) + 1,
257	    cm->cm_traits, sizeof(*cm->cm_traits));
258	if (ret)
259		goto err;
260
261	*rcm = cm;
262
263	return (0);
264
265err:
266	mapper_close(cm);
267	return (ret);
268}
269
270/*
271 * _citrus_mapper_open_direct:
272 *	open a mapper.
273 */
274int
275_citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma,
276    struct _citrus_mapper * __restrict * __restrict rcm,
277    const char * __restrict module, const char * __restrict variable)
278{
279
280	return (mapper_open(ma, rcm, module, variable));
281}
282
283/*
284 * hash_func
285 */
286static __inline int
287hash_func(const char *key)
288{
289
290	return (_string_hash_func(key, CM_HASH_SIZE));
291}
292
293/*
294 * match_func
295 */
296static __inline int
297match_func(struct _citrus_mapper *cm, const char *key)
298{
299
300	return (strcmp(cm->cm_key, key));
301}
302
303/*
304 * _citrus_mapper_open:
305 *	open a mapper with looking up "mapper.dir".
306 */
307int
308_citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
309    struct _citrus_mapper * __restrict * __restrict rcm,
310    const char * __restrict mapname)
311{
312	struct _citrus_mapper *cm;
313	char linebuf[PATH_MAX];
314	const char *module, *variable;
315	int hashval, ret;
316
317	variable = NULL;
318
319	WLOCK;
320
321	/* search in the cache */
322	hashval = hash_func(mapname);
323	_CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname,
324	    hashval);
325	if (cm) {
326		/* found */
327		cm->cm_refcount++;
328		*rcm = cm;
329		ret = 0;
330		goto quit;
331	}
332
333	/* search mapper entry */
334	ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf,
335	    (size_t)PATH_MAX, &module, &variable);
336	if (ret)
337		goto quit;
338
339	/* open mapper */
340	UNLOCK;
341	ret = mapper_open(ma, &cm, module, variable);
342	WLOCK;
343	if (ret)
344		goto quit;
345	cm->cm_key = strdup(mapname);
346	if (cm->cm_key == NULL) {
347		ret = errno;
348		_mapper_close(cm);
349		goto quit;
350	}
351
352	/* insert to the cache */
353	cm->cm_refcount = 1;
354	_CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval);
355
356	*rcm = cm;
357	ret = 0;
358quit:
359	UNLOCK;
360
361	return (ret);
362}
363
364/*
365 * _citrus_mapper_close:
366 *	close the specified mapper.
367 */
368void
369_citrus_mapper_close(struct _citrus_mapper *cm)
370{
371
372	if (cm) {
373		WLOCK;
374		if (cm->cm_refcount == REFCOUNT_PERSISTENT)
375			goto quit;
376		if (cm->cm_refcount > 0) {
377			if (--cm->cm_refcount > 0)
378				goto quit;
379			_CITRUS_HASH_REMOVE(cm, cm_entry);
380			free(cm->cm_key);
381		}
382		mapper_close(cm);
383quit:
384		UNLOCK;
385	}
386}
387
388/*
389 * _citrus_mapper_set_persistent:
390 *	set persistent count.
391 */
392void
393_citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
394{
395
396	WLOCK;
397	cm->cm_refcount = REFCOUNT_PERSISTENT;
398	UNLOCK;
399}
400