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/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 static pthread_rwlock_t cm_lock = PTHREAD_RWLOCK_INITIALIZER; 59 60 struct _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 70 int 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; 108 quit: 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 126 static int 127 lookup_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 173 quit: 174 _unmap_file(&r); 175 return (ret); 176 } 177 178 /* 179 * mapper_close: 180 * simply close a mapper. (without handling hash) 181 */ 182 static void 183 mapper_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 */ 201 static int 202 mapper_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 269 err: 270 mapper_close(cm); 271 return (ret); 272 } 273 274 /* 275 * _citrus_mapper_open_direct: 276 * open a mapper. 277 */ 278 int 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 */ 290 static __inline int 291 hash_func(const char *key) 292 { 293 294 return (_string_hash_func(key, CM_HASH_SIZE)); 295 } 296 297 /* 298 * match_func 299 */ 300 static __inline int 301 match_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 */ 311 int 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; 362 quit: 363 UNLOCK(&cm_lock); 364 365 return (ret); 366 } 367 368 /* 369 * _citrus_mapper_close: 370 * close the specified mapper. 371 */ 372 void 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; 389 quit: 390 UNLOCK(&cm_lock); 391 } 392 } 393 394 /* 395 * _citrus_mapper_set_persistent: 396 * set persistent count. 397 */ 398 void 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