1 /* $NetBSD: dict_thash.c,v 1.1.1.2 2013/01/02 18:59:12 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* dict_thash 3 6 /* SUMMARY 7 /* dictionary manager interface to hashed flat text files 8 /* SYNOPSIS 9 /* #include <dict_thash.h> 10 /* 11 /* DICT *dict_thash_open(path, open_flags, dict_flags) 12 /* const char *name; 13 /* const char *path; 14 /* int open_flags; 15 /* int dict_flags; 16 /* DESCRIPTION 17 /* dict_thash_open() opens the named flat text file, creates 18 /* an in-memory hash table, and makes it available via the 19 /* generic interface described in dict_open(3). The input 20 /* format is as with postmap(1). 21 /* DIAGNOSTICS 22 /* Fatal errors: cannot open file, out of memory. 23 /* SEE ALSO 24 /* dict(3) generic dictionary manager 25 /* LICENSE 26 /* .ad 27 /* .fi 28 /* The Secure Mailer license must be distributed with this software. 29 /* AUTHOR(S) 30 /* Wietse Venema 31 /* IBM T.J. Watson Research 32 /* P.O. Box 704 33 /* Yorktown Heights, NY 10598, USA 34 /*--*/ 35 36 /* System library. */ 37 38 #include <sys_defs.h> 39 #include <sys/stat.h> 40 #include <ctype.h> 41 #include <string.h> 42 43 /* Utility library. */ 44 45 #include <msg.h> 46 #include <mymalloc.h> 47 #include <htable.h> 48 #include <iostuff.h> 49 #include <vstring.h> 50 #include <stringops.h> 51 #include <readlline.h> 52 #include <dict.h> 53 #include <dict_thash.h> 54 #include <warn_stat.h> 55 56 /* Application-specific. */ 57 58 typedef struct { 59 DICT dict; /* generic members */ 60 HTABLE *table; /* in-memory hash */ 61 HTABLE_INFO **info; /* for iterator */ 62 HTABLE_INFO **cursor; /* ditto */ 63 } DICT_THASH; 64 65 #define STR vstring_str 66 67 /* dict_thash_lookup - find database entry */ 68 69 static const char *dict_thash_lookup(DICT *dict, const char *name) 70 { 71 DICT_THASH *dict_thash = (DICT_THASH *) dict; 72 const char *result = 0; 73 74 /* 75 * Optionally fold the key. 76 */ 77 if (dict->flags & DICT_FLAG_FOLD_FIX) { 78 if (dict->fold_buf == 0) 79 dict->fold_buf = vstring_alloc(10); 80 vstring_strcpy(dict->fold_buf, name); 81 name = lowercase(vstring_str(dict->fold_buf)); 82 } 83 84 /* 85 * Look up the value. 86 */ 87 result = htable_find(dict_thash->table, name); 88 89 DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, result); 90 } 91 92 /* dict_thash_sequence - traverse the dictionary */ 93 94 static int dict_thash_sequence(DICT *dict, int function, 95 const char **key, const char **value) 96 { 97 const char *myname = "dict_thash_sequence"; 98 DICT_THASH *dict_thash = (DICT_THASH *) dict; 99 100 /* 101 * Determine and execute the seek function. 102 */ 103 switch (function) { 104 case DICT_SEQ_FUN_FIRST: 105 if (dict_thash->info == 0) 106 dict_thash->info = htable_list(dict_thash->table); 107 dict_thash->cursor = dict_thash->info; 108 break; 109 case DICT_SEQ_FUN_NEXT: 110 if (dict_thash->cursor[0]) 111 dict_thash->cursor += 1; 112 break; 113 default: 114 msg_panic("%s: invalid function: %d", myname, function); 115 } 116 117 /* 118 * Return the entry under the cursor. 119 */ 120 if (dict_thash->cursor[0]) { 121 *key = dict_thash->cursor[0]->key; 122 *value = dict_thash->cursor[0]->value; 123 DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS); 124 } else { 125 *key = 0; 126 *value = 0; 127 DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL); 128 } 129 } 130 131 /* dict_thash_close - disassociate from data base */ 132 133 static void dict_thash_close(DICT *dict) 134 { 135 DICT_THASH *dict_thash = (DICT_THASH *) dict; 136 137 htable_free(dict_thash->table, myfree); 138 if (dict_thash->info) 139 myfree((char *) dict_thash->info); 140 if (dict->fold_buf) 141 vstring_free(dict->fold_buf); 142 dict_free(dict); 143 } 144 145 /* dict_thash_open - open flat text data base */ 146 147 DICT *dict_thash_open(const char *path, int open_flags, int dict_flags) 148 { 149 DICT_THASH *dict_thash; 150 VSTREAM *fp; 151 struct stat st; 152 time_t before; 153 time_t after; 154 VSTRING *line_buffer = 0; 155 int lineno; 156 char *key; 157 char *value; 158 HTABLE *table; 159 HTABLE_INFO *ht; 160 161 /* 162 * Sanity checks. 163 */ 164 if (open_flags != O_RDONLY) 165 return (dict_surrogate(DICT_TYPE_THASH, path, open_flags, dict_flags, 166 "%s:%s map requires O_RDONLY access mode", 167 DICT_TYPE_THASH, path)); 168 169 /* 170 * Read the flat text file into in-memory hash. Read the file again if it 171 * may have changed while we were reading. 172 */ 173 for (before = time((time_t *) 0); /* see below */ ; before = after) { 174 if ((fp = vstream_fopen(path, open_flags, 0644)) == 0) { 175 return (dict_surrogate(DICT_TYPE_THASH, path, open_flags, dict_flags, 176 "open database %s: %m", path)); 177 } 178 if (line_buffer == 0) 179 line_buffer = vstring_alloc(100); 180 lineno = 0; 181 table = htable_create(13); 182 while (readlline(line_buffer, fp, &lineno)) { 183 184 /* 185 * Split on the first whitespace character, then trim leading and 186 * trailing whitespace from key and value. 187 */ 188 key = STR(line_buffer); 189 value = key + strcspn(key, " \t\r\n"); 190 if (*value) 191 *value++ = 0; 192 while (ISSPACE(*value)) 193 value++; 194 trimblanks(key, 0)[0] = 0; 195 trimblanks(value, 0)[0] = 0; 196 197 /* 198 * Enforce the "key whitespace value" format. Disallow missing 199 * keys or missing values. 200 */ 201 if (*key == 0 || *value == 0) { 202 msg_warn("%s, line %d: expected format: key whitespace value" 203 " -- ignoring this line", path, lineno); 204 continue; 205 } 206 if (key[strlen(key) - 1] == ':') 207 msg_warn("%s, line %d: record is in \"key: value\" format;" 208 " is this an alias file?", path, lineno); 209 210 /* 211 * Optionally fold the key. 212 */ 213 if (dict_flags & DICT_FLAG_FOLD_FIX) 214 lowercase(key); 215 216 /* 217 * Store the value under the key. Handle duplicates 218 * appropriately. 219 */ 220 if ((ht = htable_locate(table, key)) != 0) { 221 if (dict_flags & DICT_FLAG_DUP_IGNORE) { 222 /* void */ ; 223 } else if (dict_flags & DICT_FLAG_DUP_REPLACE) { 224 myfree(ht->value); 225 ht->value = mystrdup(value); 226 } else if (dict_flags & DICT_FLAG_DUP_WARN) { 227 msg_warn("%s, line %d: duplicate entry: \"%s\"", 228 path, lineno, key); 229 } else { 230 msg_fatal("%s, line %d: duplicate entry: \"%s\"", 231 path, lineno, key); 232 } 233 } else { 234 htable_enter(table, key, mystrdup(value)); 235 } 236 } 237 238 /* 239 * See if the source file is hot. 240 */ 241 if (fstat(vstream_fileno(fp), &st) < 0) 242 msg_fatal("fstat %s: %m", path); 243 if (vstream_fclose(fp)) 244 msg_fatal("read %s: %m", path); 245 after = time((time_t *) 0); 246 if (st.st_mtime < before - 1 || st.st_mtime > after) 247 break; 248 249 /* 250 * Yes, it is hot. Discard the result and read the file again. 251 */ 252 htable_free(table, myfree); 253 if (msg_verbose > 1) 254 msg_info("pausing to let file %s cool down", path); 255 doze(300000); 256 } 257 vstring_free(line_buffer); 258 259 /* 260 * Create the in-memory table. 261 */ 262 dict_thash = (DICT_THASH *) 263 dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash)); 264 dict_thash->dict.lookup = dict_thash_lookup; 265 dict_thash->dict.sequence = dict_thash_sequence; 266 dict_thash->dict.close = dict_thash_close; 267 dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED; 268 if (dict_flags & DICT_FLAG_FOLD_FIX) 269 dict_thash->dict.fold_buf = vstring_alloc(10); 270 dict_thash->info = 0; 271 dict_thash->table = table; 272 dict_thash->dict.owner.uid = st.st_uid; 273 dict_thash->dict.owner.status = (st.st_uid != 0); 274 275 return (DICT_DEBUG (&dict_thash->dict)); 276 } 277