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 58 struct _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 68 int 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; 106 quit: 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 124 static int 125 lookup_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 171 quit: 172 _unmap_file(&r); 173 return (ret); 174 } 175 176 /* 177 * mapper_close: 178 * simply close a mapper. (without handling hash) 179 */ 180 static void 181 mapper_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 */ 199 static int 200 mapper_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 265 err: 266 mapper_close(cm); 267 return (ret); 268 } 269 270 /* 271 * _citrus_mapper_open_direct: 272 * open a mapper. 273 */ 274 int 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 */ 286 static __inline int 287 hash_func(const char *key) 288 { 289 290 return (_string_hash_func(key, CM_HASH_SIZE)); 291 } 292 293 /* 294 * match_func 295 */ 296 static __inline int 297 match_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 */ 307 int 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 ret = mapper_open(ma, &cm, module, variable); 341 if (ret) 342 goto quit; 343 cm->cm_key = strdup(mapname); 344 if (cm->cm_key == NULL) { 345 ret = errno; 346 _mapper_close(cm); 347 goto quit; 348 } 349 350 /* insert to the cache */ 351 cm->cm_refcount = 1; 352 _CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval); 353 354 *rcm = cm; 355 ret = 0; 356 quit: 357 UNLOCK; 358 359 return (ret); 360 } 361 362 /* 363 * _citrus_mapper_close: 364 * close the specified mapper. 365 */ 366 void 367 _citrus_mapper_close(struct _citrus_mapper *cm) 368 { 369 370 if (cm) { 371 WLOCK; 372 if (cm->cm_refcount == REFCOUNT_PERSISTENT) 373 goto quit; 374 if (cm->cm_refcount > 0) { 375 if (--cm->cm_refcount > 0) 376 goto quit; 377 _CITRUS_HASH_REMOVE(cm, cm_entry); 378 free(cm->cm_key); 379 } 380 mapper_close(cm); 381 quit: 382 UNLOCK; 383 } 384 } 385 386 /* 387 * _citrus_mapper_set_persistent: 388 * set persistent count. 389 */ 390 void 391 _citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm) 392 { 393 394 WLOCK; 395 cm->cm_refcount = REFCOUNT_PERSISTENT; 396 UNLOCK; 397 } 398