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