1 /* $NetBSD: citrus_mapper.c,v 1.10 2012/06/08 07:49:42 martin Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c)2003 Citrus Project, 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/queue.h> 35 36 #include <assert.h> 37 #include <errno.h> 38 #include <limits.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 43 #include "citrus_namespace.h" 44 #include "citrus_types.h" 45 #include "citrus_region.h" 46 #include "citrus_lock.h" 47 #include "citrus_memstream.h" 48 #include "citrus_bcs.h" 49 #include "citrus_mmap.h" 50 #include "citrus_module.h" 51 #include "citrus_hash.h" 52 #include "citrus_mapper.h" 53 54 #define _CITRUS_MAPPER_DIR "mapper.dir" 55 56 #define CM_HASH_SIZE 101 57 #define REFCOUNT_PERSISTENT -1 58 59 static pthread_rwlock_t cm_lock = PTHREAD_RWLOCK_INITIALIZER; 60 61 struct _citrus_mapper_area { 62 _CITRUS_HASH_HEAD(, _citrus_mapper, CM_HASH_SIZE) ma_cache; 63 char *ma_dir; 64 }; 65 66 /* 67 * _citrus_mapper_create_area: 68 * create mapper area 69 */ 70 71 int 72 _citrus_mapper_create_area( 73 struct _citrus_mapper_area *__restrict *__restrict rma, 74 const char *__restrict area) 75 { 76 struct _citrus_mapper_area *ma; 77 struct stat st; 78 char path[PATH_MAX]; 79 int ret; 80 81 WLOCK(&cm_lock); 82 83 if (*rma != NULL) { 84 ret = 0; 85 goto quit; 86 } 87 88 snprintf(path, (size_t)PATH_MAX, "%s/%s", area, _CITRUS_MAPPER_DIR); 89 90 ret = stat(path, &st); 91 if (ret) 92 goto quit; 93 94 ma = malloc(sizeof(*ma)); 95 if (ma == NULL) { 96 ret = errno; 97 goto quit; 98 } 99 ma->ma_dir = strdup(area); 100 if (ma->ma_dir == NULL) { 101 ret = errno; 102 free(ma); 103 goto quit; 104 } 105 _CITRUS_HASH_INIT(&ma->ma_cache, CM_HASH_SIZE); 106 107 *rma = ma; 108 ret = 0; 109 quit: 110 UNLOCK(&cm_lock); 111 112 return (ret); 113 } 114 115 116 /* 117 * lookup_mapper_entry: 118 * lookup mapper.dir entry in the specified directory. 119 * 120 * line format of iconv.dir file: 121 * mapper module arg 122 * mapper : mapper name. 123 * module : mapper module name. 124 * arg : argument for the module (generally, description file name) 125 */ 126 127 static int 128 lookup_mapper_entry(const char *dir, const char *mapname, void *linebuf, 129 size_t linebufsize, const char **module, const char **variable) 130 { 131 struct _region r; 132 struct _memstream ms; 133 const char *cp, *cq; 134 char *p; 135 char path[PATH_MAX]; 136 size_t len; 137 int ret; 138 139 /* create mapper.dir path */ 140 snprintf(path, (size_t)PATH_MAX, "%s/%s", dir, _CITRUS_MAPPER_DIR); 141 142 /* open read stream */ 143 ret = _map_file(&r, path); 144 if (ret) 145 return (ret); 146 147 _memstream_bind(&ms, &r); 148 149 /* search the line matching to the map name */ 150 cp = _memstream_matchline(&ms, mapname, &len, 0); 151 if (!cp) { 152 ret = ENOENT; 153 goto quit; 154 } 155 if (!len || len > linebufsize - 1) { 156 ret = EINVAL; 157 goto quit; 158 } 159 160 p = linebuf; 161 /* get module name */ 162 *module = p; 163 cq = _bcs_skip_nonws_len(cp, &len); 164 strlcpy(p, cp, (size_t)(cq - cp + 1)); 165 p += cq - cp + 1; 166 167 /* get variable */ 168 *variable = p; 169 cp = _bcs_skip_ws_len(cq, &len); 170 strlcpy(p, cp, len + 1); 171 172 ret = 0; 173 174 quit: 175 _unmap_file(&r); 176 return (ret); 177 } 178 179 /* 180 * mapper_close: 181 * simply close a mapper. (without handling hash) 182 */ 183 static void 184 mapper_close(struct _citrus_mapper *cm) 185 { 186 if (cm->cm_module) { 187 if (cm->cm_ops) { 188 if (cm->cm_closure) 189 (*cm->cm_ops->mo_uninit)(cm); 190 free(cm->cm_ops); 191 } 192 _citrus_unload_module(cm->cm_module); 193 } 194 free(cm->cm_traits); 195 free(cm); 196 } 197 198 /* 199 * mapper_open: 200 * simply open a mapper. (without handling hash) 201 */ 202 static int 203 mapper_open(struct _citrus_mapper_area *__restrict ma, 204 struct _citrus_mapper * __restrict * __restrict rcm, 205 const char * __restrict module, 206 const char * __restrict variable) 207 { 208 struct _citrus_mapper *cm; 209 _citrus_mapper_getops_t getops; 210 int ret; 211 212 /* initialize mapper handle */ 213 cm = malloc(sizeof(*cm)); 214 if (!cm) 215 return (errno); 216 217 cm->cm_module = NULL; 218 cm->cm_ops = NULL; 219 cm->cm_closure = NULL; 220 cm->cm_traits = NULL; 221 cm->cm_refcount = 0; 222 cm->cm_key = NULL; 223 224 /* load module */ 225 ret = _citrus_load_module(&cm->cm_module, module); 226 if (ret) 227 goto err; 228 229 /* get operators */ 230 getops = (_citrus_mapper_getops_t) 231 _citrus_find_getops(cm->cm_module, module, "mapper"); 232 if (!getops) { 233 ret = EOPNOTSUPP; 234 goto err; 235 } 236 cm->cm_ops = malloc(sizeof(*cm->cm_ops)); 237 if (!cm->cm_ops) { 238 ret = errno; 239 goto err; 240 } 241 ret = (*getops)(cm->cm_ops); 242 if (ret) 243 goto err; 244 245 if (!cm->cm_ops->mo_init || 246 !cm->cm_ops->mo_uninit || 247 !cm->cm_ops->mo_convert || 248 !cm->cm_ops->mo_init_state) { 249 ret = EINVAL; 250 goto err; 251 } 252 253 /* allocate traits structure */ 254 cm->cm_traits = malloc(sizeof(*cm->cm_traits)); 255 if (cm->cm_traits == NULL) { 256 ret = errno; 257 goto err; 258 } 259 /* initialize the mapper */ 260 ret = (*cm->cm_ops->mo_init)(ma, cm, ma->ma_dir, 261 (const void *)variable, strlen(variable) + 1, 262 cm->cm_traits, sizeof(*cm->cm_traits)); 263 if (ret) 264 goto err; 265 266 *rcm = cm; 267 268 return (0); 269 270 err: 271 mapper_close(cm); 272 return (ret); 273 } 274 275 /* 276 * _citrus_mapper_open_direct: 277 * open a mapper. 278 */ 279 int 280 _citrus_mapper_open_direct(struct _citrus_mapper_area *__restrict ma, 281 struct _citrus_mapper * __restrict * __restrict rcm, 282 const char * __restrict module, const char * __restrict variable) 283 { 284 285 return (mapper_open(ma, rcm, module, variable)); 286 } 287 288 /* 289 * hash_func 290 */ 291 static __inline int 292 hash_func(const char *key) 293 { 294 295 return (_string_hash_func(key, CM_HASH_SIZE)); 296 } 297 298 /* 299 * match_func 300 */ 301 static __inline int 302 match_func(struct _citrus_mapper *cm, const char *key) 303 { 304 305 return (strcmp(cm->cm_key, key)); 306 } 307 308 /* 309 * _citrus_mapper_open: 310 * open a mapper with looking up "mapper.dir". 311 */ 312 int 313 _citrus_mapper_open(struct _citrus_mapper_area *__restrict ma, 314 struct _citrus_mapper * __restrict * __restrict rcm, 315 const char * __restrict mapname) 316 { 317 struct _citrus_mapper *cm; 318 char linebuf[PATH_MAX]; 319 const char *module, *variable; 320 int hashval, ret; 321 322 variable = NULL; 323 324 WLOCK(&cm_lock); 325 326 /* search in the cache */ 327 hashval = hash_func(mapname); 328 _CITRUS_HASH_SEARCH(&ma->ma_cache, cm, cm_entry, match_func, mapname, 329 hashval); 330 if (cm) { 331 /* found */ 332 cm->cm_refcount++; 333 *rcm = cm; 334 ret = 0; 335 goto quit; 336 } 337 338 /* search mapper entry */ 339 ret = lookup_mapper_entry(ma->ma_dir, mapname, linebuf, 340 (size_t)PATH_MAX, &module, &variable); 341 if (ret) 342 goto quit; 343 344 /* open mapper */ 345 UNLOCK(&cm_lock); 346 ret = mapper_open(ma, &cm, module, variable); 347 WLOCK(&cm_lock); 348 if (ret) 349 goto quit; 350 cm->cm_key = strdup(mapname); 351 if (cm->cm_key == NULL) { 352 ret = errno; 353 _mapper_close(cm); 354 goto quit; 355 } 356 357 /* insert to the cache */ 358 cm->cm_refcount = 1; 359 _CITRUS_HASH_INSERT(&ma->ma_cache, cm, cm_entry, hashval); 360 361 *rcm = cm; 362 ret = 0; 363 quit: 364 UNLOCK(&cm_lock); 365 366 return (ret); 367 } 368 369 /* 370 * _citrus_mapper_close: 371 * close the specified mapper. 372 */ 373 void 374 _citrus_mapper_close(struct _citrus_mapper *cm) 375 { 376 377 if (cm) { 378 WLOCK(&cm_lock); 379 if (cm->cm_refcount == REFCOUNT_PERSISTENT) 380 goto quit; 381 if (cm->cm_refcount > 0) { 382 if (--cm->cm_refcount > 0) 383 goto quit; 384 _CITRUS_HASH_REMOVE(cm, cm_entry); 385 free(cm->cm_key); 386 } 387 UNLOCK(&cm_lock); 388 mapper_close(cm); 389 return; 390 quit: 391 UNLOCK(&cm_lock); 392 } 393 } 394 395 /* 396 * _citrus_mapper_set_persistent: 397 * set persistent count. 398 */ 399 void 400 _citrus_mapper_set_persistent(struct _citrus_mapper * __restrict cm) 401 { 402 403 WLOCK(&cm_lock); 404 cm->cm_refcount = REFCOUNT_PERSISTENT; 405 UNLOCK(&cm_lock); 406 } 407