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