1219019Sgabor/* $FreeBSD$ */
2264497Stijl/*	$NetBSD: citrus_mapper.c,v 1.10 2012/06/08 07:49:42 martin Exp $	*/
3219019Sgabor
4219019Sgabor/*-
5219019Sgabor * Copyright (c)2003 Citrus Project,
6219019Sgabor * All rights reserved.
7219019Sgabor *
8219019Sgabor * Redistribution and use in source and binary forms, with or without
9219019Sgabor * modification, are permitted provided that the following conditions
10219019Sgabor * are met:
11219019Sgabor * 1. Redistributions of source code must retain the above copyright
12219019Sgabor *    notice, this list of conditions and the following disclaimer.
13219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
14219019Sgabor *    notice, this list of conditions and the following disclaimer in the
15219019Sgabor *    documentation and/or other materials provided with the distribution.
16219019Sgabor *
17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20219019Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27219019Sgabor * SUCH DAMAGE.
28219019Sgabor */
29219019Sgabor
30219019Sgabor#include <sys/cdefs.h>
31219019Sgabor#include <sys/types.h>
32219019Sgabor#include <sys/stat.h>
33219019Sgabor#include <sys/queue.h>
34219019Sgabor
35219019Sgabor#include <assert.h>
36219019Sgabor#include <errno.h>
37219019Sgabor#include <limits.h>
38219019Sgabor#include <stdio.h>
39219019Sgabor#include <stdlib.h>
40219019Sgabor#include <string.h>
41219019Sgabor
42219019Sgabor#include "citrus_namespace.h"
43219019Sgabor#include "citrus_types.h"
44219019Sgabor#include "citrus_region.h"
45219019Sgabor#include "citrus_lock.h"
46219019Sgabor#include "citrus_memstream.h"
47219019Sgabor#include "citrus_bcs.h"
48219019Sgabor#include "citrus_mmap.h"
49219019Sgabor#include "citrus_module.h"
50219019Sgabor#include "citrus_hash.h"
51219019Sgabor#include "citrus_mapper.h"
52219019Sgabor
53219019Sgabor#define _CITRUS_MAPPER_DIR	"mapper.dir"
54219019Sgabor
55219019Sgabor#define CM_HASH_SIZE 101
56219019Sgabor#define REFCOUNT_PERSISTENT	-1
57219019Sgabor
58252584Speterstatic pthread_rwlock_t		cm_lock = PTHREAD_RWLOCK_INITIALIZER;
59252584Speter
60219019Sgaborstruct _citrus_mapper_area {
61219019Sgabor	_CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE)	 ma_cache;
62219019Sgabor	char							*ma_dir;
63219019Sgabor};
64219019Sgabor
65219019Sgabor/*
66219019Sgabor * _citrus_mapper_create_area:
67219019Sgabor *	create mapper area
68219019Sgabor */
69219019Sgabor
70219019Sgaborint
71219019Sgabor_citrus_mapper_create_area(
72219019Sgabor    struct _citrus_mapper_area *__restrict *__restrict rma,
73219019Sgabor    const char *__restrict area)
74219019Sgabor{
75219019Sgabor	struct _citrus_mapper_area *ma;
76219019Sgabor	struct stat st;
77219019Sgabor	char path[PATH_MAX];
78219019Sgabor	int ret;
79219019Sgabor
80252584Speter	WLOCK(&cm_lock);
81219019Sgabor
82219019Sgabor	if (*rma != NULL) {
83219019Sgabor		ret = 0;
84219019Sgabor		goto quit;
85219019Sgabor	}
86219019Sgabor
87219019Sgabor	snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR);
88219019Sgabor
89219019Sgabor	ret = stat(path, &st);
90219019Sgabor	if (ret)
91219019Sgabor		goto quit;
92219019Sgabor
93219019Sgabor	ma = malloc(sizeof(*ma));
94219019Sgabor	if (ma == NULL) {
95219019Sgabor		ret = errno;
96219019Sgabor		goto quit;
97219019Sgabor	}
98219019Sgabor	ma->ma_dir = strdup(area);
99219019Sgabor	if (ma->ma_dir == NULL) {
100219019Sgabor		ret = errno;
101252464Speter		free(ma);
102219019Sgabor		goto quit;
103219019Sgabor	}
104219019Sgabor	_CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE);
105219019Sgabor
106219019Sgabor	*rma = ma;
107219019Sgabor	ret = 0;
108219019Sgaborquit:
109252584Speter	UNLOCK(&cm_lock);
110219019Sgabor
111219019Sgabor	return (ret);
112219019Sgabor}
113219019Sgabor
114219019Sgabor
115219019Sgabor/*
116219019Sgabor * lookup_mapper_entry:
117219019Sgabor *	lookup mapper.dir entry in the specified directory.
118219019Sgabor *
119219019Sgabor * line format of iconv.dir file:
120219019Sgabor *	mapper	module	arg
121219019Sgabor * mapper : mapper name.
122219019Sgabor * module : mapper module name.
123219019Sgabor * arg    : argument for the module (generally, description file name)
124219019Sgabor */
125219019Sgabor
126219019Sgaborstatic int
127219019Sgaborlookup_mapper_entry(const char *dir, const char *mapname, void *linebuf,
128219019Sgabor    size_t linebufsize, const char **module, const char **variable)
129219019Sgabor{
130219019Sgabor	struct _region r;
131219019Sgabor	struct _memstream ms;
132219019Sgabor	const char *cp, *cq;
133219019Sgabor	char *p;
134219019Sgabor	char path[PATH_MAX];
135219019Sgabor	size_t len;
136219019Sgabor	int ret;
137219019Sgabor
138219019Sgabor	/* create mapper.dir path */
139219019Sgabor	snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR);
140219019Sgabor
141219019Sgabor	/* open read stream */
142219019Sgabor	ret = _map_file(&r, path);
143219019Sgabor	if (ret)
144219019Sgabor		return (ret);
145219019Sgabor
146219019Sgabor	_memstream_bind(&ms, &r);
147219019Sgabor
148219019Sgabor	/* search the line matching to the map name */
149219019Sgabor	cp = _memstream_matchline(&ms, mapname, &len, 0);
150219019Sgabor	if (!cp) {
151219019Sgabor		ret = ENOENT;
152219019Sgabor		goto quit;
153219019Sgabor	}
154219019Sgabor	if (!len || len > linebufsize - 1) {
155219019Sgabor		ret = EINVAL;
156219019Sgabor		goto quit;
157219019Sgabor	}
158219019Sgabor
159219019Sgabor	p = linebuf;
160219019Sgabor	/* get module name */
161219019Sgabor	*module = p;
162219019Sgabor	cq = _bcs_skip_nonws_len(cp, &len);
163219019Sgabor	strlcpy(p, cp, (size_t)(cq - cp + 1));
164219019Sgabor	p += cq - cp + 1;
165219019Sgabor
166219019Sgabor	/* get variable */
167219019Sgabor	*variable = p;
168219019Sgabor	cp = _bcs_skip_ws_len(cq, &len);
169219019Sgabor	strlcpy(p, cp, len + 1);
170219019Sgabor
171219019Sgabor	ret = 0;
172219019Sgabor
173219019Sgaborquit:
174219019Sgabor	_unmap_file(&r);
175219019Sgabor	return (ret);
176219019Sgabor}
177219019Sgabor
178219019Sgabor/*
179219019Sgabor * mapper_close:
180219019Sgabor *	simply close a mapper. (without handling hash)
181219019Sgabor */
182219019Sgaborstatic void
183219019Sgabormapper_close(struct _citrus_mapper *cm)
184219019Sgabor{
185219019Sgabor	if (cm->cm_module) {
186219019Sgabor		if (cm->cm_ops) {
187219019Sgabor			if (cm->cm_closure)
188219019Sgabor				(*cm->cm_ops->mo_uninit)(cm);
189219019Sgabor			free(cm->cm_ops);
190219019Sgabor		}
191219019Sgabor		_citrus_unload_module(cm->cm_module);
192219019Sgabor	}
193219019Sgabor	free(cm->cm_traits);
194219019Sgabor	free(cm);
195219019Sgabor}
196219019Sgabor
197219019Sgabor/*
198219019Sgabor * mapper_open:
199219019Sgabor *	simply open a mapper. (without handling hash)
200219019Sgabor */
201219019Sgaborstatic int
202219019Sgabormapper_open(struct _citrus_mapper_area *__restrict ma,
203219019Sgabor    struct _citrus_mapper * __restrict * __restrict rcm,
204219019Sgabor    const char * __restrict module,
205219019Sgabor    const char * __restrict variable)
206219019Sgabor{
207219019Sgabor	struct _citrus_mapper *cm;
208219019Sgabor	_citrus_mapper_getops_t getops;
209219019Sgabor	int ret;
210219019Sgabor
211219019Sgabor	/* initialize mapper handle */
212219019Sgabor	cm = malloc(sizeof(*cm));
213219019Sgabor	if (!cm)
214219019Sgabor		return (errno);
215219019Sgabor
216219019Sgabor	cm->cm_module = NULL;
217219019Sgabor	cm->cm_ops = NULL;
218219019Sgabor	cm->cm_closure = NULL;
219219019Sgabor	cm->cm_traits = NULL;
220219019Sgabor	cm->cm_refcount = 0;
221219019Sgabor	cm->cm_key = NULL;
222219019Sgabor
223219019Sgabor	/* load module */
224219019Sgabor	ret = _citrus_load_module(&cm->cm_module, module);
225219019Sgabor	if (ret)
226219019Sgabor		goto err;
227219019Sgabor
228219019Sgabor	/* get operators */
229219019Sgabor	getops = (_citrus_mapper_getops_t)
230219019Sgabor	    _citrus_find_getops(cm->cm_module, module, "mapper");
231219019Sgabor	if (!getops) {
232219019Sgabor		ret = EOPNOTSUPP;
233219019Sgabor		goto err;
234219019Sgabor	}
235219019Sgabor	cm->cm_ops = malloc(sizeof(*cm->cm_ops));
236219019Sgabor	if (!cm->cm_ops) {
237219019Sgabor		ret = errno;
238219019Sgabor		goto err;
239219019Sgabor	}
240219019Sgabor	ret = (*getops)(cm->cm_ops);
241219019Sgabor	if (ret)
242219019Sgabor		goto err;
243219019Sgabor
244219019Sgabor	if (!cm->cm_ops->mo_init ||
245219019Sgabor	    !cm->cm_ops->mo_uninit ||
246219019Sgabor	    !cm->cm_ops->mo_convert ||
247264497Stijl	    !cm->cm_ops->mo_init_state) {
248264497Stijl		ret = EINVAL;
249219019Sgabor		goto err;
250264497Stijl	}
251219019Sgabor
252219019Sgabor	/* allocate traits structure */
253219019Sgabor	cm->cm_traits = malloc(sizeof(*cm->cm_traits));
254219019Sgabor	if (cm->cm_traits == NULL) {
255219019Sgabor		ret = errno;
256219019Sgabor		goto err;
257219019Sgabor	}
258219019Sgabor	/* initialize the mapper */
259219019Sgabor	ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir,
260219019Sgabor	    (const void *)variable, strlen(variable) + 1,
261219019Sgabor	    cm->cm_traits, sizeof(*cm->cm_traits));
262219019Sgabor	if (ret)
263219019Sgabor		goto err;
264219019Sgabor
265219019Sgabor	*rcm = cm;
266219019Sgabor
267219019Sgabor	return (0);
268219019Sgabor
269219019Sgaborerr:
270219019Sgabor	mapper_close(cm);
271219019Sgabor	return (ret);
272219019Sgabor}
273219019Sgabor
274219019Sgabor/*
275219019Sgabor * _citrus_mapper_open_direct:
276219019Sgabor *	open a mapper.
277219019Sgabor */
278219019Sgaborint
279219019Sgabor_citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma,
280219019Sgabor    struct _citrus_mapper * __restrict * __restrict rcm,
281219019Sgabor    const char * __restrict module, const char * __restrict variable)
282219019Sgabor{
283219019Sgabor
284219019Sgabor	return (mapper_open(ma, rcm, module, variable));
285219019Sgabor}
286219019Sgabor
287219019Sgabor/*
288219019Sgabor * hash_func
289219019Sgabor */
290219019Sgaborstatic __inline int
291219019Sgaborhash_func(const char *key)
292219019Sgabor{
293219019Sgabor
294219019Sgabor	return (_string_hash_func(key, CM_HASH_SIZE));
295219019Sgabor}
296219019Sgabor
297219019Sgabor/*
298219019Sgabor * match_func
299219019Sgabor */
300219019Sgaborstatic __inline int
301219019Sgabormatch_func(struct _citrus_mapper *cm, const char *key)
302219019Sgabor{
303219019Sgabor
304219019Sgabor	return (strcmp(cm->cm_key, key));
305219019Sgabor}
306219019Sgabor
307219019Sgabor/*
308219019Sgabor * _citrus_mapper_open:
309219019Sgabor *	open a mapper with looking up "mapper.dir".
310219019Sgabor */
311219019Sgaborint
312219019Sgabor_citrus_mapper_open(struct _citrus_mapper_area *__restrict ma,
313219019Sgabor    struct _citrus_mapper * __restrict * __restrict rcm,
314219019Sgabor    const char * __restrict mapname)
315219019Sgabor{
316219019Sgabor	struct _citrus_mapper *cm;
317219019Sgabor	char linebuf[PATH_MAX];
318219019Sgabor	const char *module, *variable;
319219019Sgabor	int hashval, ret;
320219019Sgabor
321219019Sgabor	variable = NULL;
322219019Sgabor
323252584Speter	WLOCK(&cm_lock);
324219019Sgabor
325219019Sgabor	/* search in the cache */
326219019Sgabor	hashval = hash_func(mapname);
327219019Sgabor	_CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname,
328219019Sgabor	    hashval);
329219019Sgabor	if (cm) {
330219019Sgabor		/* found */
331219019Sgabor		cm->cm_refcount++;
332219019Sgabor		*rcm = cm;
333219019Sgabor		ret = 0;
334219019Sgabor		goto quit;
335219019Sgabor	}
336219019Sgabor
337219019Sgabor	/* search mapper entry */
338219019Sgabor	ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf,
339219019Sgabor	    (size_t)PATH_MAX, &module, &variable);
340219019Sgabor	if (ret)
341219019Sgabor		goto quit;
342219019Sgabor
343219019Sgabor	/* open mapper */
344252584Speter	UNLOCK(&cm_lock);
345219019Sgabor	ret = mapper_open(ma, &cm, module, variable);
346252584Speter	WLOCK(&cm_lock);
347219019Sgabor	if (ret)
348219019Sgabor		goto quit;
349219019Sgabor	cm->cm_key = strdup(mapname);
350219019Sgabor	if (cm->cm_key == NULL) {
351219019Sgabor		ret = errno;
352219019Sgabor		_mapper_close(cm);
353219019Sgabor		goto quit;
354219019Sgabor	}
355219019Sgabor
356219019Sgabor	/* insert to the cache */
357219019Sgabor	cm->cm_refcount = 1;
358219019Sgabor	_CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval);
359219019Sgabor
360219019Sgabor	*rcm = cm;
361219019Sgabor	ret = 0;
362219019Sgaborquit:
363252584Speter	UNLOCK(&cm_lock);
364219019Sgabor
365219019Sgabor	return (ret);
366219019Sgabor}
367219019Sgabor
368219019Sgabor/*
369219019Sgabor * _citrus_mapper_close:
370219019Sgabor *	close the specified mapper.
371219019Sgabor */
372219019Sgaborvoid
373219019Sgabor_citrus_mapper_close(struct _citrus_mapper *cm)
374219019Sgabor{
375219019Sgabor
376219019Sgabor	if (cm) {
377252584Speter		WLOCK(&cm_lock);
378219019Sgabor		if (cm->cm_refcount == REFCOUNT_PERSISTENT)
379219019Sgabor			goto quit;
380219019Sgabor		if (cm->cm_refcount > 0) {
381219019Sgabor			if (--cm->cm_refcount > 0)
382219019Sgabor				goto quit;
383219019Sgabor			_CITRUS_HASH_REMOVE(cm, cm_entry);
384219019Sgabor			free(cm->cm_key);
385219019Sgabor		}
386257039Sdelphij		UNLOCK(&cm_lock);
387219019Sgabor		mapper_close(cm);
388257039Sdelphij		return;
389219019Sgaborquit:
390252584Speter		UNLOCK(&cm_lock);
391219019Sgabor	}
392219019Sgabor}
393219019Sgabor
394219019Sgabor/*
395219019Sgabor * _citrus_mapper_set_persistent:
396219019Sgabor *	set persistent count.
397219019Sgabor */
398219019Sgaborvoid
399219019Sgabor_citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm)
400219019Sgabor{
401219019Sgabor
402252584Speter	WLOCK(&cm_lock);
403219019Sgabor	cm->cm_refcount = REFCOUNT_PERSISTENT;
404252584Speter	UNLOCK(&cm_lock);
405219019Sgabor}
406