1 /* $NetBSD: dict_open.c,v 1.1.1.4 2011/05/11 09:11:23 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* dict_open 3 6 /* SUMMARY 7 /* low-level dictionary interface 8 /* SYNOPSIS 9 /* #include <dict.h> 10 /* 11 /* DICT *dict_open(dict_spec, open_flags, dict_flags) 12 /* const char *dict_spec; 13 /* int open_flags; 14 /* int dict_flags; 15 /* 16 /* DICT *dict_open3(dict_type, dict_name, open_flags, dict_flags) 17 /* const char *dict_type; 18 /* const char *dict_name; 19 /* int open_flags; 20 /* int dict_flags; 21 /* 22 /* void dict_put(dict, key, value) 23 /* DICT *dict; 24 /* const char *key; 25 /* const char *value; 26 /* 27 /* const char *dict_get(dict, key) 28 /* DICT *dict; 29 /* const char *key; 30 /* 31 /* int dict_del(dict, key) 32 /* DICT *dict; 33 /* const char *key; 34 /* 35 /* void dict_seq(dict, func, key, value) 36 /* DICT *dict; 37 /* int func; 38 /* const char **key; 39 /* const char **value; 40 /* 41 /* void dict_close(dict) 42 /* DICT *dict; 43 /* 44 /* dict_open_register(type, open) 45 /* char *type; 46 /* DICT *(*open) (const char *, int, int); 47 /* 48 /* ARGV *dict_mapnames() 49 /* DESCRIPTION 50 /* This module implements a low-level interface to multiple 51 /* physical dictionary types. 52 /* 53 /* dict_open() takes a type:name pair that specifies a dictionary type 54 /* and dictionary name, opens the dictionary, and returns a dictionary 55 /* handle. The \fIopen_flags\fR arguments are as in open(2). The 56 /* \fIdict_flags\fR are the bit-wise OR of zero or more of the following: 57 /* .IP DICT_FLAG_DUP_WARN 58 /* Warn about duplicate keys, if the underlying database does not 59 /* support duplicate keys. The default is to terminate with a fatal 60 /* error. 61 /* .IP DICT_FLAG_DUP_IGNORE 62 /* Ignore duplicate keys if the underlying database does not 63 /* support duplicate keys. The default is to terminate with a fatal 64 /* error. 65 /* .IP DICT_FLAG_DUP_REPLACE 66 /* Replace duplicate keys if the underlying database supports such 67 /* an operation. The default is to terminate with a fatal error. 68 /* .IP DICT_FLAG_TRY0NULL 69 /* With maps where this is appropriate, append no null byte to 70 /* keys and values. 71 /* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are 72 /* specified, the software guesses what format to use for reading; 73 /* and in the absence of definite information, a system-dependent 74 /* default is chosen for writing. 75 /* .IP DICT_FLAG_TRY1NULL 76 /* With maps where this is appropriate, append one null byte to 77 /* keys and values. 78 /* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are 79 /* specified, the software guesses what format to use for reading; 80 /* and in the absence of definite information, a system-dependent 81 /* default is chosen for writing. 82 /* .IP DICT_FLAG_LOCK 83 /* With maps where this is appropriate, acquire an exclusive lock 84 /* before writing, and acquire a shared lock before reading. 85 /* .IP DICT_FLAG_OPEN_LOCK 86 /* With maps where this is appropriate, acquire an exclusive 87 /* lock upon open, and report a fatal run-time error if the 88 /* table is already locked. 89 /* .IP DICT_FLAG_FOLD_FIX 90 /* With databases whose lookup fields are fixed-case strings, 91 /* fold the search key to lower case before accessing the 92 /* database. This includes hash:, cdb:, dbm:. nis:, ldap:, 93 /* *sql. 94 /* .IP DICT_FLAG_FOLD_MUL 95 /* With databases where one lookup field can match both upper 96 /* and lower case, fold the search key to lower case before 97 /* accessing the database. This includes regexp: and pcre: 98 /* .IP DICT_FLAG_FOLD_ANY 99 /* Short-hand for (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL). 100 /* .IP DICT_FLAG_SYNC_UPDATE 101 /* With file-based maps, flush I/O buffers to file after each update. 102 /* Thus feature is not supported with some file-based dictionaries. 103 /* .IP DICT_FLAG_NO_REGSUB 104 /* Disallow regular expression substitution from left-hand side data 105 /* into the right-hand side. 106 /* .IP DICT_FLAG_NO_PROXY 107 /* Disallow access through the \fBproxymap\fR service. 108 /* .IP DICT_FLAG_NO_UNAUTH 109 /* Disallow network lookup mechanisms that lack any form of 110 /* authentication (example: tcp_table; even NIS can be secured 111 /* to some extent by requiring that the server binds to a 112 /* privileged port). 113 /* .IP DICT_FLAG_PARANOID 114 /* A combination of all the paranoia flags: DICT_FLAG_NO_REGSUB, 115 /* DICT_FLAG_NO_PROXY and DICT_FLAG_NO_UNAUTH. 116 /* .PP 117 /* Specify DICT_FLAG_NONE for no special processing. 118 /* 119 /* The dictionary types are as follows: 120 /* .IP environ 121 /* The process environment array. The \fIdict_name\fR argument is ignored. 122 /* .IP dbm 123 /* DBM file. 124 /* .IP hash 125 /* Berkeley DB file in hash format. 126 /* .IP btree 127 /* Berkeley DB file in btree format. 128 /* .IP nis 129 /* NIS map. Only read access is supported. 130 /* .IP nisplus 131 /* NIS+ map. Only read access is supported. 132 /* .IP netinfo 133 /* NetInfo table. Only read access is supported. 134 /* .IP ldap 135 /* LDAP ("light-weight" directory access protocol) database access. 136 /* .IP pcre 137 /* PERL-compatible regular expressions. 138 /* .IP regexp 139 /* POSIX-compatible regular expressions. 140 /* .IP texthash 141 /* Flat text in postmap(1) input format. 142 /* .PP 143 /* dict_open3() takes separate arguments for dictionary type and 144 /* name, but otherwise performs the same functions as dict_open(). 145 /* 146 /* dict_get() retrieves the value stored in the named dictionary 147 /* under the given key. A null pointer means the value was not found. 148 /* As with dict_lookup(), the result is owned by the lookup table 149 /* implementation. Make a copy if the result is to be modified, 150 /* or if the result is to survive multiple table lookups. 151 /* 152 /* dict_put() stores the specified key and value into the named 153 /* dictionary. 154 /* 155 /* dict_del() removes a dictionary entry, and returns zero 156 /* in case of success. 157 /* 158 /* dict_seq() iterates over all members in the named dictionary. 159 /* func is define DICT_SEQ_FUN_FIRST (select first member) or 160 /* DICT_SEQ_FUN_NEXT (select next member). A zero result means 161 /* that an entry was found. 162 /* 163 /* dict_close() closes the specified dictionary and cleans up the 164 /* associated data structures. 165 /* 166 /* dict_open_register() adds support for a new dictionary type. 167 /* 168 /* dict_mapnames() returns a sorted list with the names of all available 169 /* dictionary types. 170 /* DIAGNOSTICS 171 /* Fatal error: open error, unsupported dictionary type, attempt to 172 /* update non-writable dictionary. 173 /* LICENSE 174 /* .ad 175 /* .fi 176 /* The Secure Mailer license must be distributed with this software. 177 /* AUTHOR(S) 178 /* Wietse Venema 179 /* IBM T.J. Watson Research 180 /* P.O. Box 704 181 /* Yorktown Heights, NY 10598, USA 182 /*--*/ 183 184 /* System library. */ 185 186 #include <sys_defs.h> 187 #include <string.h> 188 #include <stdlib.h> 189 190 #ifdef STRCASECMP_IN_STRINGS_H 191 #include <strings.h> 192 #endif 193 194 /* Utility library. */ 195 196 #include <argv.h> 197 #include <mymalloc.h> 198 #include <msg.h> 199 #include <dict.h> 200 #include <dict_cdb.h> 201 #include <dict_env.h> 202 #include <dict_unix.h> 203 #include <dict_tcp.h> 204 #include <dict_sdbm.h> 205 #include <dict_dbm.h> 206 #include <dict_db.h> 207 #include <dict_nis.h> 208 #include <dict_nisplus.h> 209 #include <dict_ni.h> 210 #include <dict_pcre.h> 211 #include <dict_regexp.h> 212 #include <dict_static.h> 213 #include <dict_cidr.h> 214 #include <dict_ht.h> 215 #include <dict_thash.h> 216 #include <stringops.h> 217 #include <split_at.h> 218 #include <htable.h> 219 #include <myflock.h> 220 221 /* 222 * lookup table for available map types. 223 */ 224 typedef struct { 225 char *type; 226 struct DICT *(*open) (const char *, int, int); 227 } DICT_OPEN_INFO; 228 229 static const DICT_OPEN_INFO dict_open_info[] = { 230 #ifdef HAS_CDB 231 DICT_TYPE_CDB, dict_cdb_open, 232 #endif 233 DICT_TYPE_ENVIRON, dict_env_open, 234 DICT_TYPE_HT, dict_ht_open, 235 DICT_TYPE_UNIX, dict_unix_open, 236 DICT_TYPE_TCP, dict_tcp_open, 237 #ifdef HAS_SDBM 238 DICT_TYPE_SDBM, dict_sdbm_open, 239 #endif 240 #ifdef HAS_DBM 241 DICT_TYPE_DBM, dict_dbm_open, 242 #endif 243 #ifdef HAS_DB 244 DICT_TYPE_HASH, dict_hash_open, 245 DICT_TYPE_BTREE, dict_btree_open, 246 #endif 247 #ifdef HAS_NIS 248 DICT_TYPE_NIS, dict_nis_open, 249 #endif 250 #ifdef HAS_NISPLUS 251 DICT_TYPE_NISPLUS, dict_nisplus_open, 252 #endif 253 #ifdef HAS_NETINFO 254 DICT_TYPE_NETINFO, dict_ni_open, 255 #endif 256 #ifdef HAS_PCRE 257 DICT_TYPE_PCRE, dict_pcre_open, 258 #endif 259 #ifdef HAS_POSIX_REGEXP 260 DICT_TYPE_REGEXP, dict_regexp_open, 261 #endif 262 DICT_TYPE_STATIC, dict_static_open, 263 DICT_TYPE_CIDR, dict_cidr_open, 264 DICT_TYPE_THASH, dict_thash_open, 265 0, 266 }; 267 268 static HTABLE *dict_open_hash; 269 270 /* dict_open_init - one-off initialization */ 271 272 static void dict_open_init(void) 273 { 274 const char *myname = "dict_open_init"; 275 const DICT_OPEN_INFO *dp; 276 277 if (dict_open_hash != 0) 278 msg_panic("%s: multiple initialization", myname); 279 dict_open_hash = htable_create(10); 280 281 for (dp = dict_open_info; dp->type; dp++) 282 htable_enter(dict_open_hash, dp->type, (char *) dp); 283 } 284 285 /* dict_open - open dictionary */ 286 287 DICT *dict_open(const char *dict_spec, int open_flags, int dict_flags) 288 { 289 char *saved_dict_spec = mystrdup(dict_spec); 290 char *dict_name; 291 DICT *dict; 292 293 if ((dict_name = split_at(saved_dict_spec, ':')) == 0) 294 msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s\"", 295 dict_spec); 296 297 dict = dict_open3(saved_dict_spec, dict_name, open_flags, dict_flags); 298 myfree(saved_dict_spec); 299 return (dict); 300 } 301 302 303 /* dict_open3 - open dictionary */ 304 305 DICT *dict_open3(const char *dict_type, const char *dict_name, 306 int open_flags, int dict_flags) 307 { 308 const char *myname = "dict_open"; 309 DICT_OPEN_INFO *dp; 310 DICT *dict; 311 312 if (*dict_type == 0 || *dict_name == 0) 313 msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s:%s\"", 314 dict_type, dict_name); 315 if (dict_open_hash == 0) 316 dict_open_init(); 317 if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0) 318 msg_fatal("unsupported dictionary type: %s", dict_type); 319 if ((dict = dp->open(dict_name, open_flags, dict_flags)) == 0) 320 msg_fatal("opening %s:%s %m", dict_type, dict_name); 321 if (msg_verbose) 322 msg_info("%s: %s:%s", myname, dict_type, dict_name); 323 /* XXX the choice between wait-for-lock or no-wait is hard-coded. */ 324 if (dict->lock_fd >= 0 && (dict_flags & DICT_FLAG_OPEN_LOCK) != 0) { 325 if (dict_flags & DICT_FLAG_LOCK) 326 msg_panic("%s: attempt to open %s:%s with both \"open\" lock and \"access\" lock", 327 myname, dict_type, dict_name); 328 if (myflock(dict->lock_fd, INTERNAL_LOCK, 329 MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0) 330 msg_fatal("%s:%s: unable to get exclusive lock: %m", 331 dict_type, dict_name); 332 } 333 return (dict); 334 } 335 336 /* dict_open_register - register dictionary type */ 337 338 void dict_open_register(const char *type, 339 DICT *(*open) (const char *, int, int)) 340 { 341 const char *myname = "dict_open_register"; 342 DICT_OPEN_INFO *dp; 343 344 if (dict_open_hash == 0) 345 dict_open_init(); 346 if (htable_find(dict_open_hash, type)) 347 msg_panic("%s: dictionary type exists: %s", myname, type); 348 dp = (DICT_OPEN_INFO *) mymalloc(sizeof(*dp)); 349 dp->type = mystrdup(type); 350 dp->open = open; 351 htable_enter(dict_open_hash, dp->type, (char *) dp); 352 } 353 354 /* dict_sort_alpha_cpp - qsort() callback */ 355 356 static int dict_sort_alpha_cpp(const void *a, const void *b) 357 { 358 return (strcmp(((char **) a)[0], ((char **) b)[0])); 359 } 360 361 /* dict_mapnames - return an ARGV of available map_names */ 362 363 ARGV *dict_mapnames() 364 { 365 HTABLE_INFO **ht_info; 366 HTABLE_INFO **ht; 367 DICT_OPEN_INFO *dp; 368 ARGV *mapnames; 369 370 if (dict_open_hash == 0) 371 dict_open_init(); 372 mapnames = argv_alloc(dict_open_hash->used + 1); 373 for (ht_info = ht = htable_list(dict_open_hash); *ht; ht++) { 374 dp = (DICT_OPEN_INFO *) ht[0]->value; 375 argv_add(mapnames, dp->type, ARGV_END); 376 } 377 qsort((void *) mapnames->argv, mapnames->argc, sizeof(mapnames->argv[0]), 378 dict_sort_alpha_cpp); 379 myfree((char *) ht_info); 380 argv_terminate(mapnames); 381 return mapnames; 382 } 383 384 #ifdef TEST 385 386 /* 387 * Proof-of-concept test program. Create, update or read a database. When 388 * the input is a name=value pair, the database is updated, otherwise the 389 * program assumes that the input specifies a lookup key and prints the 390 * corresponding value. 391 */ 392 393 /* System library. */ 394 395 #include <stdlib.h> 396 #include <fcntl.h> 397 #include <unistd.h> 398 #include <signal.h> 399 400 /* Utility library. */ 401 402 #include "vstring.h" 403 #include "vstream.h" 404 #include "msg_vstream.h" 405 #include "vstring_vstream.h" 406 407 static NORETURN usage(char *myname) 408 { 409 msg_fatal("usage: %s type:file read|write|create [fold] [sync]", myname); 410 } 411 412 int main(int argc, char **argv) 413 { 414 VSTRING *keybuf = vstring_alloc(1); 415 VSTRING *inbuf = vstring_alloc(1); 416 DICT *dict; 417 char *dict_name; 418 int open_flags; 419 char *bufp; 420 char *cmd; 421 const char *key; 422 const char *value; 423 int ch; 424 int dict_flags = DICT_FLAG_LOCK | DICT_FLAG_DUP_REPLACE; 425 int n; 426 427 signal(SIGPIPE, SIG_IGN); 428 429 msg_vstream_init(argv[0], VSTREAM_ERR); 430 while ((ch = GETOPT(argc, argv, "v")) > 0) { 431 switch (ch) { 432 default: 433 usage(argv[0]); 434 case 'v': 435 msg_verbose++; 436 break; 437 } 438 } 439 optind = OPTIND; 440 if (argc - optind < 2) 441 usage(argv[0]); 442 if (strcasecmp(argv[optind + 1], "create") == 0) 443 open_flags = O_CREAT | O_RDWR | O_TRUNC; 444 else if (strcasecmp(argv[optind + 1], "write") == 0) 445 open_flags = O_RDWR; 446 else if (strcasecmp(argv[optind + 1], "read") == 0) 447 open_flags = O_RDONLY; 448 else 449 msg_fatal("unknown access mode: %s", argv[2]); 450 for (n = 2; argv[optind + n]; n++) { 451 if (strcasecmp(argv[optind + 2], "fold") == 0) 452 dict_flags |= DICT_FLAG_FOLD_ANY; 453 else if (strcasecmp(argv[optind + 2], "sync") == 0) 454 dict_flags |= DICT_FLAG_SYNC_UPDATE; 455 else 456 usage(argv[0]); 457 } 458 dict_name = argv[optind]; 459 dict = dict_open(dict_name, open_flags, dict_flags); 460 dict_register(dict_name, dict); 461 while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) { 462 bufp = vstring_str(inbuf); 463 if (!isatty(0)) { 464 vstream_printf("> %s\n", bufp); 465 vstream_fflush(VSTREAM_OUT); 466 } 467 if (*bufp == '#') 468 continue; 469 if ((cmd = mystrtok(&bufp, " ")) == 0) { 470 vstream_printf("usage: del key|get key|put key=value|first|next\n"); 471 vstream_fflush(VSTREAM_OUT); 472 continue; 473 } 474 if (dict_changed_name()) 475 msg_warn("dictionary has changed"); 476 key = *bufp ? vstring_str(unescape(keybuf, mystrtok(&bufp, " ="))) : 0; 477 value = mystrtok(&bufp, " ="); 478 if (strcmp(cmd, "del") == 0 && key && !value) { 479 if (dict_del(dict, key)) 480 vstream_printf("%s: not found\n", key); 481 else 482 vstream_printf("%s: deleted\n", key); 483 } else if (strcmp(cmd, "get") == 0 && key && !value) { 484 if ((value = dict_get(dict, key)) == 0) { 485 vstream_printf("%s: %s\n", key, 486 dict_errno == DICT_ERR_RETRY ? 487 "soft error" : "not found"); 488 } else { 489 vstream_printf("%s=%s\n", key, value); 490 } 491 } else if (strcmp(cmd, "put") == 0 && key && value) { 492 dict_put(dict, key, value); 493 vstream_printf("%s=%s\n", key, value); 494 } else if (strcmp(cmd, "first") == 0 && !key && !value) { 495 if (dict_seq(dict, DICT_SEQ_FUN_FIRST, &key, &value) == 0) 496 vstream_printf("%s=%s\n", key, value); 497 else 498 vstream_printf("%s\n", 499 dict_errno == DICT_ERR_RETRY ? 500 "soft error" : "not found"); 501 } else if (strcmp(cmd, "next") == 0 && !key && !value) { 502 if (dict_seq(dict, DICT_SEQ_FUN_NEXT, &key, &value) == 0) 503 vstream_printf("%s=%s\n", key, value); 504 else 505 vstream_printf("%s\n", 506 dict_errno == DICT_ERR_RETRY ? 507 "soft error" : "not found"); 508 } else { 509 vstream_printf("usage: del key|get key|put key=value|first|next\n"); 510 } 511 vstream_fflush(VSTREAM_OUT); 512 } 513 vstring_free(keybuf); 514 vstring_free(inbuf); 515 dict_close(dict); 516 return (0); 517 } 518 519 #endif 520