1 /*
2 OWFS -- One-Wire filesystem
3 OWHTTPD -- One-Wire Web Server
4 Written 2003 Paul H Alfille
5 email: paul.alfille@gmail.com
6 Released under the GPL
7 See the header file: ow.h for full attribution
8 1wire/iButton system from Dallas Semiconductor
9 */
10
11 // 8/18/2004 -- applied Serg Oskin's correction
12 // 8/20/2004 -- changed everything, specifically no longer using db, tsearch instead!
13 #include <config.h>
14 #include "owfs_config.h"
15 #include "ow.h"
16 #include "ow_counters.h"
17 #include "ow_connection.h"
18
19 //#define CACHE_DEBUG
20
21 #include <limits.h>
22
23 #define EXTENSION_INTERNAL -2
24
25 // Directories are bus-specific, but buses are dynamic
26 // The numbering is sequential, however, so use that and an arbitrary
27 // generic unique address for directories.
28 int DirMarkerLoc ;
29 void * Directory_Marker = &DirMarkerLoc ;
30 int AuxDirMarkerLoc ;
31 void * AuxDirectory_Marker = &AuxDirMarkerLoc ;
32 int MainDirMarkerLoc ;
33 void * MainDirectory_Marker = &MainDirMarkerLoc ;
34
35 // Devices are sn-based
36 // generic unique address for devices.
37 int DevMarkerLoc ;
38 void * Device_Marker = &DevMarkerLoc ;
39
40 // Aliases are sn-based
41 // generic unique address for devices.
42 int AliasMarkerLoc ;
43 void * Alias_Marker = &AliasMarkerLoc ;
44
45
46 /* Put the globals into a struct to declutter the namespace */
47 struct cache_data {
48 void *temporary_tree_new; // current cache database
49 void *temporary_tree_old; // older cache database
50 void *persistent_tree; // persistent database
51 void *temporary_alias_tree_new; // current cache database
52 void *temporary_alias_tree_old; // older cache database
53 void *persistent_alias_tree; // persistent database
54 size_t old_ram_size; // cache size
55 size_t new_ram_size; // cache size
56 time_t time_retired; // start time of older
57 time_t time_to_kill; // deathtime of older
58 time_t retired_lifespan; // lifetime of older
59 UINT added; // items added
60 };
61 static struct cache_data cache;
62
63 /* Cache elements are placed in a Red/Black binary tree
64 -- standard glibc implementation
65 -- use gnu tdestroy extension
66 -- compatibility implementation included
67 Cache key has 3 components: (struct tree_key)
68 sn -- 8 byte serial number of 1-wire slave
69 p -- pointer to internal structure like filetype (guaranteed unique if non-portable)
70 extension -- integer used for array elements
71 Cache node is the entire node not including data payload (struct key_node)
72 key -- sorted component as above
73 expires -- the time that the element is no longer valid
74 dsize -- length in bytes of trailing data
75 Cache data is the actual data
76 allocated at same call as cache node
77 access via macro TREE_DATA
78 freed when cache node is freed
79 Note: This means that cache data must be a copy of program data
80 both on creation and retrieval
81 */
82
83 /* Key used for sorting/retrieving cache data
84 sn is for device serial number
85 p is a pointer to filetype, or other things (guaranteed unique and fast lookup
86 extension is used for indexed array properties
87 */
88 struct tree_key {
89 BYTE sn[8];
90 void *p;
91 int extension;
92 };
93
94 /* How we organize the data in the binary tree used for cache storage
95 A key (see above)
96 An expiration time
97 And a size in bytes
98 Actaully size bytes follows with the data
99 */
100 struct tree_node {
101 struct tree_key tk;
102 time_t expires;
103 size_t dsize;
104 };
105
106 struct alias_tree_node {
107 size_t size;
108 time_t expires;
109 union {
110 INDEX_OR_ERROR bus;
111 BYTE sn[SERIAL_NUMBER_SIZE];
112 } ;
113 };
114
115 /* Bad bad C library */
116 /* implementation of tfind, tsearch returns an opaque structure */
117 /* you have to know that the first element is a pointer to your data */
118 struct tree_opaque {
119 struct tree_node *key;
120 void *other;
121 };
122
123 #define TREE_DATA(tn) ( (BYTE *)(tn) + sizeof(struct tree_node) )
124 #define CONST_TREE_DATA(tn) ( (const BYTE *)(tn) + sizeof(struct tree_node) )
125
126 #define ALIAS_TREE_DATA(atn) ( (ASCII *)(atn) + sizeof(struct alias_tree_node) )
127 #define CONST_ALIAS_TREE_DATA(atn) ( (const ASCII *)(atn) + sizeof(struct alias_tree_node) )
128
129 enum cache_task_return { ctr_ok, ctr_not_found, ctr_expired, ctr_size_mismatch, } ;
130
131 static void FlipTree( void ) ;
132
133 static int IsThisPersistent( const struct parsedname * pn ) ;
134
135 static GOOD_OR_BAD Cache_Add(const void *data, const size_t datasize, const struct parsedname *pn);
136 static GOOD_OR_BAD Cache_Add_Common(struct tree_node *tn);
137 static GOOD_OR_BAD Cache_Add_Persistent(struct tree_node *tn);
138
139 static enum cache_task_return Cache_Get_Common(void *data, size_t * dsize, time_t * duration, const struct tree_node *tn);
140 static enum cache_task_return Cache_Get_Common_Dir(struct dirblob *db, time_t * duration, const struct tree_node *tn);
141 static enum cache_task_return Cache_Get_Persistent(void *data, size_t * dsize, time_t * duration, const struct tree_node *tn);
142
143 static GOOD_OR_BAD Cache_Get_Simultaneous(const struct internal_prop *ip, struct one_wire_query *owq) ;
144 static GOOD_OR_BAD Cache_Get_Internal(void *data, size_t * dsize, const struct internal_prop *ip, const struct parsedname *pn);
145 static GOOD_OR_BAD Cache_Get_Strict(void *data, size_t dsize, const struct parsedname *pn);
146
147 static void Cache_Del(const struct parsedname *pn) ;
148 static GOOD_OR_BAD Cache_Del_Common(const struct tree_node *tn);
149 static GOOD_OR_BAD Cache_Del_Persistent(const struct tree_node *tn);
150
151 static void Cache_Add_Alias_Common(struct alias_tree_node *atn);
152 static INDEX_OR_ERROR Cache_Get_Alias_Common( struct alias_tree_node * atn) ;
153
154 static void Cache_Add_Alias_SN(const ASCII * alias_name, const BYTE * sn) ;
155 static void Cache_Del_Alias_SN(const ASCII * alias_name) ;
156
157 static void Cache_Add_Alias_Persistent(struct alias_tree_node *atn);
158 static GOOD_OR_BAD Cache_Get_Alias_Persistent( BYTE * sn, struct alias_tree_node * atn);
159 static void Cache_Del_Alias_Persistent( struct alias_tree_node * atn) ;
160
161 static GOOD_OR_BAD Add_Stat(struct cache_stats *scache, GOOD_OR_BAD result);
162 static GOOD_OR_BAD Get_Stat(struct cache_stats *scache, const enum cache_task_return result);
163 static void Del_Stat(struct cache_stats *scache, const int result);
164
165 static int tree_compare(const void *a, const void *b);
166 static time_t TimeOut(const enum fc_change change);
167 static void Aliaslistaction(const void *node, const VISIT which, const int depth) ;
168 static void LoadTK( const BYTE * sn, void * p, int extension, struct tree_node * tn ) ;
169
170 /* used for the sort/search b-tree routines */
171 /* big voodoo pointer fuss to just do a standard memory compare of the "key" */
tree_compare(const void * a,const void * b)172 static int tree_compare(const void *a, const void *b)
173 {
174 return memcmp(&((const struct tree_node *) a)->tk, &((const struct tree_node *) b)->tk, sizeof(struct tree_key));
175 }
176
177 /* used for the sort/search b-tree routines */
178 /* big voodoo pointer fuss to just do a standard memory compare of the "key" */
alias_tree_compare(const void * a,const void * b)179 static int alias_tree_compare(const void *a, const void *b)
180 {
181 int da = ((const struct alias_tree_node *) a)->size ;
182 int d = da - ((const struct alias_tree_node *) b)->size ;
183
184 if ( d != 0 ) {
185 return d ;
186 }
187 return memcmp( CONST_ALIAS_TREE_DATA((const struct alias_tree_node *) a), CONST_ALIAS_TREE_DATA((const struct alias_tree_node *) b), da);
188 }
189
190 /* Gives the delay for a given property type */
191 /* Values in seconds (as defined in Globals structure and modified by command line and "settings") */
TimeOut(const enum fc_change change)192 static time_t TimeOut(const enum fc_change change)
193 {
194 switch (change) {
195 case fc_second:
196 case fc_persistent: /* arbitrary non-zero */
197 return 1;
198 case fc_volatile:
199 case fc_simultaneous_temperature:
200 case fc_simultaneous_voltage:
201 return Globals.timeout_volatile;
202 case fc_stable:
203 case fc_read_stable:
204 return Globals.timeout_stable;
205 case fc_presence:
206 return Globals.timeout_presence;
207 case fc_directory:
208 return Globals.timeout_directory;
209 case fc_link:
210 case fc_page:
211 case fc_subdir:
212 default: /* static or statistic */
213 return 0;
214 }
215 }
216
217 #ifdef CACHE_DEBUG
218 /* debug routine -- shows a table */
219 /* Run it as twalk(dababase, tree_show ) */
node_show(struct tree_node * tn)220 static void node_show(struct tree_node *tn)
221 {
222 int i;
223 char b[26];
224 ctime_r(&tn->expires, b);
225 fprintf(stderr,"\tNode " SNformat
226 " pointer=%p extension=%d length=%d start=%p expires=%s", SNvar(tn->tk.sn), tn->tk.p, tn->tk.extension, tn->dsize, tn, b ? b : "null\n");
227 for (i = 0; i < sizeof(struct tree_key); ++i) {
228 fprintf(stderr,"%.2X ", ((uint8_t *) tn)[i]);
229 }
230 fprintf(stderr,"\n");
231 }
232
tree_show(const void * node,const VISIT which,const int depth)233 static void tree_show(const void *node, const VISIT which, const int depth)
234 {
235 const struct tree_node *tn = *(struct tree_node * *) node;
236 (void) depth;
237 if (node) {
238 switch (which) {
239 case leaf:
240 case postorder:
241 node_show(tn);
242 // fall through
243 default:
244 break;
245 }
246 } else {
247 fprintf(stderr,"Node empty\n");
248 }
249 }
new_tree(void)250 static void new_tree(void)
251 {
252 fprintf(stderr,"Walk the new tree:\n");
253 twalk(cache.temporary_tree_new, tree_show);
254 }
255 #else /* CACHE_DEBUG */
256 #define new_tree()
257 #define node_show(tn)
258 #endif /* CACHE_DEBUG */
259
IsThisPersistent(const struct parsedname * pn)260 static int IsThisPersistent( const struct parsedname * pn )
261 {
262 return ( (pn->selected_filetype->change==fc_persistent) || get_busmode(pn->selected_connection)==bus_mock ) ;
263 }
264
265 /* DB cache creation code */
266 /* Note: done in single-threaded mode so locking not yet needed */
Cache_Open(void)267 void Cache_Open(void)
268 {
269 memset(&cache, 0, sizeof(struct cache_data));
270
271 cache.retired_lifespan = TimeOut(fc_stable);
272 if (cache.retired_lifespan > 3600) {
273 cache.retired_lifespan = 3600; /* 1 hour tops */
274 }
275
276 // Flip once (at start) to set up old tree.
277 FlipTree() ;
278 }
279
280 /* Note: done in a simgle single thread mode so locking not needed */
Cache_Close(void)281 void Cache_Close(void)
282 {
283 Cache_Clear() ;
284 SAFETDESTROY( cache.persistent_tree, owfree_func);
285 SAFETDESTROY( cache.persistent_alias_tree, owfree_func);
286 }
287
288 /* Moves new to old tree, initializes new tree, and clears former old tree location */
FlipTree(void)289 static void FlipTree( void )
290 {
291 void * flip = cache.temporary_tree_old; // old old saved for later clearing
292 void * flip_alias = cache.temporary_alias_tree_old; // old old saved for later clearing
293
294 /* Flip caches! old = new. New truncated, reset time and counters and flag */
295 LEVEL_DEBUG("Flipping cache tree (purging timed-out data)");
296
297 // move "new" pointers to "old"
298 cache.temporary_tree_old = cache.temporary_tree_new;
299 cache.old_ram_size = cache.new_ram_size;
300 cache.temporary_alias_tree_old = cache.temporary_alias_tree_new;
301
302 // New cache setup
303 cache.temporary_tree_new = NULL;
304 cache.temporary_alias_tree_new = NULL;
305 cache.new_ram_size = 0;
306 cache.added = 0;
307
308 // set up "old" cache times
309 cache.time_retired = NOW_TIME;
310 cache.time_to_kill = cache.time_retired + cache.retired_lifespan;
311
312 // delete really old tree
313 LEVEL_DEBUG("flip cache. tdestroy() will be called.");
314 SAFETDESTROY( flip, owfree_func);
315 SAFETDESTROY( flip_alias, owfree_func);
316 STATLOCK;
317 ++cache_flips; /* statistics */
318 memcpy(&old_avg, &new_avg, sizeof(struct average));
319 AVERAGE_CLEAR(&new_avg);
320 STATUNLOCK;
321 }
322
323 /* Clear the cache (a change was made that might give stale information) */
Cache_Clear(void)324 void Cache_Clear(void)
325 {
326 CACHE_WLOCK;
327 FlipTree() ;
328 FlipTree() ;
329 CACHE_WUNLOCK;
330 }
331
332 /* Wrapper to perform a cache function and add statistics */
Add_Stat(struct cache_stats * scache,GOOD_OR_BAD result)333 static GOOD_OR_BAD Add_Stat(struct cache_stats *scache, GOOD_OR_BAD result)
334 {
335 if ( GOOD(result) ) {
336 STAT_ADD1(scache->adds);
337 }
338 return result;
339 }
340
341 /* Higher level add of a one-wire-query object */
OWQ_Cache_Add(const struct one_wire_query * owq)342 GOOD_OR_BAD OWQ_Cache_Add(const struct one_wire_query *owq)
343 {
344 const struct parsedname *pn = PN(owq);
345 if (pn->extension == EXTENSION_ALL) {
346 switch (pn->selected_filetype->format) {
347 case ft_ascii:
348 case ft_vascii:
349 case ft_alias:
350 case ft_binary:
351 return gbBAD; // cache of string arrays not supported
352 case ft_integer:
353 case ft_unsigned:
354 case ft_yesno:
355 case ft_date:
356 case ft_float:
357 case ft_pressure:
358 case ft_temperature:
359 case ft_tempgap:
360 LEVEL_DEBUG("Adding data for %s", SAFESTRING(pn->path) );
361 return Cache_Add(OWQ_array(owq), (pn->selected_filetype->ag->elements) * sizeof(union value_object), pn);
362 default:
363 return gbBAD;
364 }
365 } else {
366 switch (pn->selected_filetype->format) {
367 case ft_ascii:
368 case ft_vascii:
369 case ft_alias:
370 case ft_binary:
371 if (OWQ_offset(owq) > 0) {
372 return gbBAD;
373 }
374 LEVEL_DEBUG("Adding data for %s", SAFESTRING(pn->path) );
375 return Cache_Add(OWQ_buffer(owq), OWQ_length(owq), pn);
376 case ft_integer:
377 case ft_unsigned:
378 case ft_yesno:
379 case ft_date:
380 case ft_float:
381 case ft_pressure:
382 case ft_temperature:
383 case ft_tempgap:
384 LEVEL_DEBUG("Adding data for %s", SAFESTRING(pn->path) );
385 return Cache_Add(&OWQ_val(owq), sizeof(union value_object), pn);
386 default:
387 return gbBAD;
388 }
389 }
390 }
391
392 /* Add an item to the cache */
393 /* return 0 if good, 1 if not */
Cache_Add(const void * data,const size_t datasize,const struct parsedname * pn)394 static GOOD_OR_BAD Cache_Add(const void *data, const size_t datasize, const struct parsedname *pn)
395 {
396 struct tree_node *tn;
397 time_t duration;
398 int persistent ;
399
400 if (!pn || IsAlarmDir(pn)) {
401 return gbGOOD; // do check here to avoid needless processing
402 }
403
404 // Special handling of Mock
405 persistent = IsThisPersistent(pn) ;
406 if ( persistent ) {
407 duration = 1 ;
408 } else {
409 duration = TimeOut(pn->selected_filetype->change);
410 if (duration <= 0) {
411 return gbGOOD; /* in case timeout set to 0 */
412 }
413 }
414
415 // allocate space for the node and data
416 tn = (struct tree_node *) owmalloc(sizeof(struct tree_node) + datasize);
417 if (!tn) {
418 return gbBAD;
419 }
420
421 LEVEL_DEBUG(SNformat " size=%d", SNvar(pn->sn), (int) datasize);
422
423 // populate the node structure with data
424 LoadTK( pn->sn, pn->selected_filetype, pn->extension, tn );
425 tn->expires = duration + NOW_TIME;
426 tn->dsize = datasize;
427 if (datasize) {
428 memcpy(TREE_DATA(tn), data, datasize);
429 }
430 return persistent ?
431 Add_Stat(&cache_pst, Cache_Add_Persistent(tn)) :
432 Add_Stat(&cache_ext, Cache_Add_Common(tn)) ;
433 }
434
435 /* Add a directory entry to the cache */
436 /* return 0 if good, 1 if not */
Cache_Add_Dir(const struct dirblob * db,const struct parsedname * pn)437 GOOD_OR_BAD Cache_Add_Dir(const struct dirblob *db, const struct parsedname *pn)
438 {
439 time_t duration = TimeOut(fc_directory);
440 struct tree_node *tn;
441 size_t size = DirblobElements(db) * SERIAL_NUMBER_SIZE;
442 struct parsedname pn_directory;
443
444 if (pn==NO_PARSEDNAME || pn->selected_connection==NO_CONNECTION) {
445 return gbGOOD; // do check here to avoid needless processing
446 }
447
448 switch ( get_busmode(pn->selected_connection) ) {
449 case bus_fake:
450 case bus_tester:
451 case bus_mock:
452 case bus_w1:
453 case bus_bad:
454 case bus_unknown:
455 return gbGOOD ;
456 default:
457 break ;
458 }
459
460 if (duration <= 0) {
461 return 0; /* in case timeout set to 0 */
462 }
463
464 if ( DirblobElements(db) < 1 ) {
465 // only cache long directories.
466 // zero (or one?) entry is possibly an error and needs to be repeated more quickly
467 LEVEL_DEBUG("Won\'t cache empty directory");
468 Cache_Del_Dir( pn ) ;
469 return gbGOOD ;
470 }
471
472 // allocate space for the node and data
473 tn = (struct tree_node *) owmalloc(sizeof(struct tree_node) + size);
474 if (!tn) {
475 return gbBAD;
476 }
477
478 LEVEL_DEBUG("Adding directory for " SNformat " elements=%d", SNvar(pn->sn), DirblobElements(db));
479
480 // populate node with directory name and dirblob
481 FS_LoadDirectoryOnly(&pn_directory, pn);
482 LoadTK( pn_directory.sn, Directory_Marker, pn->selected_connection->index, tn );
483 tn->expires = duration + NOW_TIME;
484 tn->dsize = size;
485 if (size) {
486 memcpy(TREE_DATA(tn), db->snlist, size);
487 }
488 return Add_Stat(&cache_dir, Cache_Add_Common(tn));
489 }
490
491 /* Add a Simultaneous entry to the cache */
492 /* return 0 if good, 1 if not */
Cache_Add_Simul(const struct internal_prop * ip,const struct parsedname * pn)493 GOOD_OR_BAD Cache_Add_Simul(const struct internal_prop *ip, const struct parsedname *pn)
494 {
495 // Note: pn already points to directory
496 time_t duration = TimeOut(ip->change);
497 struct tree_node *tn;
498
499 if (pn==NO_PARSEDNAME || pn->selected_connection==NO_CONNECTION) {
500 return gbGOOD; // do check here to avoid needless processing
501 }
502
503 // Already removed inappropriate busmodes (like fake and usb_monitor)
504 // Now test requested time
505 if (duration <= 0) {
506 return gbGOOD; /* in case timeout set to 0 */
507 }
508
509 // allocate space for the node and data
510 LEVEL_DEBUG("Adding for conversion time for "SNformat, SNvar(pn->sn));
511 tn = (struct tree_node *) owmalloc(sizeof(struct tree_node));
512 if (!tn) {
513 return gbBAD;
514 }
515
516 LEVEL_DEBUG(SNformat, SNvar(pn->sn));
517
518 // populate node with directory name and dirblob
519 LoadTK( pn->sn, ip->name, 0, tn) ;
520 LEVEL_DEBUG("Simultaneous add type=%s",ip->name);
521 tn->expires = duration + NOW_TIME;
522 tn->dsize = 0;
523 return Add_Stat(&cache_dir, Cache_Add_Common(tn));
524 }
525
526 /* Add a device entry to the cache */
527 /* return 0 if good, 1 if not */
Cache_Add_Device(const int bus_nr,const BYTE * sn)528 GOOD_OR_BAD Cache_Add_Device(const int bus_nr, const BYTE * sn)
529 {
530 time_t duration = TimeOut(fc_presence);
531 struct tree_node *tn;
532
533 if (duration <= 0) {
534 return gbGOOD; /* in case timeout set to 0 */
535 }
536
537 if ( sn[0] == 0 ) { //bad serial number
538 return gbGOOD ;
539 }
540
541 tn = (struct tree_node *) owmalloc(sizeof(struct tree_node) + sizeof(int));
542 if (!tn) {
543 return gbBAD;
544 }
545
546 LEVEL_DEBUG("Adding device location " SNformat " bus=%d", SNvar(sn), (int) bus_nr);
547 LoadTK(sn, Device_Marker, 0, tn );
548 tn->expires = duration + NOW_TIME;
549 tn->dsize = sizeof(int);
550 memcpy(TREE_DATA(tn), &bus_nr, sizeof(int));
551 return Add_Stat(&cache_dev, Cache_Add_Common(tn));
552 }
553
554 /* What do we cache?
555 Type sn extension p What
556 directory 0=root 0 *in dirblob
557 DS2409_branch 0 *in dirblob
558 device device sn EXTENSION_DEVICE=-1 NULL bus_nr
559 internal device sn EXTENSION_INTERNAL=-2 ip->name binary data
560 property device sn extension *ft binary data
561 */
562
563 /* Add an item to the cache */
564 /* return 0 if good, 1 if not */
Cache_Add_SlaveSpecific(const void * data,const size_t datasize,const struct internal_prop * ip,const struct parsedname * pn)565 GOOD_OR_BAD Cache_Add_SlaveSpecific(const void *data, const size_t datasize, const struct internal_prop *ip, const struct parsedname *pn)
566 {
567 struct tree_node *tn;
568 time_t duration;
569 //printf("Cache_Add_SlaveSpecific\n");
570 if (!pn) {
571 return gbGOOD; // do check here to avoid needless processing
572 }
573
574 duration = TimeOut(ip->change);
575 if (duration <= 0) {
576 return gbGOOD; /* in case timeout set to 0 */
577 }
578
579 tn = (struct tree_node *) owmalloc(sizeof(struct tree_node) + datasize);
580 if (!tn) {
581 return gbBAD;
582 }
583
584 LEVEL_DEBUG("Adding internal data for "SNformat " size=%d", SNvar(pn->sn), (int) datasize);
585 LoadTK( pn->sn, ip->name, EXTENSION_INTERNAL, tn );
586 tn->expires = duration + NOW_TIME;
587 tn->dsize = datasize;
588 if (datasize) {
589 memcpy(TREE_DATA(tn), data, datasize);
590 }
591 //printf("ADD INTERNAL name= %s size=%d \n",tn->tk.p.nm,tn->dsize);
592 //printf(" ADD INTERNAL data[0]=%d size=%d \n",((BYTE *)data)[0],datasize);
593 switch (ip->change) {
594 case fc_persistent:
595 return Add_Stat(&cache_pst, Cache_Add_Persistent(tn));
596 default:
597 return Add_Stat(&cache_int, Cache_Add_Common(tn));
598 }
599 }
600
601 /* Add an item to the cache */
602 /* return 0 if good, 1 if not */
Cache_Add_Alias(const ASCII * name,const BYTE * sn)603 GOOD_OR_BAD Cache_Add_Alias(const ASCII *name, const BYTE * sn)
604 {
605 struct tree_node *tn;
606 size_t size = strlen(name) ;
607
608 if ( size == 0 ) {
609 return gbGOOD ;
610 }
611
612 tn = (struct tree_node *) owmalloc(sizeof(struct tree_node) + size + 1 );
613 if (!tn) {
614 return gbBAD;
615 }
616
617 LEVEL_DEBUG("Adding alias for " SNformat " = %s", SNvar(sn), name);
618 LoadTK( sn, Alias_Marker, 0, tn );
619 tn->expires = NOW_TIME;
620 tn->dsize = size;
621 memcpy((ASCII *)TREE_DATA(tn), name, size+1 ); // includes NULL
622 Cache_Add_Alias_SN( name, sn ) ;
623 return Add_Stat(&cache_pst, Cache_Add_Persistent(tn));
624 }
625
626 /* Add an item to the cache */
627 /* retire the cache (flip) if too old, and start a new one (keep the old one for a while) */
628 /* return 0 if good, 1 if not */
Cache_Add_Common(struct tree_node * tn)629 static GOOD_OR_BAD Cache_Add_Common(struct tree_node *tn)
630 {
631 struct tree_opaque *opaque;
632 enum { no_add, yes_add, just_update } state = no_add;
633
634 node_show(tn);
635 LEVEL_DEBUG("Add to cache sn " SNformat " pointer=%p index=%d size=%d", SNvar(tn->tk.sn), tn->tk.p, tn->tk.extension, tn->dsize);
636 CACHE_WLOCK;
637 if (cache.time_to_kill < NOW_TIME) { // old database has timed out
638 FlipTree() ;
639 }
640 if (Globals.cache_size && (cache.old_ram_size + cache.new_ram_size > Globals.cache_size)) {
641 // failed size test
642 owfree(tn);
643 } else if ((opaque = tsearch(tn, &cache.temporary_tree_new, tree_compare))) {
644 //printf("Cache_Add_Common to %p\n",opaque);
645 if (tn != opaque->key) {
646 cache.new_ram_size += sizeof(tn) - sizeof(opaque->key);
647 owfree(opaque->key);
648 opaque->key = tn;
649 state = just_update;
650 } else {
651 state = yes_add;
652 cache.new_ram_size += sizeof(tn);
653 }
654 } else { // nothing found or added?!? free our memory segment
655 owfree(tn);
656 }
657 CACHE_WUNLOCK;
658 /* Added or updated, update statistics */
659 switch (state) {
660 case yes_add: // add new entry
661 STATLOCK;
662 AVERAGE_IN(&new_avg);
663 ++cache_adds; /* statistics */
664 STATUNLOCK;
665 return gbGOOD;
666 case just_update: // update the time mark and data
667 STATLOCK;
668 AVERAGE_MARK(&new_avg);
669 ++cache_adds; /* statistics */
670 STATUNLOCK;
671 return gbGOOD;
672 default: // unable to add
673 return gbBAD;
674 }
675 }
676
677 /* Add an item to the cache */
678 /* retire the cache (flip) if too old, and start a new one (keep the old one for a while) */
679 /* return 0 if good, 1 if not */
Cache_Add_Persistent(struct tree_node * tn)680 static GOOD_OR_BAD Cache_Add_Persistent(struct tree_node *tn)
681 {
682 struct tree_opaque *opaque;
683 enum { no_add, yes_add, just_update } state = no_add;
684 LEVEL_DEBUG("Adding data to permanent store");
685
686 PERSISTENT_WLOCK;
687 opaque = tsearch(tn, &cache.persistent_tree, tree_compare) ;
688 if ( opaque != NULL ) {
689 //printf("CACHE ADD pointer=%p, key=%p\n",tn,opaque->key);
690 if (tn != opaque->key) {
691 owfree(opaque->key);
692 opaque->key = tn;
693 state = just_update;
694 } else {
695 state = yes_add;
696 }
697 } else { // nothing found or added?!? free our memory segment
698 owfree(tn);
699 }
700 PERSISTENT_WUNLOCK;
701
702 switch (state) {
703 case yes_add:
704 STATLOCK;
705 AVERAGE_IN(&store_avg);
706 STATUNLOCK;
707 return gbGOOD;
708 case just_update:
709 STATLOCK;
710 AVERAGE_MARK(&store_avg);
711 STATUNLOCK;
712 return gbGOOD;
713 default:
714 return gbBAD;
715 }
716 }
717
Get_Stat(struct cache_stats * scache,const enum cache_task_return result)718 static GOOD_OR_BAD Get_Stat(struct cache_stats *scache, const enum cache_task_return result)
719 {
720 GOOD_OR_BAD gbret = gbBAD ; // default
721
722 STATLOCK;
723 ++scache->tries;
724 switch ( result ) {
725 case ctr_expired:
726 ++scache->expires;
727 break ;
728 case ctr_ok:
729 ++scache->hits;
730 gbret = gbGOOD ;
731 break ;
732 default:
733 break ;
734 }
735 STATUNLOCK;
736 return gbret ;
737 }
738
739 /* Does cache get, but doesn't allow play in data size */
Cache_Get_Strict(void * data,size_t dsize,const struct parsedname * pn)740 static GOOD_OR_BAD Cache_Get_Strict(void *data, size_t dsize, const struct parsedname *pn)
741 {
742 size_t size = dsize;
743 RETURN_BAD_IF_BAD( Cache_Get(data, &size, pn) ) ;
744 return ( dsize == size) ? gbGOOD : gbBAD ;
745 }
746
747
OWQ_Cache_Get(struct one_wire_query * owq)748 GOOD_OR_BAD OWQ_Cache_Get(struct one_wire_query *owq)
749 {
750 struct parsedname *pn = PN(owq);
751
752 // do check here to avoid needless processing
753 if (IsUncachedDir(pn) || IsAlarmDir(pn)) {
754 return gbBAD;
755 }
756
757 switch (pn->selected_filetype->change) {
758 case fc_simultaneous_temperature:
759 return Cache_Get_Simultaneous(SlaveSpecificTag(S_T), owq) ;
760 case fc_simultaneous_voltage:
761 return Cache_Get_Simultaneous(SlaveSpecificTag(S_T), owq) ;
762 default:
763 break ;
764 }
765
766 if (pn->extension == EXTENSION_ALL) {
767 switch (pn->selected_filetype->format) {
768 case ft_ascii:
769 case ft_vascii:
770 case ft_alias:
771 case ft_binary:
772 return gbBAD; // string arrays not supported
773 case ft_integer:
774 case ft_unsigned:
775 case ft_yesno:
776 case ft_date:
777 case ft_float:
778 case ft_pressure:
779 case ft_temperature:
780 case ft_tempgap:
781 return Cache_Get_Strict(OWQ_array(owq), (pn->selected_filetype->ag->elements) * sizeof(union value_object), pn);
782 default:
783 return gbBAD;
784 }
785 } else {
786 switch (pn->selected_filetype->format) {
787 case ft_ascii:
788 case ft_vascii:
789 case ft_alias:
790 case ft_binary:
791 if (OWQ_offset(owq) > 0) {
792 return gbBAD;
793 }
794 OWQ_length(owq) = OWQ_size(owq);
795 return Cache_Get(OWQ_buffer(owq), &OWQ_length(owq), pn);
796 case ft_integer:
797 case ft_unsigned:
798 case ft_yesno:
799 case ft_date:
800 case ft_float:
801 case ft_pressure:
802 case ft_temperature:
803 case ft_tempgap:
804 return Cache_Get_Strict(&OWQ_val(owq), sizeof(union value_object), pn);
805 default:
806 return gbBAD;
807 }
808 }
809 }
810
811 /* Look in caches, 0=found and valid, 1=not or uncachable in the first place */
Cache_Get(void * data,size_t * dsize,const struct parsedname * pn)812 GOOD_OR_BAD Cache_Get(void *data, size_t * dsize, const struct parsedname *pn)
813 {
814 time_t duration;
815 struct tree_node tn;
816 int persistent ;
817
818 // do check here to avoid needless processing
819 if (IsUncachedDir(pn) || IsAlarmDir(pn)) {
820 return gbBAD;
821 }
822
823 // Special handling of Mock
824 persistent = IsThisPersistent(pn) ;
825 if ( persistent ) {
826 duration = 1 ;
827 } else {
828 duration = TimeOut(pn->selected_filetype->change);
829 if (duration <= 0) {
830 return gbBAD; /* in case timeout set to 0 */
831 }
832 }
833
834
835 LEVEL_DEBUG(SNformat " size=%d IsUncachedDir=%d", SNvar(pn->sn), (int) dsize[0], IsUncachedDir(pn));
836 LoadTK( pn->sn, pn->selected_filetype, pn->extension, &tn );
837 return persistent ?
838 Get_Stat(&cache_pst, Cache_Get_Persistent(data, dsize, &duration, &tn)) :
839 Get_Stat(&cache_ext, Cache_Get_Common(data, dsize, &duration, &tn));
840 }
841
842 /* Look in caches, 0=found and valid, 1=not or uncachable in the first place */
Cache_Get_Dir(struct dirblob * db,const struct parsedname * pn)843 GOOD_OR_BAD Cache_Get_Dir(struct dirblob *db, const struct parsedname *pn)
844 {
845 time_t duration = TimeOut(fc_directory);
846 struct tree_node tn;
847 struct parsedname pn_directory;
848 DirblobInit(db);
849 if (duration <= 0) {
850 return gbBAD;
851 }
852
853 LEVEL_DEBUG("Looking for directory "SNformat, SNvar(pn->sn));
854 FS_LoadDirectoryOnly(&pn_directory, pn);
855 LoadTK( pn_directory.sn, Directory_Marker, pn->selected_connection->index, &tn) ;
856 return Get_Stat(&cache_dir, Cache_Get_Common_Dir(db, &duration, &tn));
857 }
858
859 /* Look in caches, 0=found and valid, 1=not or uncachable in the first place */
Cache_Get_Common_Dir(struct dirblob * db,time_t * duration,const struct tree_node * tn)860 static enum cache_task_return Cache_Get_Common_Dir(struct dirblob *db, time_t * duration, const struct tree_node *tn)
861 {
862 enum cache_task_return ctr_ret;
863 time_t now = NOW_TIME;
864 size_t size;
865 struct tree_opaque *opaque;
866 LEVEL_DEBUG("Get from cache sn " SNformat " pointer=%p extension=%d", SNvar(tn->tk.sn), tn->tk.p, tn->tk.extension);
867 CACHE_RLOCK;
868 opaque = tfind(tn, &cache.temporary_tree_new, tree_compare) ;
869 if ( opaque == NULL ) {
870 // not found in new tree
871 if ( cache.time_retired + duration[0] > now ) {
872 // old tree could be new enough
873 opaque = tfind(tn, &cache.temporary_tree_old, tree_compare) ;
874 }
875 }
876 if ( opaque != NULL ) {
877 duration[0] = opaque->key->expires - now ;
878 if (duration[0] >= 0) {
879 LEVEL_DEBUG("Dir found in cache");
880 size = opaque->key->dsize;
881 if (DirblobRecreate(TREE_DATA(opaque->key), size, db) == 0) {
882 //printf("Cache: snlist=%p, devices=%lu, size=%lu\n",*snlist,devices[0],size) ;
883 ctr_ret = ctr_ok;
884 } else {
885 ctr_ret = ctr_size_mismatch;
886 }
887 } else {
888 //char b[26];
889 //printf("GOT DEAD now:%s",ctime_r(&now,b)) ;
890 //printf(" then:%s",ctime_r(&opaque->key->expires,b)) ;
891 LEVEL_DEBUG("Dir expired in cache");
892 ctr_ret = ctr_expired;
893 }
894 } else {
895 LEVEL_DEBUG("Dir not found in cache");
896 ctr_ret = ctr_not_found;
897 }
898 CACHE_RUNLOCK;
899 return ctr_ret;
900 }
901
902 /* Look in caches, 0=found and valid, 1=not or uncachable in the first place */
Cache_Get_Device(void * bus_nr,const struct parsedname * pn)903 GOOD_OR_BAD Cache_Get_Device(void *bus_nr, const struct parsedname *pn)
904 {
905 time_t duration = TimeOut(fc_presence);
906 size_t size = sizeof(int);
907 struct tree_node tn;
908 if (duration <= 0) {
909 return gbBAD;
910 }
911
912 LEVEL_DEBUG("Looking for device "SNformat, SNvar(pn->sn));
913 LoadTK( pn->sn, Device_Marker, 0, &tn ) ;
914 return Get_Stat(&cache_dev, Cache_Get_Common(bus_nr, &size, &duration, &tn));
915 }
916
917 /* Does cache get, but doesn't allow play in data size */
Cache_Get_SlaveSpecific(void * data,size_t dsize,const struct internal_prop * ip,const struct parsedname * pn)918 GOOD_OR_BAD Cache_Get_SlaveSpecific(void *data, size_t dsize, const struct internal_prop *ip, const struct parsedname *pn)
919 {
920 size_t size = dsize;
921 RETURN_BAD_IF_BAD( Cache_Get_Internal(data, &size, ip, pn) ) ;
922
923 return ( dsize == size) ? gbGOOD : gbBAD ;
924 }
925
926 /* Look in caches, 0=found and valid, 1=not or uncachable in the first place */
Cache_Get_Internal(void * data,size_t * dsize,const struct internal_prop * ip,const struct parsedname * pn)927 static GOOD_OR_BAD Cache_Get_Internal(void *data, size_t * dsize, const struct internal_prop *ip, const struct parsedname *pn)
928 {
929 struct tree_node tn;
930 time_t duration;
931 //printf("Cache_Get_Internal");
932 if (!pn) {
933 return gbBAD; // do check here to avoid needless processing
934 }
935
936 duration = TimeOut(ip->change);
937 if (duration <= 0) {
938 return gbBAD; /* in case timeout set to 0 */
939 }
940
941 LEVEL_DEBUG(SNformat " size=%d", SNvar(pn->sn), (int) dsize[0]);
942 LoadTK( pn->sn, ip->name, EXTENSION_INTERNAL, &tn) ;
943 switch (ip->change) {
944 case fc_persistent:
945 return Get_Stat(&cache_pst, Cache_Get_Persistent(data, dsize, &duration, &tn));
946 default:
947 return Get_Stat(&cache_int, Cache_Get_Common(data, dsize, &duration, &tn));
948 }
949 }
950
951 /* Test for a simultaneous property
952 If the property isn't independently cached, return false (1)
953 If the simultaneous conversion is more recent, return false (1)
954 Else return the cached value and true (0)
955 */
Cache_Get_Simul_Time(const struct internal_prop * ip,time_t * dwell_time,const struct parsedname * pn)956 GOOD_OR_BAD Cache_Get_Simul_Time(const struct internal_prop *ip, time_t * dwell_time, const struct parsedname * pn)
957 {
958 // valid cached primary data -- see if a simultaneous conversion should be used instead
959 struct tree_node tn;
960 time_t duration ;
961 size_t dsize_simul = 0 ;
962 struct parsedname pn_directory ;
963
964 duration = TimeOut(ip->change); // time allocated this conversion
965 if ( duration <= 0) {
966 // uncachable
967 return gbBAD;
968 }
969
970 LEVEL_DEBUG("Looking for conversion time "SNformat, SNvar(pn->sn));
971
972 FS_LoadDirectoryOnly(&pn_directory, pn);
973 LoadTK(pn_directory.sn, ip->name, 0, &tn ) ;
974 if ( Get_Stat(&cache_int, Cache_Get_Common(NULL, &dsize_simul, &duration, &tn)) ) {
975 return gbBAD ;
976 }
977 // duration_simul is time left
978 // duration is time allocated
979 // back-compute dwell time
980 dwell_time[0] = TimeOut(ip->change) - duration ;
981 return gbGOOD ;
982 }
983
984 /* Test for a simultaneous property
985 * return true if simultaneous is the prefered method
986 * bad if no simultaneous, or it's not the best
987 */
Cache_Get_Simultaneous(const struct internal_prop * ip,struct one_wire_query * owq)988 static GOOD_OR_BAD Cache_Get_Simultaneous(const struct internal_prop *ip, struct one_wire_query *owq)
989 {
990 struct tree_node tn;
991 time_t duration ;
992 time_t time_left ;
993 time_t dwell_time_simul ;
994 struct parsedname * pn = PN(owq) ;
995 size_t dsize = sizeof(union value_object) ;
996
997 time_left = duration = TimeOut(pn->selected_filetype->change);
998 if (duration <= 0) {
999 // probably "uncached" requested
1000 return gbBAD;
1001 }
1002
1003 LoadTK( pn->sn, pn->selected_filetype, pn->extension, &tn ) ;
1004
1005 if ( Get_Stat(&cache_ext, Cache_Get_Common( &OWQ_val(owq), &dsize, &time_left, &tn)) == 0 ) {
1006 // valid cached primary data -- see if a simultaneous conversion should be used instead
1007 time_t dwell_time_data = duration - time_left ;
1008
1009 if ( BAD( Cache_Get_Simul_Time( ip, &dwell_time_simul, pn)) ) {
1010 // Simul not found or timed out
1011 LEVEL_DEBUG("Simultaneous conversion not found.") ;
1012 OWQ_SIMUL_CLR(owq) ;
1013 return gbGOOD ;
1014 }
1015 if ( dwell_time_simul < dwell_time_data ) {
1016 LEVEL_DEBUG("Simultaneous conversion is newer than previous reading.") ;
1017 OWQ_SIMUL_SET(owq) ;
1018 return gbBAD ; // Simul is newer
1019 }
1020 // Cached data is newer, so use it
1021 OWQ_SIMUL_CLR(owq) ;
1022 return gbGOOD ;
1023 }
1024 // fall through -- no cached primary data
1025 if ( BAD( Cache_Get_Simul_Time( ip, &dwell_time_simul, pn)) ) {
1026 // no simultaneous either
1027 OWQ_SIMUL_CLR(owq) ;
1028 return gbBAD ;
1029 }
1030 OWQ_SIMUL_SET(owq) ;
1031 return gbBAD ; // Simul is newer
1032 }
1033
1034 /* Look in caches, 0=found and valid, 1=not or uncachable in the first place */
1035 /* space allocated, needs to be owfree-d */
1036 /* returns null-terminated string */
Cache_Get_Alias(const BYTE * sn)1037 ASCII * Cache_Get_Alias(const BYTE * sn)
1038 {
1039 struct tree_node tn;
1040 struct tree_opaque *opaque;
1041 ASCII * alias_name = NULL ;
1042
1043
1044 LoadTK(sn, Alias_Marker, 0, &tn ) ;
1045
1046 PERSISTENT_RLOCK;
1047 opaque = tfind(&tn, &cache.persistent_tree, tree_compare) ;
1048 if ( opaque != NULL ) {
1049 alias_name = owmalloc( opaque->key->dsize + 1 ) ;
1050 if ( alias_name != NULL ) {
1051 memcpy( alias_name, (ASCII *)TREE_DATA(opaque->key), opaque->key->dsize+1 ) ;
1052 LEVEL_DEBUG("Retrieving " SNformat " alias=%s", SNvar(sn), alias_name );
1053 }
1054 }
1055 PERSISTENT_RUNLOCK;
1056 return alias_name ;
1057 }
1058
1059 /* Look in caches */
1060 /* duration is time left */
1061 /* inputs: dsize, duration, tn
1062 * outputs: return value, data, dsize (updated), duration (updated)
1063 * */
Cache_Get_Common(void * data,size_t * dsize,time_t * duration,const struct tree_node * tn)1064 static enum cache_task_return Cache_Get_Common(void *data, size_t * dsize, time_t * duration, const struct tree_node *tn)
1065 {
1066 enum cache_task_return ctr_ret;
1067 time_t now = NOW_TIME;
1068 struct tree_opaque *opaque;
1069
1070 LEVEL_DEBUG("Search in cache sn " SNformat " pointer=%p index=%d size=%d", SNvar(tn->tk.sn), tn->tk.p, tn->tk.extension, (int) dsize[0]);
1071 //node_show(tn);
1072 //new_tree();
1073 CACHE_RLOCK;
1074 opaque = tfind(tn, &cache.temporary_tree_new, tree_compare) ;
1075 if ( opaque == NULL ) {
1076 // not found in new tree
1077 if ( cache.time_retired + duration[0] > now ) {
1078 // retired time isn't too old for this data item
1079 opaque = tfind(tn, &cache.temporary_tree_old, tree_compare) ;
1080 }
1081 }
1082 if ( opaque != NULL ) {
1083 // modify duration to time left (can be negative if expired)
1084 duration[0] = opaque->key->expires - now ;
1085 if (duration[0] > 0) {
1086 LEVEL_DEBUG("Value found in cache. Remaining life: %d seconds.",duration[0]);
1087 // Compared with >= before, but fc_second(1) always cache for 2 seconds in that case.
1088 // Very noticable when reading time-data like "/26.80A742000000/date" for example.
1089 if ( dsize[0] >= opaque->key->dsize) {
1090 // lower data size if stored value is shorter
1091 dsize[0] = opaque->key->dsize;
1092 //tree_show(opaque,leaf,0);
1093 if (dsize[0] > 0) {
1094 memcpy(data, TREE_DATA(opaque->key), dsize[0]);
1095 }
1096 ctr_ret = ctr_ok;
1097 //twalk(cache.temporary_tree_new,tree_show) ;
1098 } else {
1099 ctr_ret = ctr_size_mismatch;
1100 }
1101 } else {
1102 LEVEL_DEBUG("Value found in cache, but expired by %d seconds.",-duration[0]);
1103 ctr_ret = ctr_expired;
1104 }
1105 } else {
1106 LEVEL_DEBUG("Value not found in cache");
1107 ctr_ret = ctr_not_found;
1108 }
1109 CACHE_RUNLOCK;
1110 return ctr_ret;
1111 }
1112
1113 /* Look in caches, 0=found and valid, 1=not or uncachable in the first place */
Cache_Get_Persistent(void * data,size_t * dsize,time_t * duration,const struct tree_node * tn)1114 static enum cache_task_return Cache_Get_Persistent(void *data, size_t * dsize, time_t * duration, const struct tree_node *tn)
1115 {
1116 struct tree_opaque *opaque;
1117 enum cache_task_return ctr_ret;
1118 (void) duration; // ignored -- no timeout
1119 PERSISTENT_RLOCK;
1120 opaque = tfind(tn, &cache.persistent_tree, tree_compare) ;
1121 if ( opaque != NULL ) {
1122 if ( dsize[0] >= opaque->key->dsize) {
1123 dsize[0] = opaque->key->dsize;
1124 if (dsize[0] > 0) {
1125 memcpy(data, TREE_DATA(opaque->key), dsize[0]);
1126 }
1127 ctr_ret = ctr_ok;
1128 } else {
1129 ctr_ret = ctr_size_mismatch;
1130 }
1131 } else {
1132 ctr_ret = ctr_not_found;
1133 }
1134 PERSISTENT_RUNLOCK;
1135 return ctr_ret;
1136 }
1137
Del_Stat(struct cache_stats * scache,const int result)1138 static void Del_Stat(struct cache_stats *scache, const int result)
1139 {
1140 if ( GOOD( result)) {
1141 STAT_ADD1(scache->deletes);
1142 }
1143 }
1144
OWQ_Cache_Del(struct one_wire_query * owq)1145 void OWQ_Cache_Del(struct one_wire_query *owq)
1146 {
1147 Cache_Del(PN(owq));
1148 }
1149
OWQ_Cache_Del_ALL(struct one_wire_query * owq)1150 void OWQ_Cache_Del_ALL(struct one_wire_query *owq)
1151 {
1152 struct parsedname * pn = PN(owq) ; // convenience
1153 int extension = pn->extension ; // store extension
1154
1155 pn->extension = EXTENSION_ALL ; // temporary assignment
1156 Cache_Del(pn);
1157 pn->extension = extension ; // restore extension
1158 }
1159
OWQ_Cache_Del_BYTE(struct one_wire_query * owq)1160 void OWQ_Cache_Del_BYTE(struct one_wire_query *owq)
1161 {
1162 struct parsedname * pn = PN(owq) ; // convenience
1163 int extension = pn->extension ; // store extension
1164
1165 pn->extension = EXTENSION_BYTE ; // temporary assignment
1166 Cache_Del(pn);
1167 pn->extension = extension ; // restore extension
1168 }
1169
OWQ_Cache_Del_parts(struct one_wire_query * owq)1170 void OWQ_Cache_Del_parts(struct one_wire_query *owq)
1171 {
1172 struct parsedname * pn = PN(owq) ; // convenience
1173
1174 if ( pn->selected_filetype->ag != NON_AGGREGATE ) {
1175 int extension = pn->extension ; // store extension
1176 int extension_index ;
1177 for ( extension_index = pn->selected_filetype->ag->elements - 1 ; extension_index >= 0 ; -- extension_index ) {
1178 pn->extension = extension_index ; // temporary assignment
1179 Cache_Del(pn);
1180 }
1181 pn->extension = extension ; // restore extension
1182 } else {
1183 Cache_Del(pn);
1184 }
1185 }
1186
1187 // Delete a serial number's alias
1188 // Safe to call if no alias exists
1189 // Also deletes from linked alias_sn file
Cache_Del_Alias(const BYTE * sn)1190 void Cache_Del_Alias(const BYTE * sn)
1191 {
1192 ASCII * alias_name ;
1193 struct tree_node *tn;
1194 size_t size ;
1195
1196 alias_name = Cache_Get_Alias( sn ) ;
1197 if ( alias_name == NULL ) {
1198 // doesn't exist
1199 // (or a memory error -- unlikely)
1200 return ;
1201 }
1202
1203 LEVEL_DEBUG("Deleting alias %s from "SNformat, alias_name, SNvar(sn)) ;
1204 size = strlen( alias_name ) ;
1205 tn = (struct tree_node *) owmalloc(sizeof(struct tree_node) + size + 1 );
1206 if ( tn != NULL ) {
1207 tn->expires = NOW_TIME;
1208 tn->dsize = size;
1209 memcpy((ASCII *)TREE_DATA(tn), alias_name, size+1); // includes NULL
1210 LoadTK( sn, Alias_Marker, 0, tn ) ;
1211 Del_Stat(&cache_pst, Cache_Del_Persistent(tn));
1212 Cache_Del_Alias_SN( alias_name ) ;
1213 }
1214 owfree( alias_name ) ;
1215 }
1216
Cache_Del(const struct parsedname * pn)1217 static void Cache_Del(const struct parsedname *pn)
1218 {
1219 struct tree_node tn;
1220 time_t duration;
1221
1222 //printf("Cache_Del\n") ;
1223 //printf("Cache_Del\n") ;
1224 if (!pn) {
1225 return; // do check here to avoid needless processing
1226 }
1227
1228 duration = TimeOut(pn->selected_filetype->change);
1229 if (duration <= 0) {
1230 return; /* in case timeout set to 0 */
1231 }
1232
1233 LoadTK( pn->sn, pn->selected_filetype, pn->extension, &tn ) ;
1234 switch (pn->selected_filetype->change) {
1235 case fc_persistent:
1236 Del_Stat(&cache_pst, Cache_Del_Persistent(&tn));
1237 break ;
1238 default:
1239 Del_Stat(&cache_ext, Cache_Del_Common(&tn));
1240 break ;
1241 }
1242 }
1243
Cache_Del_Mixed_Individual(const struct parsedname * pn)1244 void Cache_Del_Mixed_Individual(const struct parsedname *pn)
1245 {
1246 struct tree_node tn;
1247 time_t duration;
1248 //printf("Cache_Del\n") ;
1249 if (!pn) {
1250 return; // do check here to avoid needless processing
1251 }
1252 if (pn->selected_filetype->ag==NON_AGGREGATE || pn->selected_filetype->ag->combined!=ag_mixed) {
1253 return ;
1254 }
1255 duration = TimeOut(pn->selected_filetype->change);
1256 if (duration <= 0) {
1257 return; /* in case timeout set to 0 */
1258 }
1259
1260 LoadTK( pn->sn, pn->selected_filetype, 0, &tn) ;
1261 for ( tn.tk.extension = pn->selected_filetype->ag->elements-1 ; tn.tk.extension >= 0 ; --tn.tk.extension ) {
1262 switch (pn->selected_filetype->change) {
1263 case fc_persistent:
1264 Del_Stat(&cache_pst, Cache_Del_Persistent(&tn));
1265 break ;
1266 default:
1267 Del_Stat(&cache_ext, Cache_Del_Common(&tn));
1268 break ;
1269 }
1270 }
1271 }
1272
Cache_Del_Mixed_Aggregate(const struct parsedname * pn)1273 void Cache_Del_Mixed_Aggregate(const struct parsedname *pn)
1274 {
1275 struct tree_node tn;
1276 time_t duration;
1277 //printf("Cache_Del\n") ;
1278 if (!pn) {
1279 return; // do check here to avoid needless processing
1280 }
1281 if (pn->selected_filetype->ag==NON_AGGREGATE || pn->selected_filetype->ag->combined!=ag_mixed) {
1282 return ;
1283 }
1284 duration = TimeOut(pn->selected_filetype->change);
1285 if (duration <= 0) {
1286 return; /* in case timeout set to 0 */
1287 }
1288
1289 LoadTK( pn->sn, pn->selected_filetype, EXTENSION_ALL, &tn) ;
1290 switch (pn->selected_filetype->change) {
1291 case fc_persistent:
1292 Del_Stat(&cache_pst, Cache_Del_Persistent(&tn));
1293 break ;
1294 default:
1295 Del_Stat(&cache_ext, Cache_Del_Common(&tn));
1296 break ;
1297 }
1298 }
1299
Cache_Del_Dir(const struct parsedname * pn)1300 void Cache_Del_Dir(const struct parsedname *pn)
1301 {
1302 struct tree_node tn;
1303 struct parsedname pn_directory;
1304
1305 FS_LoadDirectoryOnly(&pn_directory, pn);
1306 LoadTK( pn_directory.sn, Directory_Marker, pn->selected_connection->index, &tn ) ;
1307 Del_Stat(&cache_dir, Cache_Del_Common(&tn));
1308 }
1309
Cache_Del_Simul(const struct internal_prop * ip,const struct parsedname * pn)1310 void Cache_Del_Simul(const struct internal_prop *ip, const struct parsedname *pn)
1311 {
1312 struct tree_node tn;
1313 struct parsedname pn_directory;
1314
1315 FS_LoadDirectoryOnly(&pn_directory, pn);
1316 LoadTK(pn_directory.sn, ip->name, 0, &tn );
1317 Del_Stat(&cache_dir, Cache_Del_Common(&tn));
1318 }
1319
Cache_Del_Device(const struct parsedname * pn)1320 void Cache_Del_Device(const struct parsedname *pn)
1321 {
1322 struct tree_node tn;
1323 time_t duration = TimeOut(fc_presence);
1324 if (duration <= 0) {
1325 return;
1326 }
1327
1328 LoadTK(pn->sn, Device_Marker, 0, &tn) ;
1329 Del_Stat(&cache_dev, Cache_Del_Common(&tn));
1330 }
1331
Cache_Del_Internal(const struct internal_prop * ip,const struct parsedname * pn)1332 void Cache_Del_Internal(const struct internal_prop *ip, const struct parsedname *pn)
1333 {
1334 struct tree_node tn;
1335 time_t duration;
1336 //printf("Cache_Del_Internal\n") ;
1337 if (!pn) {
1338 return; // do check here to avoid needless processing
1339 }
1340
1341 duration = TimeOut(ip->change);
1342 if (duration <= 0) {
1343 return; /* in case timeout set to 0 */
1344 }
1345
1346 LoadTK(pn->sn, ip->name, 0, &tn);
1347 switch (ip->change) {
1348 case fc_persistent:
1349 Del_Stat(&cache_pst, Cache_Del_Persistent(&tn));
1350 break;
1351 default:
1352 Del_Stat(&cache_int, Cache_Del_Common(&tn));
1353 break;
1354 }
1355 }
1356
Cache_Del_Common(const struct tree_node * tn)1357 static GOOD_OR_BAD Cache_Del_Common(const struct tree_node *tn)
1358 {
1359 struct tree_opaque *opaque;
1360 time_t now = NOW_TIME;
1361 GOOD_OR_BAD ret = gbBAD;
1362 LEVEL_DEBUG("Delete from cache sn " SNformat " in=%p index=%d", SNvar(tn->tk.sn), tn->tk.p, tn->tk.extension);
1363
1364 CACHE_WLOCK;
1365 opaque = tfind(tn, &cache.temporary_tree_new, tree_compare) ;
1366 if ( opaque == NULL ) {
1367 // not in new tree
1368 if ( cache.time_to_kill > now ) {
1369 // old tree still alive
1370 opaque = tfind(tn, &cache.temporary_tree_old, tree_compare) ;
1371 }
1372 }
1373 if ( opaque != NULL ) {
1374 opaque->key->expires = now - 1;
1375 ret = gbGOOD;
1376 }
1377 CACHE_WUNLOCK;
1378
1379 return ret;
1380 }
1381
Cache_Del_Persistent(const struct tree_node * tn)1382 static GOOD_OR_BAD Cache_Del_Persistent(const struct tree_node *tn)
1383 {
1384 struct tree_opaque *opaque;
1385 struct tree_node *tn_found = NULL;
1386
1387 PERSISTENT_WLOCK;
1388 opaque = tfind(tn, &cache.persistent_tree, tree_compare) ;
1389 if ( opaque != NULL ) {
1390 tn_found = opaque->key;
1391 tdelete(tn, &cache.persistent_tree, tree_compare);
1392 }
1393 PERSISTENT_WUNLOCK;
1394
1395 if ( tn_found == NULL ) {
1396 return gbBAD;
1397 }
1398
1399 owfree(tn_found);
1400 STATLOCK;
1401 AVERAGE_OUT(&store_avg);
1402 STATUNLOCK;
1403 return gbGOOD;
1404 }
1405
LoadTK(const BYTE * sn,void * p,int extension,struct tree_node * tn)1406 static void LoadTK( const BYTE * sn, void * p, int extension, struct tree_node * tn )
1407 {
1408 memset(&(tn->tk), 0, sizeof(struct tree_key));
1409 memcpy(tn->tk.sn, sn, SERIAL_NUMBER_SIZE);
1410 tn->tk.p = p;
1411 tn->tk.extension = extension;
1412 }
1413
1414 // Alias list from persistent cache
1415 // formatted as an alias file:
1416 // NNNNNNNNNNNN=alias_name\n
1417 //
1418 // Need to protect a global variable (aliaslist_cb) since twalk has no way of sending user data.
1419
1420 struct memblob * aliaslist_mb ;
1421
Aliaslistaction(const void * node,const VISIT which,const int depth)1422 static void Aliaslistaction(const void *node, const VISIT which, const int depth)
1423 {
1424 const struct tree_node *p = *(struct tree_node * const *) node;
1425 (void) depth;
1426 char SN_address[SERIAL_NUMBER_SIZE*2] ;
1427
1428 switch (which) {
1429 case leaf:
1430 case postorder:
1431 if ( p->tk.p != Alias_Marker ) {
1432 return ;
1433 }
1434 // Add sn address
1435 bytes2string(SN_address, p->tk.sn, SERIAL_NUMBER_SIZE);
1436 MemblobAdd( (BYTE *) SN_address, SERIAL_NUMBER_SIZE*2, aliaslist_mb ) ;
1437 // Add '='
1438 MemblobAdd( (BYTE *) "=", 1, aliaslist_mb ) ;
1439 // Add alias name
1440 MemblobAdd( (const BYTE *)CONST_TREE_DATA(p), p->dsize, aliaslist_mb ) ;
1441 // Add <CR>
1442 MemblobAdd( (BYTE *) "\x0D\x0A", 2, aliaslist_mb ) ;
1443 return ;
1444 case preorder:
1445 case endorder:
1446 break;
1447 }
1448 }
1449
Aliaslist(struct memblob * mb)1450 void Aliaslist( struct memblob * mb )
1451 {
1452 PERSISTENT_RLOCK ;
1453 ALIASLISTLOCK ;
1454 aliaslist_mb = mb ;
1455 twalk(cache.persistent_tree, Aliaslistaction);
1456 ALIASLISTUNLOCK ;
1457 PERSISTENT_RUNLOCK ;
1458 }
1459
1460 /* Add an alias to the temporary database of name->bus */
1461 /* alias_name is a null-terminated string */
Cache_Add_Alias_Bus(const ASCII * alias_name,INDEX_OR_ERROR bus)1462 void Cache_Add_Alias_Bus(const ASCII * alias_name, INDEX_OR_ERROR bus)
1463 {
1464 // allocate space for the node and data
1465 size_t datasize = strlen(alias_name) ;
1466 struct alias_tree_node *atn = (struct alias_tree_node *) owmalloc(sizeof(struct alias_tree_node) + datasize + 1 );
1467 time_t duration = TimeOut(fc_presence);
1468
1469 if (atn==NULL) {
1470 return ;
1471 }
1472
1473 if (datasize==0) {
1474 owfree(atn) ;
1475 return ;
1476 }
1477
1478 // populate the node structure with data
1479 atn->expires = duration + NOW_TIME;
1480 atn->size = datasize ;
1481 atn->bus = bus ;
1482 memcpy( ALIAS_TREE_DATA(atn), alias_name, datasize + 1 ) ;
1483
1484 Cache_Add_Alias_Common( atn ) ;
1485 }
1486
1487 /* Add an item to the alias cache */
1488 /* retire the cache (flip) if too old, and start a new one (keep the old one for a while) */
Cache_Add_Alias_Common(struct alias_tree_node * atn)1489 static void Cache_Add_Alias_Common(struct alias_tree_node *atn)
1490 {
1491 struct tree_opaque *opaque;
1492
1493 CACHE_WLOCK;
1494 if (cache.time_to_kill < NOW_TIME) { // old database has timed out
1495 FlipTree() ;
1496 }
1497 if (Globals.cache_size && (cache.old_ram_size + cache.new_ram_size > Globals.cache_size)) {
1498 // failed size test
1499 owfree(atn);
1500 } else if ((opaque = tsearch(atn, &cache.temporary_alias_tree_new, alias_tree_compare))) {
1501 if ( (void *)atn != (void *) (opaque->key) ) {
1502 cache.new_ram_size += sizeof(atn) - sizeof(opaque->key);
1503 owfree(opaque->key);
1504 opaque->key = (void *) atn;
1505 } else {
1506 cache.new_ram_size += sizeof(atn);
1507 }
1508 } else { // nothing found or added?!? free our memory segment
1509 owfree(atn);
1510 }
1511 CACHE_WUNLOCK;
1512 }
1513
1514 /* Add an alias/sn to the persistent database of name->sn */
1515 /* Alias name must be a null-terminated string */
Cache_Add_Alias_SN(const ASCII * alias_name,const BYTE * sn)1516 static void Cache_Add_Alias_SN(const ASCII * alias_name, const BYTE * sn)
1517 {
1518 // allocate space for the node and data
1519 size_t datasize = strlen(alias_name) ;
1520 struct alias_tree_node *atn = (struct alias_tree_node *) owmalloc(sizeof(struct alias_tree_node) + datasize + 1);
1521
1522 if (atn==NULL) {
1523 return ;
1524 }
1525
1526 if (datasize==0) {
1527 owfree(atn) ;
1528 return ;
1529 }
1530
1531 // populate the node structure with data
1532 atn->expires = NOW_TIME;
1533 atn->size = datasize;
1534 memcpy( atn->sn, sn, SERIAL_NUMBER_SIZE ) ;
1535 memcpy( ALIAS_TREE_DATA(atn), alias_name, datasize+1 ) ;
1536
1537 Cache_Add_Alias_Persistent( atn ) ;
1538 }
1539
Cache_Add_Alias_Persistent(struct alias_tree_node * atn)1540 static void Cache_Add_Alias_Persistent(struct alias_tree_node *atn)
1541 {
1542 struct tree_opaque *opaque;
1543
1544 PERSISTENT_WLOCK;
1545 opaque = tsearch(atn, &cache.persistent_alias_tree, alias_tree_compare) ;
1546 if ( opaque != NULL ) {
1547 if ( (void *) atn != (void *) (opaque->key) ) {
1548 owfree(opaque->key);
1549 opaque->key = (void *) atn;
1550 }
1551 } else { // nothing found or added?!? free our memory segment
1552 owfree(atn);
1553 }
1554 PERSISTENT_WUNLOCK;
1555 }
1556
1557 /* Find bus from alias name */
1558 /* Alias name must be a null-terminated string */
Cache_Get_Alias_Bus(const ASCII * alias_name)1559 INDEX_OR_ERROR Cache_Get_Alias_Bus(const ASCII * alias_name)
1560 {
1561 // allocate space for the node and data
1562 size_t datasize = strlen(alias_name) ;
1563 struct alias_tree_node *atn = (struct alias_tree_node *) owmalloc(sizeof(struct alias_tree_node) + datasize + 1);
1564
1565 if (atn==NULL) {
1566 return INDEX_BAD ;
1567 }
1568
1569 if (datasize==0) {
1570 owfree(atn) ;
1571 return INDEX_BAD ;
1572 }
1573
1574 // populate the node structure with data
1575 atn->size = datasize;
1576 memcpy( ALIAS_TREE_DATA(atn), alias_name, datasize+1 ) ;
1577
1578 return Cache_Get_Alias_Common( atn ) ;
1579 }
1580
Cache_Get_Alias_Common(struct alias_tree_node * atn)1581 static INDEX_OR_ERROR Cache_Get_Alias_Common( struct alias_tree_node * atn)
1582 {
1583 INDEX_OR_ERROR bus = INDEX_BAD;
1584 time_t now = NOW_TIME;
1585 struct tree_opaque *opaque;
1586
1587 CACHE_RLOCK;
1588 opaque = tfind(atn, &cache.temporary_alias_tree_new, alias_tree_compare) ;
1589 if ( opaque == NULL ) {
1590 // try old tree
1591 opaque = tfind(atn, &cache.temporary_alias_tree_old, alias_tree_compare) ;
1592 }
1593 if ( opaque != NULL ) {
1594 // test expiration
1595 if ( ((struct alias_tree_node *)(opaque->key))->expires > now) {
1596 bus = ((struct alias_tree_node *)(opaque->key))->bus ;
1597 LEVEL_DEBUG("Found %s on bus.%d",ALIAS_TREE_DATA(atn),bus) ;
1598 }
1599 }
1600 CACHE_RUNLOCK;
1601 LEVEL_DEBUG("Finding %s unsuccessful",ALIAS_TREE_DATA(atn)) ;
1602 owfree(atn) ;
1603 return bus;
1604 }
1605
1606 /* sn must point to an 8 byte buffer */
1607 /* Alias name must be a null-terminated string */
Cache_Get_Alias_SN(const ASCII * alias_name,BYTE * sn)1608 GOOD_OR_BAD Cache_Get_Alias_SN(const ASCII * alias_name, BYTE * sn )
1609 {
1610 // allocate space for the node and data
1611 size_t datasize = strlen(alias_name) ;
1612 struct alias_tree_node *atn ;
1613
1614 if (datasize==0) {
1615 return gbBAD ;
1616 }
1617
1618 atn = (struct alias_tree_node *) owmalloc(sizeof(struct alias_tree_node) + datasize+1);
1619 if (atn==NULL) {
1620 return gbBAD ;
1621 }
1622
1623 // populate the node structure with data
1624 atn->size = datasize;
1625 memcpy( ALIAS_TREE_DATA(atn), alias_name, datasize+1 ) ;
1626
1627 return Cache_Get_Alias_Persistent( sn, atn ) ;
1628 }
1629
1630 /* look in persistent alias->sn tree */
Cache_Get_Alias_Persistent(BYTE * sn,struct alias_tree_node * atn)1631 static GOOD_OR_BAD Cache_Get_Alias_Persistent( BYTE * sn, struct alias_tree_node * atn)
1632 {
1633 struct tree_opaque *opaque;
1634 GOOD_OR_BAD ret = gbBAD ;
1635
1636 PERSISTENT_RLOCK;
1637 opaque = tfind(atn, &cache.persistent_alias_tree, alias_tree_compare) ;
1638 if ( opaque != NULL ) {
1639 memcpy( sn, ((struct alias_tree_node *)(opaque->key))->sn, SERIAL_NUMBER_SIZE ) ;
1640 LEVEL_DEBUG("Lookup of %s gives "SNformat, CONST_ALIAS_TREE_DATA(atn), SNvar(sn) ) ;
1641 ret = gbGOOD ;
1642 } else {
1643 LEVEL_DEBUG("Lookup of %s unsuccessful",CONST_ALIAS_TREE_DATA(atn)) ;
1644 }
1645 PERSISTENT_RUNLOCK;
1646 owfree(atn) ;
1647 return ret;
1648 }
1649
1650 /* Alias name must be a null-terminated string */
Cache_Del_Alias_SN(const ASCII * alias_name)1651 static void Cache_Del_Alias_SN(const ASCII * alias_name)
1652 {
1653 // allocate space for the node and data
1654 size_t datasize = strlen(alias_name) ;
1655 struct alias_tree_node *atn = (struct alias_tree_node *) owmalloc(sizeof(struct alias_tree_node) + datasize + 1);
1656
1657 if (atn==NULL) {
1658 return ;
1659 }
1660
1661 // populate the node structure with data
1662 atn->expires = NOW_TIME;
1663 atn->size = datasize;
1664 memcpy( ALIAS_TREE_DATA(atn), alias_name, datasize+1 ) ;
1665
1666 Cache_Del_Alias_Persistent( atn ) ;
1667 }
1668
1669 /* look in persistent alias->sn tree */
Cache_Del_Alias_Persistent(struct alias_tree_node * atn)1670 static void Cache_Del_Alias_Persistent( struct alias_tree_node * atn)
1671 {
1672 struct tree_opaque *opaque;
1673 struct alias_tree_node *atn_found = NULL;
1674
1675 PERSISTENT_RLOCK;
1676 opaque = tfind(atn, &cache.persistent_alias_tree, alias_tree_compare) ;
1677 if ( opaque != NULL ) {
1678 atn_found = (struct alias_tree_node *) (opaque->key);
1679 }
1680 PERSISTENT_RUNLOCK;
1681 owfree(atn_found) ;
1682 }
1683
1684 /* Delete bus from alias name */
1685 /* Alias name must be a null-terminated string */
Cache_Del_Alias_Bus(const ASCII * alias_name)1686 void Cache_Del_Alias_Bus(const ASCII * alias_name)
1687 {
1688 // Cheat -- just change to a bad bus value
1689 LEVEL_DEBUG("Hide %s",alias_name) ;
1690 Cache_Add_Alias_Bus( alias_name, INDEX_BAD ) ;
1691 }
1692