]> andersk Git - moira.git/blame_incremental - server/cache.pc
Allow hostname to start with a digit in hostname_check, but disallow it
[moira.git] / server / cache.pc
... / ...
CommitLineData
1/* $Id$
2 *
3 * Keep a cache of name/id mappings
4 *
5 * Copyright (C) 1989-1998 by the Massachusetts Institute of Technology
6 * For copying and distribution information, please see the file
7 * <mit-copyright.h>.
8 */
9
10#include <mit-copyright.h>
11#include "mr_server.h"
12#include "query.h"
13
14#include <stdlib.h>
15#include <string.h>
16
17EXEC SQL INCLUDE sqlca;
18
19RCSID("$Header$");
20
21EXEC SQL WHENEVER SQLERROR DO dbmserr();
22
23extern char *whoami;
24
25/* Cache parameters: */
26#define CACHESIZE 101 /* number of cache slots */
27
28struct item {
29 char name[MAX_FIELD_WIDTH];
30 enum tables type;
31 int nhash;
32 int id;
33 struct item *next;
34 struct item *prev;
35 struct item *trans; /* keep track of transactions */
36};
37
38static struct item cachehead;
39static int cachesize;
40
41/* statistics counters */
42int cachehits = 0, cachemisses = 0;
43
44int hashname(char *name, enum tables type);
45
46/* Name hash function. */
47
48int hashname(char *name, enum tables type)
49{
50 int val = type;
51
52 while (*name)
53 val = (val << 5) - val + *name++ - '`';
54 return val;
55}
56
57
58/* Initialize the cache, flushing any old data, and report the statistics
59 * if the cache was previously in use.
60 */
61
62void flush_cache(void)
63{
64 struct item *i;
65
66 if (cachehits + cachemisses != 0)
67 {
68 com_err(whoami, 0, "Flushing cache; %d hits, %d misses, %d%% hit rate",
69 cachehits, cachemisses,
70 (100 * cachehits) / (cachehits + cachemisses));
71 }
72 else
73 cachehead.next = cachehead.prev = &cachehead;
74 cachehits = cachemisses = cachesize = 0;
75 for (i = cachehead.next; i != &cachehead; i = i->next)
76 {
77 if (i->prev != &cachehead)
78 free(i->prev);
79 }
80 if (cachehead.prev != &cachehead)
81 free(cachehead.prev);
82 cachehead.next = cachehead.prev = &cachehead;
83 cachehead.trans = NULL;
84}
85
86
87/* Do a name to ID translation. id will be updated with the answer if
88 * it is available, and as a side effect the cache is updated.
89 */
90
91int name_to_id(char *name, enum tables type, int *id)
92{
93 struct item *i, *t;
94 EXEC SQL BEGIN DECLARE SECTION;
95 char *iname;
96 int j, h;
97 EXEC SQL END DECLARE SECTION;
98
99 h = hashname(name, type);
100 for (i = cachehead.next; i != &cachehead; i = i->next)
101 {
102 if (i->nhash != h || strcmp(name, i->name) || type != i->type)
103 continue;
104 *id = i->id;
105 cachehits++;
106 i->next->prev = i->prev;
107 i->prev->next = i->next;
108 i->next = cachehead.next;
109 i->prev = &cachehead;
110 cachehead.next->prev = i;
111 cachehead.next = i;
112 return MR_SUCCESS;
113 }
114
115 cachemisses++;
116 iname = name;
117
118 switch (type)
119 {
120 case USERS_TABLE:
121 if (strchr(iname, '@') || (strlen(iname) > 8))
122 {
123 sqlca.sqlcode = SQL_NO_MATCH;
124 break;
125 }
126 EXEC SQL SELECT users_id INTO :j FROM users WHERE login = :iname;
127 break;
128 case LIST_TABLE:
129 EXEC SQL SELECT list_id INTO :j FROM list WHERE name = :iname;
130 break;
131 case MACHINE_TABLE:
132 EXEC SQL SELECT mach_id INTO :j FROM machine WHERE name = UPPER(:iname);
133 break;
134 case SUBNET_TABLE:
135 EXEC SQL SELECT snet_id INTO :j FROM subnet WHERE name = UPPER(:iname);
136 break;
137 case CLUSTERS_TABLE:
138 EXEC SQL SELECT clu_id INTO :j FROM clusters WHERE name = :iname;
139 break;
140 case FILESYS_TABLE:
141 EXEC SQL SELECT filsys_id INTO :j FROM filesys WHERE label = :iname;
142 break;
143 case STRINGS_TABLE:
144 if (!iname[0]) /* special-case empty string */
145 {
146 *id = 0;
147 return MR_SUCCESS;
148 }
149 EXEC SQL SELECT string_id INTO :j FROM strings WHERE string = :iname;
150 break;
151 default:
152 return MR_INTERNAL;
153 }
154 if (sqlca.sqlcode == SQL_NO_MATCH)
155 return MR_NO_MATCH;
156 if (sqlca.sqlerrd[2] > 1)
157 return MR_NOT_UNIQUE;
158 if (sqlca.sqlcode)
159 return MR_DBMS_ERR;
160 *id = j;
161 if (name[0] == '#' && type != USERS_TABLE)
162 return MR_SUCCESS;
163 if (cachesize < CACHESIZE)
164 {
165 i = malloc(sizeof(struct item));
166 if (!i)
167 return MR_SUCCESS;
168 cachesize++;
169 }
170 else
171 {
172 i = cachehead.prev;
173 cachehead.prev = i->prev;
174 i->prev->next = &cachehead;
175 }
176 strcpy(i->name, name);
177 i->type = type;
178 i->nhash = h;
179 i->id = j;
180 i->next = cachehead.next;
181 i->prev = &cachehead;
182 i->trans = NULL;
183 cachehead.next->prev = i;
184 cachehead.next = i;
185 /* find the end of the transaction chain & add this item */
186 for (t = &cachehead; t->trans && t != i; t = t->trans)
187 ;
188 if (t != i)
189 t->trans = i;
190 return MR_SUCCESS;
191}
192
193
194/* Perform an ID to name mapping. name should be a pointer to a pointer to
195 * malloc'ed data. The buffer it refers to will be freed, and a new buffer
196 * allocated with the answer.
197 */
198
199int id_to_name(int id, enum tables type, char **name)
200{
201 struct item *i, *t;
202 EXEC SQL BEGIN DECLARE SECTION;
203 char iname[MAX_FIELD_WIDTH];
204 int j;
205 EXEC SQL END DECLARE SECTION;
206
207 for (i = cachehead.next; i != &cachehead; i = i->next)
208 {
209 if (i->id != id || type != i->type)
210 continue;
211 free(*name);
212 *name = xstrdup(i->name);
213 cachehits++;
214 i->next->prev = i->prev;
215 i->prev->next = i->next;
216 i->next = cachehead.next;
217 i->prev = &cachehead;
218 cachehead.next->prev = i;
219 cachehead.next = i;
220 return MR_SUCCESS;
221 }
222
223 cachemisses++;
224 j = id;
225
226 switch (type)
227 {
228 case USERS_TABLE:
229 EXEC SQL SELECT login INTO :iname FROM users WHERE users_id = :j;
230 break;
231 case LIST_TABLE:
232 EXEC SQL SELECT name INTO :iname FROM list WHERE list_id = :j;
233 break;
234 case MACHINE_TABLE:
235 EXEC SQL SELECT name INTO :iname FROM machine WHERE mach_id = :j;
236 break;
237 case SUBNET_TABLE:
238 EXEC SQL SELECT name INTO :iname FROM subnet WHERE snet_id = :j;
239 break;
240 case CLUSTERS_TABLE:
241 EXEC SQL SELECT name INTO :iname FROM clusters WHERE clu_id = :j;
242 break;
243 case FILESYS_TABLE:
244 EXEC SQL SELECT label INTO :iname FROM filesys WHERE filsys_id = :j;
245 break;
246 case STRINGS_TABLE:
247 EXEC SQL SELECT string INTO :iname FROM strings WHERE string_id = :j;
248 break;
249 default:
250 return MR_INTERNAL;
251 }
252 if (sqlca.sqlcode == SQL_NO_MATCH)
253 {
254 free(*name);
255 sprintf(iname, "#%d", j);
256 *name = xstrdup(iname);
257 return MR_NO_MATCH;
258 }
259 if (sqlca.sqlerrd[2] > 1)
260 return MR_INTERNAL;
261 if (sqlca.sqlcode)
262 return MR_DBMS_ERR;
263 free(*name);
264 *name = xstrdup(strtrim(iname));
265 if (**name == '#' && type != USERS_TABLE)
266 return MR_SUCCESS;
267 if (cachesize < CACHESIZE)
268 {
269 i = malloc(sizeof(struct item));
270 if (!i)
271 return MR_SUCCESS;
272 cachesize++;
273 }
274 else
275 {
276 i = cachehead.prev;
277 cachehead.prev = i->prev;
278 i->prev->next = &cachehead;
279 }
280 strcpy(i->name, *name);
281 i->type = type;
282 i->nhash = hashname(*name, type);
283 i->id = id;
284 i->next = cachehead.next;
285 i->prev = &cachehead;
286 i->trans = NULL;
287 cachehead.next->prev = i;
288 cachehead.next = i;
289 /* find the end of the transaction chain & add this item */
290 for (t = &cachehead; t->trans && t != i; t = t->trans)
291 ;
292 if (t != i)
293 t->trans = i;
294 return MR_SUCCESS;
295}
296
297
298/* Explicitly add something to the cache without doing a lookup in the
299 * database.
300 */
301
302int cache_entry(char *name, enum tables type, int id)
303{
304 struct item *i, *t;
305
306 for (i = cachehead.next; i != &cachehead; i = i->next)
307 {
308 if (i->id == id && i->type == type)
309 {
310 if (strcmp(i->name, name))
311 {
312 strcpy(i->name, name);
313 i->nhash = hashname(name, type);
314 }
315 return MR_SUCCESS;
316 }
317 }
318 if (cachesize < CACHESIZE)
319 {
320 i = malloc(sizeof(struct item));
321 if (!i)
322 return MR_SUCCESS;
323 cachesize++;
324 }
325 else
326 {
327 i = cachehead.prev;
328 cachehead.prev = i->prev;
329 i->prev->next = &cachehead;
330 }
331 strcpy(i->name, name);
332 i->type = type;
333 i->nhash = hashname(name, type);
334 i->id = id;
335 i->next = cachehead.next;
336 i->prev = &cachehead;
337 i->trans = NULL;
338 cachehead.next->prev = i;
339 cachehead.next = i;
340 /* find the end of the transaction chain & add this item */
341 for (t = &cachehead; t->trans && t != i; t = t->trans)
342 ;
343 if (t != i)
344 t->trans = i;
345 return MR_SUCCESS;
346}
347
348
349/* Flush something that may or may not already be in the cache. */
350
351void flush_name(char *name, enum tables type)
352{
353 struct item *i;
354
355 /* first clear it out of the transaction chain */
356 for (i = &cachehead; i && i->trans; i = i->trans)
357 {
358 if (!strcmp(name, i->trans->name) && type == i->trans->type)
359 i->trans = i->trans->trans;
360 }
361
362 /* now remove it */
363 for (i = cachehead.next; i != &cachehead; i = i->next)
364 {
365 if (!strcmp(name, i->name) && type == i->type)
366 {
367 cachesize--;
368 i->next->prev = i->prev;
369 i->prev->next = i->next;
370 free(i);
371 break;
372 }
373 }
374}
375
376
377/* Called when a transaction is committed to also commit any cache changes.
378 * Just throws away the list of cache changes for this transaction.
379 */
380
381void cache_commit(void)
382{
383 cachehead.trans = NULL;
384}
385
386
387/* Called whan a transaction is aborted to throw away any cache changes
388 * from that transaction.
389 */
390
391void cache_abort(void)
392{
393 struct item *i, *t;
394
395 for (i = cachehead.trans; i; i = t)
396 {
397 t = i->trans;
398 flush_name(i->name, i->type);
399 }
400 cachehead.trans = NULL;
401}
This page took 0.03949 seconds and 5 git commands to generate.