MIDAS
Loading...
Searching...
No Matches
odb.cxx
Go to the documentation of this file.
1/********************************************************************\
2
3 Name: ODB.C
4 Created by: Stefan Ritt
5
6 Contents: MIDAS online database functions
7
8 $Id$
9
10\********************************************************************/
11
12#undef NDEBUG // midas required assert() to be always enabled
13
28#ifndef DOXYGEN_SHOULD_SKIP_THIS
29
30#include "midas.h"
31#include "msystem.h"
32#include "mxml.h"
33#include "git-revision.h"
34#include "mstrlcpy.h"
35#include <assert.h>
36#include <signal.h>
37#include <math.h>
38
39#include <deque>
40
41#include "mjson.h"
42
43#define CHECK_OPEN_RECORD 1
44
45/*------------------------------------------------------------------*/
46
47/********************************************************************\
48* *
49* db_xxx - Database Functions *
50* *
51\********************************************************************/
52
53/* Globals */
54
55#ifdef LOCAL_ROUTINES
58#endif
59
62
65
66INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER * writer);
67
68/*------------------------------------------------------------------*/
69
70#ifdef LOCAL_ROUTINES
71
73
74static void db_msg(db_err_msg** msg, INT message_type, const char *filename, INT line, const char *routine, const char *format, ...) MATTRPRINTF(6,7);
75static void db_print_msg(const db_err_msg* msg);
76static void db_flush_msg(db_err_msg** msg);
77
78static bool db_validate_key_offset(const DATABASE_HEADER * pheader, int offset);
79static bool db_validate_data_offset(const DATABASE_HEADER * pheader, int offset);
80
81static HNDLE db_pkey_to_hkey(const DATABASE_HEADER* pheader, const KEY* pkey);
82
83static const KEY* db_get_pkey( const DATABASE_HEADER* pheader, HNDLE hKey, int* pstatus, const char* caller, db_err_msg **msg);
84static const KEY* db_get_parent(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, const char* caller, db_err_msg **msg);
85
86static const KEYLIST* db_get_pkeylist(const DATABASE_HEADER* pheader, const KEY* pkey, const char* caller, db_err_msg **msg, bool kludge_repair = false);
87
88static INT db_find_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, const char *key_name, bool follow_links, HNDLE * subhKey, db_err_msg** msg);
89static const KEY* db_find_pkey_locked(const DATABASE_HEADER *pheader, const KEY* pkey, const char *key_name, bool follow_links, int *pstatus, db_err_msg** msg);
90
91static std::string db_get_path_locked(const DATABASE_HEADER* pheader, HNDLE hKey);
92static std::string db_get_path_pkey(const DATABASE_HEADER* pheader, const KEY *pkey);
93
94static int db_scan_tree_locked(const DATABASE_HEADER* pheader, const KEY* pkey, int level, int(*callback) (const DATABASE_HEADER*, const KEY*, int, void*, db_err_msg**), void *info, db_err_msg** msg);
95
96static int db_set_mode_wlocked(DATABASE_HEADER*,KEY*,WORD mode,int recurse,db_err_msg**);
97static const KEY* db_resolve_link_locked(const DATABASE_HEADER*, const KEY*,int *pstatus, db_err_msg**);
98static int db_notify_clients_locked(const DATABASE* pdb, HNDLE hKeyMod, int index, BOOL bWalk, db_err_msg** msg);
99static int db_create_key_wlocked(DATABASE_HEADER* pheader, KEY* parentKey, const char *key_name, DWORD type, KEY** pnewkey, db_err_msg** msg);
100static int db_delete_key_locked(DATABASE* pdb, const KEY* pkey, int level, db_err_msg** msg);
101static int db_set_value_wlocked(DATABASE* pdb, KEY* pkey_root, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type, db_err_msg** msg);
102static INT db_get_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, void *data, INT * buf_size, DWORD type, db_err_msg** msg);
103static INT db_set_data_wlocked(DATABASE_HEADER* pheader, KEY* pkey, const void *data, INT data_size, INT num_values, DWORD type, const char* caller, db_err_msg** msg);
104static INT db_set_data_index_wlocked(DATABASE_HEADER* pheader, KEY* pkey, int idx, const void *data, INT data_size, DWORD type, const char* caller, db_err_msg** msg);
105static INT db_check_set_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, const void *data, INT data_size, INT num_values, DWORD type, const char* caller, db_err_msg** msg);
106static INT db_check_set_data_index_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, const void *data, INT data_size, DWORD type, const char* caller, db_err_msg** msg);
108
109static DATABASE* db_lock_database(HNDLE hDB, int *pstatus, const char* caller, bool check_attached = true);
110static void db_unlock_database(DATABASE* pdb, const char* caller);
111
112#endif // LOCAL_ROUTINES
113
114/*------------------------------------------------------------------*/
115
116/********************************************************************\
117* *
118* db_msg_xxx error message handling *
119* *
120\********************************************************************/
121
122#ifdef LOCAL_ROUTINES
123
125{
128 std::string filename;
129 int line = 0;
130 std::string routine;
131 std::string text;
132};
133
134static db_err_msg* _last_error_message = NULL; // for debuging core dumps
135
136void db_print_msg(const db_err_msg* msg)
137{
138 while (msg != NULL) {
139 printf("db_err_msg: %p, next %p, type %d, file \'%s:%d\', function \'%s\': %s\n", msg, msg->next, msg->message_type, msg->filename.c_str(), msg->line, msg->routine.c_str(), msg->text.c_str());
140 msg = msg->next;
141 }
142}
143
144void db_msg(db_err_msg** msgp, INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
145{
146 if (!msgp)
147 return;
148
149 va_list argptr;
150 char message[1000];
151
152 /* print argument list into message */
153 va_start(argptr, format);
154 vsnprintf(message, sizeof(message)-1, format, argptr);
155 va_end(argptr);
156 message[sizeof(message)-1] = 0; // ensure string is NUL-terminated
157
158 if (strlen(message) >= sizeof(message) - 2) {
159 const char* s = "!db_msg too long!";
160 memcpy(message + strlen(message) - strlen(s), s, strlen(s));
161 }
162
163 db_err_msg* msg = new db_err_msg;
164
165 msg->next = NULL;
166 msg->message_type = message_type;
167 msg->filename = filename;
168 msg->line = line;
169 msg->routine = routine;
170 msg->text = message;
171
173
174 //printf("new message:\n");
175 //db_print_msg(msg);
176
177 if (*msgp == NULL) {
178 *msgp = msg;
179 return;
180 }
181
182 // append new message to the end of the list
183 db_err_msg *m = (*msgp);
184 while (m->next != NULL) {
185 m = m->next;
186 }
187 assert(m->next == NULL);
188 m->next = msg;
189
190 //printf("Message list with added new message:\n");
191 //db_print_msg(*msgp);
192 return;
193}
194
196{
197 db_err_msg *msg = *msgp;
198 *msgp = NULL;
199
200 if (/* DISABLES CODE */ (0)) {
201 printf("db_flush_msg: %p\n", msg);
202 db_print_msg(msg);
203 }
204
205 while (msg != NULL) {
206 cm_msg(msg->message_type, msg->filename.c_str(), msg->line, msg->routine.c_str(), "%s", msg->text.c_str());
207 db_err_msg* next = msg->next;
208 msg->message_type = 0;
209 msg->next = NULL;
210 delete msg;
211 msg = next;
212 }
213}
214
215#endif // LOCAL_ROUTINES
216
217/*------------------------------------------------------------------*/
218
219#ifdef LOCAL_ROUTINES
220
221/********************************************************************\
222* *
223* Shared Memory Allocation *
224* *
225\********************************************************************/
226
227/*------------------------------------------------------------------*/
228static void *malloc_key(DATABASE_HEADER * pheader, INT size, const char* caller)
229{
230 // Notes:
231 // we allocate objects:
232 // KEY aligned size 72
233 // KEYLIST aligned size 16
234 // they are not multiples of each other and we need to be
235 // careful to avoid 8-byte allocation holes.
236 // K.O.
237
238 if (size == 0)
239 return NULL;
240
241 assert(size == sizeof(KEY) || size == sizeof(KEYLIST));
242
243 /* quadword alignment for alpha CPU */
244 size = ALIGN8(size);
245
246 //printf("malloc_key(%d) from [%s]\n", size, caller);
247
248 if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
249 return NULL;
250 }
251
252 if (pheader->first_free_key == 0) { // no free blocks!
253 return NULL;
254 }
255
256 /* search for free block */
257
258 bool have_good_block = false;
259 int pass = 1;
260 FREE_DESCRIP* pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
261 FREE_DESCRIP* pprev = NULL;
262
263 while (1) {
264 //printf("pfree %p, size %3d, next_free %7d, pprev %p, want %3d, pass %d\n", pfree, pfree->size, pfree->next_free, pprev, size, pass);
265
266 if (pfree->size >= size) { // found free block
267 have_good_block = true;
268 if (pass == 1) {
269 // do not use a free block if remaining size is smaller than 16 (aligned size of KEYLIST)
270 // because it will become dead space we cannot use for anything. K.O.
271 int remaining = pfree->size - size;
272 if ((remaining == 0) || (remaining >= (int)ALIGN8(sizeof(KEYLIST))))
273 break;
274 } else {
275 // first pass could not find any free blocks we like,
276 // second pass will use whatever fits. K.O.
277 break;
278 }
279 }
280
281 if (pfree->next_free == 0) { // last block
282 //printf("pfree %p, size %3d, next_free %7d, pprev %p, want %3d, pass %d, out of free blocks, have_good_block %d\n", pfree, pfree->size, pfree->next_free, pprev, size, pass, have_good_block);
283 if (pass == 1 && have_good_block) {
284 // try again from scratch
285 pass = 2;
286 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
287 pprev = NULL;
288 continue;
289 }
290 return NULL;
291 }
292
293 if (!db_validate_key_offset(pheader, pfree->next_free)) {
294 return NULL;
295 }
296
297 pprev = pfree;
298 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
299
300 if (pfree == pprev) {
301 // we are in an infinite loop
302 return NULL;
303 }
304 }
305
306 //printf("found free block %p size %d, want %d\n", pfree, pfree->size, size);
307
308 assert(pfree->size >= size);
309
310 FREE_DESCRIP* pfound = pfree;
311
312 /* if found block is first in list, correct pheader */
313 if (pfree == (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key)) {
314 if (size < pfree->size) {
315 /* free block is only used partially */
316 pheader->first_free_key += size;
317 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
318
319 pfree->size = pfound->size - size;
320 pfree->next_free = pfound->next_free;
321 } else {
322 /* free block is used totally */
323 pheader->first_free_key = pfree->next_free;
324 }
325 } else {
326 /* check if free block is used totally */
327 if (pfound->size - size < (int) sizeof(FREE_DESCRIP)) {
328 if (pfound->next_free == 0) {
329 // we are about to kill the very last free block
330 // let's not do it, free_key() cannot recover from this
331 // and we lose most of the free space. K.O.
332 //printf("found free block %p size %d, want %d, this will kill the last free block\n", pfree, pfree->size, size);
333 return NULL;
334 }
335 /* skip block totally */
336 pprev->next_free = pfound->next_free;
337 } else {
338 /* decrease free block */
339 pfree = (FREE_DESCRIP *) ((char *) pfound + size);
340
341 pfree->size = pfound->size - size;
342 pfree->next_free = pfound->next_free;
343
344 pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
345 }
346 }
347
348 assert((void*)pfound != (void*)pheader);
349
350 memset(pfound, 0, size);
351
352 return pfound;
353}
354
355/*------------------------------------------------------------------*/
356static void free_key(DATABASE_HEADER * pheader, void *address, INT size)
357{
358 FREE_DESCRIP *pfree, *pprev, *pnext;
359
360 if (size == 0)
361 return;
362
363 assert(address != pheader);
364
365 /* quadword alignment for alpha CPU */
366 size = ALIGN8(size);
367
368 pfree = (FREE_DESCRIP *) address;
369 pprev = NULL;
370
371 /* clear current block */
372 memset(address, 0, size);
373
374 /* if key comes before first free block, adjust pheader */
375 if ((POINTER_T) address - (POINTER_T) pheader < pheader->first_free_key) {
376 pfree->size = size;
377 pfree->next_free = pheader->first_free_key;
378 pheader->first_free_key = (POINTER_T) address - (POINTER_T) pheader;
379 } else {
380 /* find last free block before current block */
381 pprev = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
382
383 while (pprev->next_free < (POINTER_T) address - (POINTER_T) pheader) {
384 if (pprev->next_free <= 0) {
385 cm_msg(MERROR, "free_key", "database is corrupted: pprev=%p, pprev->next_free=%d", pprev, pprev->next_free);
386 return;
387 }
388 pprev = (FREE_DESCRIP *) ((char *) pheader + pprev->next_free);
389 }
390
391 pfree->size = size;
392 pfree->next_free = pprev->next_free;
393
394 pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
395 }
396
397 /* try to melt adjacent free blocks after current block */
398 pnext = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
399 if ((POINTER_T) pnext == (POINTER_T) pfree + pfree->size) {
400 pfree->size += pnext->size;
401 pfree->next_free = pnext->next_free;
402
403 memset(pnext, 0, pnext->size);
404 }
405
406 /* try to melt adjacent free blocks before current block */
407 if (pprev && pprev->next_free == (POINTER_T) pprev - (POINTER_T) pheader + pprev->size) {
408 pprev->size += pfree->size;
409 pprev->next_free = pfree->next_free;
410
411 memset(pfree, 0, pfree->size);
412 }
413}
414
416{
417 if (free_data <= 0)
418 return 0;
419
420 if (free_data < (int)sizeof(DATABASE_HEADER)) {
421 //printf("validate_free_data: failed: %d is inside the database header 0..%d\n", free_data, (int)sizeof(DATABASE_HEADER));
422 return 0;
423 }
424
425 if (free_data < (int)sizeof(DATABASE_HEADER) + pheader->key_size) {
426 //printf("validate_free_data: failed: %d is inside key space %d..%d\n", free_data, (int)sizeof(DATABASE_HEADER), (int)sizeof(DATABASE_HEADER) + pheader->key_size);
427 return 0;
428 }
429
430 if (free_data > (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size) {
431 //printf("validate_free_data: failed: %d is beyound end of odb %d+%d+%d = %d\n", free_data, (int)sizeof(DATABASE_HEADER), pheader->key_size, pheader->data_size, (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size);
432 return 0;
433 }
434
435 return 1;
436}
437
438/*------------------------------------------------------------------*/
439static void *malloc_data(DATABASE_HEADER * pheader, INT size)
440{
441 if (size == 0)
442 return NULL;
443
444 assert(size > 0);
445
446 /* quadword alignment for alpha CPU */
447 size = ALIGN8(size);
448
449 /* smallest allocation size is 8 bytes to make sure we can always create a new FREE_DESCRIP in free_data() */
450 assert(size >= (int)sizeof(FREE_DESCRIP));
451
452 if (!validate_free_data(pheader, pheader->first_free_data)) {
453 return NULL;
454 }
455
456 /* search for free block */
457 FREE_DESCRIP *pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
458 FREE_DESCRIP *pprev = NULL;
459 FREE_DESCRIP *pfound = NULL;
460
461 while (1) {
462 //printf("malloc_data: pprev %p, pfree %p, next %d, size %d, want %d\n", pprev, pfree, pfree->next_free, pfree->size, size);
463
464 if (pfree->size >= size) {
465 // we will use this block
466 pfound = pfree;
467 break;
468 }
469
470 if (!pfree->next_free) {
471 // no more free blocks
472 return NULL;
473 }
474
475 if (!validate_free_data(pheader, pfree->next_free)) {
476 // next_free is invalid
477 //printf("malloc_data: pprev %p, pfree %p, next %d, size %d, next is invalid\n", pprev, pfree, pfree->next_free, pfree->size);
478 return NULL;
479 }
480 pprev = pfree;
481 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
482
483 if (pfree == pprev) {
484 // we are in an infinite loop
485 return NULL;
486 }
487 }
488
489 //printf("malloc_data: pprev %p, pfound %p, size %d, want %d\n", pprev, pfound, pfound->size, size);
490
491 assert(pfound != NULL);
492 assert(size <= pfound->size);
493
494 /* if found block is first in list, correct pheader */
495 if (!pprev) {
496 if (size < pfree->size) {
497 /* free block is only used partially */
498 pheader->first_free_data += size;
499 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
500
501 pfree->size = pfound->size - size;
502 pfree->next_free = pfound->next_free;
503 } else {
504 /* free block is used totally */
505 pheader->first_free_data = pfree->next_free;
506 }
507 } else {
508 /* check if free block is used totally */
509 if (pfound->size - size < (int) sizeof(FREE_DESCRIP)) {
510 /* delete this free block from the free blocks chain */
511 pprev->next_free = pfound->next_free;
512 } else {
513 /* decrease free block */
514 pfree = (FREE_DESCRIP *) ((char *) pfound + size);
515
516 pfree->size = pfound->size - size;
517 pfree->next_free = pfound->next_free;
518
519 pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
520 }
521 }
522
523 assert((void*)pfound != (void*)pheader);
524
525 /* zero memeory */
526 memset(pfound, 0, size);
527
528 return pfound;
529}
530
531/*------------------------------------------------------------------*/
532static int free_data(DATABASE_HEADER * pheader, void *address, INT size, const char* caller)
533{
534 if (size == 0)
535 return DB_SUCCESS;
536
537 assert(address != pheader);
538
539 /* quadword alignment for alpha CPU */
540 size = ALIGN8(size);
541
542 /* smallest allocation size is 8 bytes to make sure we can always create a new FREE_DESCRIP in free_data() */
543 assert(size >= (int)sizeof(FREE_DESCRIP));
544
545 FREE_DESCRIP *pprev = NULL;
546 FREE_DESCRIP *pfree = (FREE_DESCRIP *) address;
547 int pfree_offset = (POINTER_T) address - (POINTER_T) pheader;
548
549 /* clear current block */
550 memset(address, 0, size);
551
552 if (pheader->first_free_data == 0) {
553 /* if free list is empty, create the first free block, adjust pheader */
554 pfree->size = size;
555 pfree->next_free = 0;
556 pheader->first_free_data = pfree_offset;
557 /* nothing else to do */
558 return DB_SUCCESS;
559 } else if ((POINTER_T) address - (POINTER_T) pheader < pheader->first_free_data) {
560 /* if data comes before first free block, create new free block, adjust pheader */
561 pfree->size = size;
562 pfree->next_free = pheader->first_free_data;
563 pheader->first_free_data = pfree_offset;
564 /* maybe merge next free block into the new free block */
565 //printf("free_data: created new first free block, maybe merge with old first free block\n");
566 } else {
567 /* find last free block before current block */
568 pprev = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
569
570 while (pprev->next_free < pfree_offset) {
571 if (pprev->next_free == 0) {
572 /* add new free block at the end of the chain of free blocks */
573 //printf("free_data: adding new free block at the very end\n");
574 break;
575 }
576 if (!validate_free_data(pheader, pprev->next_free)) {
577 cm_msg(MERROR, "free_data", "database is corrupted: pprev=%p, pprev->next_free=%d in free_data(%p,%p,%d) from %s", pprev, pprev->next_free, pheader, address, size, caller);
578 return DB_CORRUPTED;
579 }
580
581 pprev = (FREE_DESCRIP *) ((char *) pheader + pprev->next_free);
582 }
583
584 pfree->size = size;
585 pfree->next_free = pprev->next_free;
586
587 pprev->next_free = pfree_offset;
588 }
589
590 /* try to melt adjacent free blocks after current block */
591 FREE_DESCRIP *pnext = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
592 if ((POINTER_T) pnext == (POINTER_T) pfree + pfree->size) {
593 //printf("free_data: merging first and second free block\n");
594 pfree->size += pnext->size;
595 pfree->next_free = pnext->next_free;
596
597 memset(pnext, 0, pnext->size);
598 }
599
600 /* try to melt adjacent free blocks before current block */
601 if (pprev && pprev->next_free == (POINTER_T) pprev - (POINTER_T) pheader + pprev->size) {
602 //printf("free_data: merging pprev and pfree\n");
603 pprev->size += pfree->size;
604 pprev->next_free = pfree->next_free;
605
606 memset(pfree, 0, pfree->size);
607 }
608
609 return DB_SUCCESS;
610}
611
612/*------------------------------------------------------------------*/
613static void *realloc_data(DATABASE_HEADER * pheader, void *address, INT old_size, INT new_size, const char* caller)
614{
615 void *tmp = NULL;
616
617 if (old_size) {
618 int status;
619 tmp = malloc(old_size);
620 if (tmp == NULL) {
621 cm_msg(MERROR, "realloc_data", "cannot malloc(%d), called from %s", old_size, caller);
622 return NULL;
623 }
624
625 memcpy(tmp, address, old_size);
626
627 status = free_data(pheader, address, old_size, caller);
628 if (status != DB_SUCCESS) {
629 free(tmp);
630 cm_msg(MERROR, "realloc_data", "cannot free_data(%p, %d), called from %s", address, old_size, caller);
631 return NULL;
632 }
633 }
634
635 void *pnew = malloc_data(pheader, new_size);
636
637 if (!pnew) {
638 if (tmp)
639 free(tmp);
640 cm_msg(MERROR, "realloc_data", "cannot malloc_data(%d), called from %s", new_size, caller);
641 return NULL;
642 }
643
644 if (old_size) {
645 memcpy(pnew, tmp, old_size < new_size ? old_size : new_size);
646 free(tmp);
647 }
648
649 return pnew;
650}
651
652#endif // LOCAL_ROUTINES
653
654/*------------------------------------------------------------------*/
655char *strcomb(const char **list)
656/* convert list of strings into single string to be used by db_paste() */
657{
658 INT i, j;
659 static char *str = NULL;
660
661 /* counter number of chars */
662 for (i = 0, j = 0; list[i]; i++)
663 j += strlen(list[i]) + 1;
664 j += 1;
665
666 if (str == NULL)
667 str = (char *) malloc(j);
668 else
669 str = (char *) realloc(str, j);
670
671 assert(str != NULL);
672
673 str[0] = 0;
674 for (i = 0; list[i]; i++) {
675 strcat(str, list[i]);
676 strcat(str, "\n");
677 }
678
679 return str;
680}
681
682/*------------------------------------------------------------------*/
683
684std::string strcomb1(const char **list)
685/* convert list of strings into single string to be used by db_paste() */
686{
687 std::string s;
688
689 for (int i = 0; list[i]; i++) {
690 s += list[i];
691 s += "\n";
692 }
693
694 return s;
695}
696
697/*------------------------------------------------------------------*/
698
699#ifdef LOCAL_ROUTINES
700
702 int type = 0;
704 size_t skey = 0;
706 size_t sdata = 0;
708 HNDLE hsort = 0; // value to sort on
709};
710
711bool operator<(const keylist_struct& a, const keylist_struct& b) {
712 return a.hsort < b.hsort;
713}
714
715typedef std::vector<keylist_struct> keylist_vector;
716
717static bool db_get_key_free_list_locked(const DATABASE_HEADER* pheader, keylist_vector* keyfreelist, db_err_msg **msg)
718{
719 /* validate the key free list */
720
721 keyfreelist->clear();
722
723 if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
724 db_msg(msg, MERROR, "db_get_key_free_list", "Error: database corruption, invalid pheader->first_free_key %d", pheader->first_free_key);
725 return false;
726 }
727
728 const FREE_DESCRIP* pfree = (const FREE_DESCRIP *) ((const char *) pheader + pheader->first_free_key);
729
730 for (int i=0; i<pheader->key_size; i++) { // avoid infinite loop
731
733 ks.hkey = (char*)pfree - (char*)pheader;
734 ks.skey = pfree->size;
735 ks.next_free = pfree->next_free;
736 keyfreelist->push_back(ks);
737
738 if (pfree->next_free == 0) { // end of free list
739 return true;
740 }
741
742 if (!db_validate_key_offset(pheader, pfree->next_free)) {
743 db_msg(msg, MERROR, "db_get_key_free_list", "Error: database corruption, invalid pfree->next_free %d", pfree->next_free);
744 return false;
745 }
746
747 pfree = (const FREE_DESCRIP *) ((const char *) pheader + pfree->next_free);
748 }
749
750 db_msg(msg, MERROR, "db_get_key_free_list", "Error: database corruption, free list is too long: %zu", keyfreelist->size());
751
752 return false;
753}
754
755static bool db_get_data_free_list_locked(const DATABASE_HEADER* pheader, keylist_vector* datafreelist, db_err_msg **msg)
756{
757 /* validate the data free list */
758
759 datafreelist->clear();
760
761 if (!db_validate_data_offset(pheader, pheader->first_free_data)) {
762 db_msg(msg, MERROR, "db_get_data_free_list", "Error: database corruption, invalid pheader->first_free_data %d", pheader->first_free_data);
763 return false;
764 }
765
766 const FREE_DESCRIP* pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
767
768 for (int i=0; i<pheader->data_size; i++) { // avoid infinite loop
769
771 ks.hdata = (char*)pfree - (char*)pheader;
772 ks.sdata = pfree->size;
773 ks.next_free = pfree->next_free;
774 datafreelist->push_back(ks);
775
776 if (pfree->next_free == 0) { // end of free list
777 return true;
778 }
779
780 if (!db_validate_data_offset(pheader, pfree->next_free)) {
781 db_msg(msg, MERROR, "db_get_key_free_list", "Error: database corruption, invalid pfree->next_free %d", pfree->next_free);
782 return false;
783 }
784
785 pfree = (const FREE_DESCRIP *) ((const char *) pheader + pfree->next_free);
786 }
787
788 db_msg(msg, MERROR, "db_get_data_free_list", "Error: database corruption, free list is too long: %zu", datafreelist->size());
789
790 return false;
791}
792
793static INT print_key_info_locked(const DATABASE_HEADER* pheader, const KEY* pkey, INT level, void *info, db_err_msg**msg)
794{
795 std::string* pbuf = (std::string*)info;
796
797 HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
798
799 *pbuf += msprintf("%9d %9d %9d ",
800 (int) (hKey - sizeof(DATABASE_HEADER)),
801 (int) (pkey->data - sizeof(DATABASE_HEADER)), (int) pkey->total_size);
802
803 for (int i = 0; i < level; i++)
804 *pbuf += " ";
805
806 *pbuf += pkey->name;
807 *pbuf += "\n";
808
809 return SUCCESS;
810}
811
813{
814 std::string buf;
815
816 bool flag;
817 int status;
818
819 DATABASE* pdb = db_lock_database(hDB, &status, "db_show_mem");
820
821 if (!pdb)
822 return "(Cannot lock ODB)";
823
824 db_err_msg *msg = NULL;
825
826 const DATABASE_HEADER *pheader = pdb->database_header;
827
828 size_t odb_key_start = sizeof(DATABASE_HEADER);
829 size_t odb_key_end = sizeof(DATABASE_HEADER) + pheader->key_size;
830
831 size_t odb_data_start = odb_key_end;
832 size_t odb_data_end = odb_data_start + pheader->data_size;
833
834 buf += msprintf("Database header: %9d - %9zu, size %9zu bytes\n", 0, sizeof(DATABASE_HEADER), sizeof(DATABASE_HEADER));
835 buf += msprintf("Key area: %9zu - %9zu, size %9d bytes\n", odb_key_start, odb_key_end, pheader->key_size);
836 buf += msprintf("Data area: %9zu - %9zu, size %9d bytes\n", odb_data_start, odb_data_end, pheader->data_size);
837 buf += "\n";
838 buf += "Key free list:\n";
839 buf += "--------\n";
840
841 keylist_vector keyfreelist;
842
843 flag = db_get_key_free_list_locked(pheader, &keyfreelist, &msg);
844
845 size_t total_size_key = 0;
846
847 if (!flag) {
848 buf += "ODB key free list is corrupted, see messages!\n\n";
849 }
850
851 for (size_t i=0; i<keyfreelist.size(); i++) {
852 buf += msprintf("Free block %3zu at %9d, size %9zu, next %9d\n",
853 i,
854 keyfreelist[i].hkey,
855 keyfreelist[i].skey,
856 keyfreelist[i].next_free);
857 total_size_key += keyfreelist[i].skey;
858 }
859
860 buf += "\n";
861 buf += msprintf("Free Key area: %zu bytes out of %d bytes\n", total_size_key, pheader->key_size);
862 buf += "\n";
863 buf += "Data free list:\n";
864 buf += "-----\n";
865
866 keylist_vector datafreelist;
867
868 flag = db_get_data_free_list_locked(pheader, &datafreelist, &msg);
869
870 size_t total_size_data = 0;
871 size_t biggest_size_data = 0;
872
873 if (!flag) {
874 buf += "ODB data free list is corrupted, see messages!\n\n";
875 }
876
877 for (size_t i=0; i<datafreelist.size(); i++) {
878 buf += msprintf("Free block %3zu at %9d, size %9zu, next %9d\n",
879 i,
880 datafreelist[i].hdata,
881 datafreelist[i].sdata,
882 datafreelist[i].next_free);
883 total_size_data += datafreelist[i].sdata;
884 if (datafreelist[i].sdata > biggest_size_data)
885 biggest_size_data = datafreelist[i].sdata;
886 }
887
888 buf += "\n";
889 buf += msprintf("Free Data area: %zu bytes out of %d bytes\n", total_size_data, pheader->data_size);
890 buf += "\n";
891 buf += msprintf("Biggest Data area free block: %zu bytes\n", biggest_size_data);
892 buf += "\n";
893 buf += msprintf("Free: %zu (%1.1lf%%) keylist, %zu (%1.1lf%%) data\n",
894 total_size_key,
895 100 * (double) total_size_key / pheader->key_size,
896 total_size_data, 100 * (double) total_size_data / pheader->data_size);
897
898 if (verbose) {
899 const KEY* proot = db_get_pkey(pheader, 0, 0, "db_show_mem", &msg);
900
901 if (proot) {
902 buf += "\n";
903 buf += "\n";
904 buf += " Key Data Size\n";
905 buf += "-----------------------------\n";
906 db_scan_tree_locked(pheader, proot, 0, print_key_info_locked, &buf, &msg);
907 buf += "-----------------------------\n";
908 buf += " Key Data Size\n";
909 }
910 }
911
912 buf += "\n";
913 buf += msprintf("Total ODB size: %d (0x%08X) Bytes, %lg MBytes\n",
914 pheader->key_size + pheader->data_size, pheader->key_size + pheader->data_size,
915 ((pheader->key_size + pheader->data_size) / 1E6));
916
917 db_unlock_database(pdb, "db_show_mem");
918
919 if (msg)
920 db_flush_msg(&msg);
921
922 return buf;
923}
924
925INT db_get_free_mem(HNDLE hDB, size_t *pkey_size, size_t *pdata_size, size_t *pdata_biggest)
926{
927 bool flag = true;
928
929 *pkey_size = 0;
930 *pdata_size = 0;
931 *pdata_biggest = 0;
932
933 int status = 0;
934
935 DATABASE* pdb = db_lock_database(hDB, &status, "db_get_free_mem");
936
937 if (!pdb)
938 return status;
939
940 db_err_msg *msg = NULL;
941
942 const DATABASE_HEADER* pheader = pdb->database_header;
943
944 keylist_vector keyfreelist;
945
946 flag &= db_get_key_free_list_locked(pheader, &keyfreelist, &msg);
947
948 for (const auto e : keyfreelist) {
949 *pkey_size += e.skey;
950 }
951
952 keylist_vector datafreelist;
953
954 flag &= db_get_data_free_list_locked(pheader, &datafreelist, &msg);
955
956 for (const auto e : datafreelist) {
957 *pdata_size += e.sdata;
958 if (e.sdata > *pdata_biggest)
959 *pdata_biggest = e.sdata;
960 }
961
962 db_unlock_database(pdb, "db_get_free_mem");
963
964 if (msg)
965 db_flush_msg(&msg);
966
967 if (!flag)
968 return DB_CORRUPTED;
969
970 return DB_SUCCESS;
971}
972
973#endif // LOCAL_ROUTINES
974
975// Method to check if a given string is valid UTF-8. Returns 1 if it is.
976// This method was taken from stackoverflow user Christoph, specifically
977// http://stackoverflow.com/questions/1031645/how-to-detect-utf-8-in-plain-c
978static bool is_utf8(const char * string)
979{
980 if (!string)
981 return false;
982
983 return ss_is_valid_utf8(string);
984}
985
986#ifdef LOCAL_ROUTINES
987
988/*------------------------------------------------------------------*/
989static int db_validate_name(const char* name, int maybe_path, const char* caller_name, db_err_msg** msg)
990{
991 //printf("db_validate_name [%s] length %d, maybe_path %d from %s\n", name, (int)strlen(name), maybe_path, caller_name);
992
993 if (name == NULL) {
994 db_msg(msg, MERROR, "db_validate_name", "Invalid name passed to %s: should not be NULL", caller_name);
995 return DB_INVALID_NAME;
996 }
997
998 size_t len = strlen(name);
999
1000 if (len < 1) {
1001 db_msg(msg, MERROR, "db_validate_name", "Invalid name passed to %s: should not be an empty string", caller_name);
1002 return DB_INVALID_NAME;
1003 }
1004
1005 if (strchr(name, '[')) {
1006 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"[\"", name, caller_name);
1007 return DB_INVALID_NAME;
1008 }
1009
1010 if (name[0] == ' ') {
1011 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not start with a space", name, caller_name);
1012 return DB_INVALID_NAME;
1013 }
1014
1015 if (name[len-1] == ' ') {
1016 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not end with a space", name, caller_name);
1017 return DB_INVALID_NAME;
1018 }
1019
1020 if (strchr(name, ']')) {
1021 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"[\"", name, caller_name);
1022 return DB_INVALID_NAME;
1023 }
1024
1025 if (!is_utf8(name)) {
1026 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: invalid unicode UTF-8 encoding", name, caller_name);
1027 return DB_INVALID_NAME;
1028 }
1029
1030 if (!maybe_path) {
1031 if (strchr(name, '/')) {
1032 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"/\"", name, caller_name);
1033 return DB_INVALID_NAME;
1034 }
1035
1036 if (strlen(name) >= NAME_LENGTH) {
1037 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: length %d should be less than %d bytes", name, caller_name, (int)strlen(name), NAME_LENGTH);
1038 return DB_INVALID_NAME;
1039 }
1040 }
1041
1042 //if (strcmp(name, "test")==0)
1043 //return DB_INVALID_NAME;
1044
1045 return DB_SUCCESS;
1046}
1047
1048/*------------------------------------------------------------------*/
1049static bool db_validate_key_offset(const DATABASE_HEADER * pheader, int offset)
1050/* check if key offset lies in valid range */
1051{
1052 if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
1053 return false;
1054
1055 if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size)
1056 return false;
1057
1058 return true;
1059}
1060
1061static bool db_validate_data_offset(const DATABASE_HEADER * pheader, int offset)
1062/* check if data offset lies in valid range */
1063{
1064 if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
1065 return false;
1066
1067 if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size)
1068 return false;
1069
1070 return true;
1071}
1072
1073static bool db_validate_hkey(const DATABASE_HEADER * pheader, HNDLE hKey)
1074{
1075 if (hKey == 0) {
1076 cm_msg(MERROR, "db_validate_hkey", "Error: invalid zero hkey %d", hKey);
1077 return false;
1078 }
1079 if (!db_validate_key_offset(pheader, hKey)) {
1080 cm_msg(MERROR, "db_validate_hkey", "Error: invalid hkey %d", hKey);
1081 return false;
1082 }
1083 return true;
1084}
1085
1086static const KEY* db_get_pkey(const DATABASE_HEADER* pheader, HNDLE hKey, int* pstatus, const char* caller, db_err_msg **msg)
1087{
1088 BOOL hKey_is_root_key = FALSE;
1089
1090 if (!hKey) {
1091 hKey_is_root_key = TRUE;
1092 hKey = pheader->root_key;
1093 }
1094
1095 /* check if hKey argument is correct */
1096 if (hKey == 0) {
1097 if (pstatus)
1098 *pstatus = DB_INVALID_HANDLE;
1099 return NULL;
1100 }
1101
1102 /* check if hKey argument is correct */
1103 if (!db_validate_key_offset(pheader, hKey)) {
1104 if (pstatus)
1105 *pstatus = DB_INVALID_HANDLE;
1106 return NULL;
1107 }
1108
1109 const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
1110
1111 if (pkey->type < 1 || pkey->type >= TID_LAST) {
1112 DWORD tid = pkey->type;
1113 if (hKey_is_root_key) {
1114 db_msg(msg, MERROR, caller, "db_get_pkey: root_key hkey %d invalid key type %d, database root directory is corrupted", hKey, tid);
1115 if (pstatus)
1116 *pstatus = DB_CORRUPTED;
1117 return NULL;
1118 } else {
1119 std::string path = db_get_path_pkey(pheader, pkey);
1120 db_msg(msg, MERROR, caller, "db_get_pkey: hkey %d path \"%s\" invalid key type %d", hKey, path.c_str(), tid);
1121 }
1122 if (pstatus)
1123 *pstatus = DB_NO_KEY;
1124 return NULL;
1125 }
1126
1127 if (pkey->name[0] == 0) {
1128 std::string path = db_get_path_pkey(pheader, pkey);
1129 db_msg(msg, MERROR, caller, "db_get_pkey: hkey %d path \"%s\" invalid name \"%s\" is empty", hKey, path.c_str(), pkey->name);
1130 if (pstatus)
1131 *pstatus = DB_NO_KEY;
1132 return NULL;
1133 }
1134
1135 return pkey;
1136}
1137
1138static const KEYLIST* db_get_pkeylist(const DATABASE_HEADER* pheader, const KEY* pkey, const char* caller, db_err_msg **msg, bool kludge_repair)
1139{
1140 if (pkey->type != TID_KEY) {
1141 std::string path = db_get_path_pkey(pheader, pkey);
1142 db_msg(msg, MERROR, caller, "db_get_pkeylist: path \"%s\" unexpected call to db_get_pkeylist(), not a subdirectory, pkey->type %d", path.c_str(), pkey->type);
1143 return NULL;
1144 }
1145
1146 if (!db_validate_data_offset(pheader, pkey->data)) {
1147 std::string path = db_get_path_pkey(pheader, pkey);
1148 db_msg(msg, MERROR, caller, "Invalid pkey->data %d at path \"%s\"", pkey->data, path.c_str());
1149 return NULL;
1150 }
1151
1152 const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->data);
1153
1154 if (pkeylist->parent != db_pkey_to_hkey(pheader, pkey)) {
1155 std::string path = db_get_path_pkey(pheader, pkey);
1156 db_msg(msg, MERROR, caller, "Invalid pkeylist->parent %d should be hkey %d at \"%s\"", pkeylist->parent, db_pkey_to_hkey(pheader, pkey), path.c_str());
1157 return NULL;
1158 }
1159
1160 if (pkeylist->first_key == 0 && pkeylist->num_keys != 0) {
1161 if (!kludge_repair) {
1162 std::string path = db_get_path_pkey(pheader, pkey);
1163 db_msg(msg, MERROR, caller, "Invalid pkeylist->first_key %d should be non zero for num_keys %d at \"%s\"", pkeylist->first_key, pkeylist->num_keys, path.c_str());
1164 return NULL;
1165 }
1166
1167 // FIXME: this repair should be done in db_validate_and_repair_key()
1168
1169 // FIXME: this will crash if ODB is write-protected.
1170
1171 std::string path = db_get_path_pkey(pheader, pkey);
1172 db_msg(msg, MERROR, caller, "Repaired invalid num_keys %d when pkeylist->first_key is zero at \"%s\"", pkeylist->num_keys, path.c_str());
1173 ((KEYLIST*)pkeylist)->num_keys = 0;
1174 }
1175
1176 return pkeylist;
1177}
1178
1179static HNDLE db_pkey_to_hkey(const DATABASE_HEADER* pheader, const KEY* pkey)
1180{
1181 return (POINTER_T) pkey - (POINTER_T) pheader;
1182}
1183
1184static const KEY* db_get_parent(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, const char* caller, db_err_msg **msg)
1185{
1186 if (pkey->parent_keylist == 0) {
1187 // they asked for the parent of "/", return "/"
1188 return db_get_pkey(pheader, pheader->root_key, pstatus, caller, msg);
1189 }
1190
1191 if (!db_validate_data_offset(pheader, pkey->parent_keylist)) {
1192 db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkey->parent %d", db_pkey_to_hkey(pheader, pkey), db_get_path_pkey(pheader, pkey).c_str(), pkey->parent_keylist);
1193 if (pstatus)
1194 *pstatus = DB_CORRUPTED;
1195 return NULL;
1196 }
1197
1198 const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
1199
1200 if (pkeylist->first_key == 0 && pkeylist->num_keys != 0) {
1201 db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid parent pkeylist->first_key %d should be non zero for num_keys %d", db_pkey_to_hkey(pheader, pkey), db_get_path_pkey(pheader, pkey).c_str(), pkeylist->first_key, pkeylist->num_keys);
1202 if (pstatus)
1203 *pstatus = DB_CORRUPTED;
1204 return NULL;
1205 }
1206
1207 return db_get_pkey(pheader, pkeylist->parent, pstatus, caller, msg);
1208}
1209
1210static const KEY* db_enum_first_locked(const DATABASE_HEADER *pheader, const KEY* pkey, db_err_msg **msg)
1211{
1212 if (pkey->type != TID_KEY) {
1213 std::string path = db_get_path_pkey(pheader, pkey);
1214 db_msg(msg, MERROR, "db_enum_first_locked", "path \"%s\" tid %d is not a directory", path.c_str(), pkey->type);
1215 return NULL;
1216 }
1217
1218 const KEYLIST *pkeylist = db_get_pkeylist(pheader, pkey, "db_enum_first_locked", msg);
1219
1220 if (!pkeylist) {
1221 // error
1222 return NULL;
1223 }
1224
1225 if (pkeylist->num_keys == 0) {
1226 // empty directory
1227 return NULL;
1228 }
1229
1230 if (pkeylist->first_key == 0) {
1231 // empty directory
1232 return NULL;
1233 }
1234
1235 return db_get_pkey(pheader, pkeylist->first_key, NULL, "db_enum_first_locked", msg);
1236}
1237
1238static const KEY* db_enum_next_locked(const DATABASE_HEADER *pheader, const KEY* pdir, const KEY* pkey, db_err_msg **msg)
1239{
1240 if (pkey->next_key == 0)
1241 return NULL;
1242
1243 return db_get_pkey(pheader, pkey->next_key, NULL, "db_enum_next_locked", msg);
1244}
1245
1246static const KEY* db_resolve_link_locked(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, db_err_msg** msg)
1247{
1248 if (pkey->type != TID_LINK)
1249 return pkey;
1250
1251 if (!db_validate_data_offset(pheader, pkey->data)) {
1252 db_msg(msg, MERROR, "db_resolve_link_locked", "invalid data offset %d for symlink \"%s\"", pkey->data, db_get_path_pkey(pheader, pkey).c_str());
1253 if (pstatus)
1254 *pstatus = DB_CORRUPTED;
1255 return NULL;
1256 }
1257
1258 if (*((char *) pheader + pkey->data) == '/') {
1259 return db_find_pkey_locked(pheader, NULL, (char*)pheader + pkey->data, true, pstatus, msg);
1260 } else {
1261 return db_find_pkey_locked(pheader, pkey, (char*)pheader + pkey->data, true, pstatus, msg);
1262 }
1263}
1264
1265static void db_print_pkey(const DATABASE_HEADER * pheader, const KEY* pkey, int recurse = 0, const char *path = NULL, HNDLE parenthkeylist = 0)
1266{
1267 HNDLE hkey = db_pkey_to_hkey(pheader, pkey);
1268
1269 std::string xpath;
1270 if (path == NULL) {
1271 xpath = db_get_path_pkey(pheader, pkey);
1272 path = xpath.c_str();
1273 }
1274
1275 printf("path \"%s\", parenthkey %d, hkey %d, name \"%s\", type %d, parent %d, data %d, total_size %d", path, parenthkeylist, hkey, pkey->name, pkey->type, pkey->parent_keylist, pkey->data, pkey->total_size);
1276
1277 if (pkey->type != TID_KEY) {
1278 printf("\n");
1279 } else {
1280 const KEYLIST *pkeylist = db_get_pkeylist(pheader, pkey, "db_validate_key", NULL);
1281
1282 if (pkeylist) {
1283 printf(", pkeylist parent %d, num_keys %d, first_key %d", pkeylist->parent, pkeylist->num_keys, pkeylist->first_key);
1284 printf("\n");
1285 }
1286 }
1287}
1288
1289static void db_print_hkey(const DATABASE_HEADER * pheader, HNDLE hkey, int recurse = 0, const char *path = NULL, HNDLE parenthkeylist = 0)
1290{
1291 const KEY *pkey = db_get_pkey(pheader, hkey, NULL, "db_print_key", NULL);
1292
1293 if (!pkey) {
1294 return;
1295 }
1296
1297 db_print_pkey(pheader, pkey, recurse, path, parenthkeylist);
1298}
1299
1300static bool db_validate_and_repair_key_wlocked(DATABASE_HEADER * pheader, int recurse, const char *path, HNDLE parenthkeylist, HNDLE hkey, KEY * pkey, db_err_msg **msg, keylist_vector* keylist)
1301{
1302 int status;
1303 bool flag = true;
1304
1305 if (recurse >= MAX_ODB_PATH) {
1306 db_msg(msg, MERROR, "db_validate_and_repair_key_wlocked", "Max ODB depth level %d exceeded at \"%s\"", MAX_ODB_PATH, path);
1307 return true;
1308 }
1309
1310 //printf("path \"%s\", parenthkey %d, hkey %d, pkey->name \"%s\", type %d\n", path, parenthkeylist, hkey, pkey->name, pkey->type);
1311
1312 //std::string xpath = db_get_path_locked(pheader, hkey);
1313 //if (xpath != path)
1314 // printf("hkey %d, path \"%s\" vs \"%s\"\n", hkey, path, xpath.c_str());
1315
1316 //db_print_key(pheader, 0, path, parenthkeylist, hkey);
1317
1318 if (hkey==0 || !db_validate_key_offset(pheader, hkey)) {
1319 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid hkey", hkey, path);
1320 return false;
1321 }
1322
1323 /* check key type */
1324 if (pkey->type <= 0 || pkey->type >= TID_LAST) {
1325 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", name \"%s\", invalid key type %d", hkey, path, pkey->name, pkey->type);
1326 return false;
1327 }
1328
1329 /* check key name */
1330 status = db_validate_name(pkey->name, FALSE, "db_validate_key", msg);
1331 if (status != DB_SUCCESS) {
1332 char newname[NAME_LENGTH];
1333 sprintf(newname, "%p", pkey);
1334 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\": invalid name \"%s\" replaced with \"%s\"", hkey, path, pkey->name, newname);
1335 mstrlcpy(pkey->name, newname, sizeof(pkey->name));
1336 flag = false;
1337 //return false;
1338 }
1339
1340 /* check parent */
1341 if (pkey->parent_keylist != parenthkeylist) {
1342 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", name \"%s\", invalid parent_keylist %d should be %d", hkey, path, pkey->name, pkey->parent_keylist, parenthkeylist);
1343 return false;
1344 }
1345
1346 if (!db_validate_data_offset(pheader, pkey->data)) {
1347 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid data offset 0x%08X is invalid", hkey, path, pkey->data - (int)sizeof(DATABASE_HEADER));
1348 return false;
1349 }
1350
1351 /* check key sizes */
1352 if ((pkey->total_size < 0) || (pkey->total_size > pheader->data_size)) {
1353 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->total_size %d", hkey, path, pkey->total_size);
1354 return false;
1355 }
1356
1357 if ((pkey->item_size < 0) || (pkey->item_size > pheader->data_size)) {
1358 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->item_size: %d", hkey, path, pkey->item_size);
1359 return false;
1360 }
1361
1362 if ((pkey->num_values < 0) || (pkey->num_values > pheader->data_size)) {
1363 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->num_values: %d", hkey, path, pkey->num_values);
1364 return false;
1365 }
1366
1367 /* check and correct key size */
1368 if (pkey->total_size != pkey->item_size * pkey->num_values) {
1369 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", corrected pkey->total_size from %d to %d*%d=%d", hkey, path, pkey->total_size, pkey->item_size, pkey->num_values, pkey->item_size * pkey->num_values);
1370 pkey->total_size = pkey->item_size * pkey->num_values;
1371 flag = false;
1372 }
1373
1374 /* check and correct key size */
1375 if (pkey->data == 0 && pkey->total_size != 0) {
1376 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", pkey->data is zero, corrected pkey->num_values %d and pkey->total_size %d to be zero, should be zero", hkey, path, pkey->num_values, pkey->total_size);
1377 pkey->num_values = 0;
1378 pkey->total_size = 0;
1379 flag = false;
1380 }
1381
1382 if (pkey->type == TID_STRING || pkey->type == TID_LINK) {
1383 const char* s = (char*)pheader + pkey->data;
1384 if (!is_utf8(s)) {
1385 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\" is not a valid UTF-8 Unicode string", hkey, path);
1386 //flag = false;
1387 }
1388 }
1389
1390 /* check link */
1391 if (pkey->type == TID_LINK) {
1392 bool good_link = true;
1393
1394 if (good_link) {
1395 /* check for empty link */
1396 // minimum symlink length is 3 bytes:
1397 // one byte "/"
1398 // one byte odb entry name
1399 // one byte "\0"
1400 if (pkey->total_size <= 2) {
1401 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK is an empty link", hkey, path);
1402 flag = false;
1403 good_link = false;
1404 //return false;
1405 }
1406 }
1407
1408 if (good_link) {
1409 /* check for too long link */
1410 if (pkey->total_size >= MAX_ODB_PATH) {
1411 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK length %d exceeds MAX_ODB_PATH %d", hkey, path, pkey->total_size, MAX_ODB_PATH);
1412 flag = false;
1413 good_link = false;
1414 //return false;
1415 }
1416 }
1417
1418 if (good_link) {
1419 /* check for link loop */
1420
1421 const char* link = (char*)pheader + pkey->data;
1422 size_t link_len = strlen(link);
1423
1424 if (link_len <= 2) {
1425 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK is an empty link", hkey, path);
1426 flag = false;
1427 good_link = false;
1428 //return false;
1429 } else {
1430 size_t path_len = strlen(path);
1431 if (link_len == path_len) {
1432 // check for link to itself
1433 if (equal_ustring(link, path)) {
1434 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK to \"%s\" is a link to itself", hkey, path, link);
1435 flag = false;
1436 good_link = false;
1437 //return false;
1438 }
1439 } else if (link_len < path_len) {
1440 // check for link to the "path" subdirectory
1441 char tmp[MAX_ODB_PATH];
1442 memcpy(tmp, path, link_len);
1443 tmp[link_len] = 0;
1444 if (equal_ustring(link, tmp) && path[link_len] == DIR_SEPARATOR) {
1445 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK to \"%s\" is a loop", hkey, path, link);
1446 flag = false;
1447 good_link = false;
1448 //return false;
1449 }
1450 }
1451 }
1452 }
1453 }
1454
1455 /* check access mode */
1457 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->access_mode %d", hkey, path, pkey->access_mode);
1458 flag = false;
1459 //return false;
1460 }
1461
1462 /* check if access time is in the future */
1463 if (pkey->last_written > 0 && ((DWORD)pkey->last_written > ss_time() + 3600)) {
1464 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->last_written time %d", hkey, path, pkey->last_written);
1465 flag = false;
1466 //return false;
1467 }
1468
1469 if (keylist) {
1470 if (pkey->type != TID_KEY) {
1471 keylist_struct kv;
1472 kv.type = pkey->type;
1473 kv.hkey = hkey;
1474 kv.skey = sizeof(*pkey);
1475 kv.hdata = pkey->data;
1476 kv.sdata = pkey->total_size;
1477 keylist->push_back(kv);
1478 } else {
1479 keylist_struct kv1;
1480 kv1.type = pkey->type;
1481 kv1.hkey = hkey;
1482 kv1.skey = sizeof(*pkey);
1483 kv1.hdata = 0;
1484 kv1.sdata = 0;
1485 keylist->push_back(kv1);
1486
1487 keylist_struct kv2;
1488 kv2.type = 0;
1489 kv2.hkey = pkey->data;
1490 kv2.skey = pkey->total_size;
1491 kv2.hdata = 0;
1492 kv2.sdata = 0;
1493 keylist->push_back(kv2);
1494 }
1495 }
1496
1497 if (pkey->type == TID_KEY) {
1498 bool pkeylist_ok = true;
1499 // FIXME: notice the kludged repair of pkeylist! K.O.
1500 const KEYLIST *pkeylist = db_get_pkeylist(pheader, pkey, "db_validate_key", msg, true);
1501
1502 if (!pkeylist) {
1503 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->data %d", hkey, path, pkey->data);
1504 flag = false;
1505 } else {
1506 if (pkeylist->parent != hkey) {
1507 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->parent %d is not hkey %d", hkey, path, pkeylist->parent, hkey);
1508 flag = false;
1509 pkeylist_ok = false;
1510 }
1511
1512 if (pkeylist->num_keys < 0 || pkeylist->num_keys > pheader->key_size) {
1513 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->num_keys %d", hkey, path, pkeylist->num_keys);
1514 flag = false;
1515 pkeylist_ok = false;
1516 }
1517
1518 if (pkeylist->num_keys == 0 && pkeylist->first_key == 0) {
1519 // empty key
1520 } else if (pkeylist->first_key == 0 || !db_validate_key_offset(pheader, pkeylist->first_key)) {
1521 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->first_key %d", hkey, path, pkeylist->first_key);
1522 flag = false;
1523 pkeylist_ok = false;
1524 }
1525
1526 if (pkeylist_ok) {
1527 //printf("hkey %d, path \"%s\", pkey->data %d, pkeylist parent %d, num_keys %d, first_key %d: ", hkey, path, pkey->data, pkeylist->parent, pkeylist->num_keys, pkeylist->first_key);
1528
1529 HNDLE subhkey = pkeylist->first_key;
1530
1531 int count = 0;
1532 while (subhkey != 0) {
1533 KEY* subpkey = (KEY*)db_get_pkey(pheader, subhkey, NULL, "db_validate_key", msg);
1534 if (!subpkey) {
1535 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid subhkey %d", hkey, path, subhkey);
1536 pkeylist_ok = false;
1537 flag = false;
1538 break;
1539 }
1540
1541 std::string buf;
1542 buf += path;
1543 buf += "/";
1544 buf += subpkey->name;
1545
1546 //printf("pkey %p, next %d, name [%s], path %s\n", subpkey, subpkey->next_key, subpkey->name, buf.c_str());
1547
1548 if (recurse) {
1549 flag &= db_validate_and_repair_key_wlocked(pheader, recurse + 1, buf.c_str(), pkey->data, subhkey, subpkey, msg, keylist);
1550 }
1551
1552 count++;
1553 subhkey = subpkey->next_key;
1554 }
1555
1556 if (count != pkeylist->num_keys) {
1557 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", repaired TID_KEY mismatch of pkeylist->num_keys %d against key chain length %d", hkey, path, pkeylist->num_keys, count);
1558 ((KEYLIST*)pkeylist)->num_keys = count;
1559 flag = false;
1560 pkeylist_ok = false;
1561 }
1562 }
1563 }
1564 }
1565
1566 return flag;
1567}
1568
1569/*------------------------------------------------------------------*/
1570
1572{
1573 assert(pdb);
1574 assert(pdb->database_header);
1575
1576 DATABASE_HEADER *pheader = pdb->database_header;
1577
1578 int idx = pdb->client_index;
1579
1580 if (idx < 0 || idx >= pheader->max_client_index || idx >= MAX_CLIENTS) {
1581 cm_msg(MERROR, "db_get_my_client_locked", "My client index %d in ODB is invalid: out of range 0..%d. Maybe this client was removed by a timeout, see midas.log. Cannot continue, aborting...", idx, pheader->max_client_index-1);
1583 abort();
1584 }
1585
1586 DATABASE_CLIENT* pclient = &pheader->client[idx]; // safe to dereference on "idx"
1587
1588 if (pclient->name[0] == 0) {
1589 cm_msg(MERROR, "db_get_my_client_locked", "My client index %d in ODB is invalid: client name is blank. Maybe this client was removed by a timeout, see midas.log. Cannot continue, aborting...", idx);
1591 abort();
1592 }
1593
1594 int pid = getpid();
1595
1596 if (pclient->pid != pid) {
1597 cm_msg(MERROR, "db_get_my_client_locked", "My client index %d in ODB is invalid: pid mismatch, my pid is %d, but ODB has %d. Maybe this client was removed by a timeout, see midas.log. Cannot continue, aborting...", idx, pid, pclient->pid);
1599 abort();
1600 }
1601
1602 //printf("db_get_my_client_locked: idx %d, name [%s], pid %d\n", idx, pclient->name, pid);
1603
1604 //if (strcmp(pclient->name, "mdump") == 0) {
1605 // ss_semaphore_release(pdb->semaphore);
1606 // for (int i=0; i<15; i++) {
1607 // printf("sleep %d\n", i);
1608 // ::sleep(1);
1609 // }
1610 // abort();
1611 //}
1612
1613 return pclient;
1614}
1615
1616/*------------------------------------------------------------------*/
1618{
1619 /* validate size of data structures (miscompiled, 32/64 bit mismatch, etc */
1620
1621 if (0) {
1622#define S(x) printf("assert(sizeof(%-20s) == %6d);\n", #x, (int)sizeof(x))
1623 // basic data types
1624 S(char *);
1625 S(char);
1626 S(int);
1627 S(long int);
1628 S(float);
1629 S(double);
1630 S(BOOL);
1631 S(WORD);
1632 S(DWORD);
1633 S(INT);
1634 S(POINTER_T);
1636 // data buffers
1640 // history files
1641 S(HIST_RECORD);
1642 S(DEF_RECORD);
1643 S(INDEX_RECORD);
1644 S(TAG);
1645 // ODB shared memory structures
1646 S(KEY);
1647 S(KEYLIST);
1648 S(OPEN_RECORD);
1651 // misc structures
1652 S(EVENT_HEADER);
1653 S(RUNINFO);
1656 S(BANK_HEADER);
1657 S(BANK);
1658 S(BANK32);
1660 S(PROGRAM_INFO);
1661 S(ALARM_CLASS);
1662 S(ALARM);
1663 //S(CHN_SETTINGS);
1664 //S(CHN_STATISTICS);
1665#undef S
1666 }
1667
1668#if 0
1670 printf("EQUIPMENT_INFO offset of event_id: %d\n", (int)((char*)&eq.event_id - (char*)&eq));
1671 printf("EQUIPMENT_INFO offset of eq_type: %d\n", (int)((char*)&eq.eq_type - (char*)&eq));
1672 printf("EQUIPMENT_INFO offset of event_limit: %d\n", (int)((char*)&eq.event_limit - (char*)&eq));
1673 printf("EQUIPMENT_INFO offset of num_subevents: %d\n", (int)((char*)&eq.num_subevents - (char*)&eq));
1674 printf("EQUIPMENT_INFO offset of status: %d\n", (int)((char*)&eq.status - (char*)&eq));
1675 printf("EQUIPMENT_INFO offset of hidden: %d\n", (int)((char*)&eq.hidden - (char*)&eq));
1676#endif
1677
1678 assert(sizeof(UINT8) == 1);
1679 assert(sizeof(INT8) == 1);
1680 assert(sizeof(UINT16) == 2);
1681 assert(sizeof(INT16) == 2);
1682 assert(sizeof(UINT32) == 4);
1683 assert(sizeof(INT32) == 4);
1684 assert(sizeof(UINT64) == 8);
1685 assert(sizeof(INT64) == 8);
1686
1687#ifdef OS_LINUX
1688 assert(sizeof(EVENT_REQUEST) == 16); // ODB v3
1689 assert(sizeof(BUFFER_CLIENT) == 256);
1690 assert(sizeof(BUFFER_HEADER) == 16444);
1691 assert(sizeof(HIST_RECORD) == 20);
1692 assert(sizeof(DEF_RECORD) == 40);
1693 assert(sizeof(INDEX_RECORD) == 12);
1694 assert(sizeof(TAG) == 40);
1695 assert(sizeof(KEY) == 68);
1696 assert(sizeof(KEYLIST) == 12);
1697 assert(sizeof(OPEN_RECORD) == 8);
1698 assert(sizeof(DATABASE_CLIENT) == 2112);
1699 assert(sizeof(DATABASE_HEADER) == 135232);
1700 assert(sizeof(EVENT_HEADER) == 16);
1701 //assert(sizeof(EQUIPMENT_INFO) == 696); has been moved to dynamic checking inside mhttpd.c
1702 assert(sizeof(EQUIPMENT_STATS) == 24);
1703 assert(sizeof(BANK_HEADER) == 8);
1704 assert(sizeof(BANK) == 8);
1705 assert(sizeof(BANK32) == 12);
1706 assert(sizeof(BANK32A) == 16);
1707 assert(sizeof(ANA_OUTPUT_INFO) == 792);
1708 assert(sizeof(PROGRAM_INFO) == 316);
1709 assert(sizeof(ALARM) == 460);
1710 assert(sizeof(ALARM_CLASS) == 352);
1711 //assert(sizeof(CHN_SETTINGS) == 648); // ODB v3
1712 //assert(sizeof(CHN_STATISTICS) == 56); // ODB v3
1713#endif
1714}
1715
1725
1726static int db_update_open_record_wlocked(const DATABASE_HEADER* xpheader, const KEY* xpkey, int level, void* voidp, db_err_msg **msg)
1727{
1728 int found = 0;
1729 int count = 0;
1730 int status;
1731 int k;
1733
1734 KEY* pkey = (KEY*)xpkey; // drop "const": we already have "allow_write"
1735
1736 HNDLE hKey = db_pkey_to_hkey(uorp->pheader, pkey);
1737
1738 for (k=0; k<uorp->num_keys; k++)
1739 if (uorp->hkeys[k] == hKey) {
1740 found = 1;
1741 count = uorp->counts[k];
1742 break;
1743 }
1744
1745 if (pkey->notify_count == 0 && !found)
1746 return DB_SUCCESS; // no open record here
1747
1748 std::string path = db_get_path_pkey(uorp->pheader, pkey);
1749 if (path == "") {
1750 db_msg(msg, MINFO, "db_update_open_record", "Invalid hKey %d", hKey);
1751 return DB_SUCCESS;
1752 }
1753
1754 //if (!db_validate_hkey(uorp->pheader, hKey)) {
1755 // cm_msg(MINFO, "db_update_open_record", "Invalid hKey %d", hKey);
1756 // return DB_SUCCESS;
1757 //}
1758 //
1759 //KEY* pkey = (KEY *) ((char *) uorp->pheader + hKey);
1760
1761 //printf("path [%s], type %d, notify_count %d\n", path, pkey->type, pkey->notify_count);
1762
1763 // extra check: are we looking at the same key?
1764 //assert(xkey->notify_count == pkey->notify_count);
1765
1766#if 0
1767 printf("%s, notify_count %d, found %d, our count %d\n", path, pkey->notify_count, found, count);
1768#endif
1769
1770 if (pkey->notify_count==0 && found) {
1771 db_msg(msg, MINFO, "db_update_open_record", "Added missing open record flag to \"%s\"", path.c_str());
1772 pkey->notify_count = count;
1773 uorp->num_modified++;
1774 return DB_SUCCESS;
1775 }
1776
1777 if (pkey->notify_count!=0 && !found) {
1778 db_msg(msg, MINFO, "db_update_open_record", "Removed open record flag from \"%s\"", path.c_str());
1779 pkey->notify_count = 0;
1780 uorp->num_modified++;
1781
1782 if (pkey->access_mode | MODE_EXCLUSIVE) {
1783 status = db_set_mode_wlocked(uorp->pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 1, msg);
1784 if (status != DB_SUCCESS) {
1785 db_msg(msg, MERROR, "db_update_open_record", "Cannot remove exclusive access mode from \"%s\", db_set_mode() status %d", path.c_str(), status);
1786 return DB_SUCCESS;
1787 }
1788 db_msg(msg, MINFO, "db_update_open_record", "Removed exclusive access mode from \"%s\"", path.c_str());
1789 }
1790 return DB_SUCCESS;
1791 }
1792
1793 if (pkey->notify_count != uorp->counts[k]) {
1794 db_msg(msg, MINFO, "db_update_open_record", "Updated notify_count of \"%s\" from %d to %d", path.c_str(), pkey->notify_count, count);
1795 pkey->notify_count = count;
1796 uorp->num_modified++;
1797 return DB_SUCCESS;
1798 }
1799
1800 return DB_SUCCESS;
1801}
1802
1804{
1805 int status = DB_SUCCESS;
1807 int i, j, k;
1808
1810 uor.num_keys = 0;
1811 uor.hkeys = (HNDLE*)calloc(uor.max_keys, sizeof(HNDLE));
1812 uor.counts = (int*)calloc(uor.max_keys, sizeof(int));
1813 uor.modes = (int*)calloc(uor.max_keys, sizeof(int));
1814 uor.num_modified = 0;
1815
1816 assert(uor.hkeys != NULL);
1817 assert(uor.counts != NULL);
1818 assert(uor.modes != NULL);
1819
1820 uor.pheader = pheader;
1821
1822 for (i = 0; i < pheader->max_client_index; i++) {
1823 DATABASE_CLIENT* pclient = &pheader->client[i];
1824 for (j = 0; j < pclient->max_index; j++)
1825 if (pclient->open_record[j].handle) {
1826 int found = 0;
1827 for (k=0; k<uor.num_keys; k++) {
1828 if (uor.hkeys[k] == pclient->open_record[j].handle) {
1829 uor.counts[k]++;
1830 found = 1;
1831 break;
1832 }
1833 }
1834 if (!found) {
1835 uor.hkeys[uor.num_keys] = pclient->open_record[j].handle;
1836 uor.counts[uor.num_keys] = 1;
1837 uor.modes[uor.num_keys] = pclient->open_record[j].access_mode;
1838 uor.num_keys++;
1839 }
1840 }
1841 }
1842
1843#if 0
1844 for (i=0; i<uor.num_keys; i++)
1845 printf("index %d, handle %d, count %d, access mode %d\n", i, uor.hkeys[i], uor.counts[i], uor.modes[i]);
1846#endif
1847
1848 const KEY* proot = db_get_pkey(pheader, 0, &status, "db_validate_open_record", msg);
1849
1850 if (proot) {
1851 db_scan_tree_locked(pheader, proot, 0, db_update_open_record_wlocked, &uor, msg);
1852 }
1853
1854 if (uor.num_modified) {
1855 db_msg(msg, MINFO, "db_validate_open_records", "Corrected %d ODB entries", uor.num_modified);
1856 }
1857
1858 free(uor.hkeys);
1859 free(uor.counts);
1860 free(uor.modes);
1861
1862 return status;
1863}
1864
1865/*------------------------------------------------------------------*/
1866
1868{
1869 bool enable_repair = false;
1870
1871 if (enable_repair)
1872 return true;
1873
1874 if (getenv("MIDAS_ODB_REPAIR_FREE_LIST")) {
1875 db_msg(msg, MINFO, "db_validate_db", "ODB free list repair is enabled by env.variable");
1876 return true;
1877 }
1878
1879 db_msg(msg, MERROR, "db_validate_db", "ODB key free list repair is disabled. To enable repair, \"export MIDAS_ODB_REPAIR_FREE_LIST=1\" and try again");
1880 return false;
1881}
1882
1883/*------------------------------------------------------------------*/
1884static bool db_validate_key_area_wlocked(DATABASE_HEADER * pheader, const keylist_vector& keylist, const keylist_vector& keyfreelist, db_err_msg **msg)
1885{
1886 bool debug = false;
1887 bool flag = true;
1888
1889 if (debug) {
1890 for (size_t i=0; i<keylist.size(); i++) {
1891 size_t gap = 0;
1892 if (i>0)
1893 gap = keylist[i].hkey - (keylist[i-1].hkey + ALIGN8(keylist[i-1].skey));
1894 printf("keylist[%zu] type %2d, hkey %7d, key size %3zu, hdata %9d, data size %9zu, key gap %zu\n", i, keylist[i].type, keylist[i].hkey, keylist[i].skey, keylist[i].hdata, keylist[i].sdata, gap);
1895 }
1896 }
1897
1898 size_t odb_key_start = sizeof(DATABASE_HEADER);
1899 size_t odb_key_end = sizeof(DATABASE_HEADER) + pheader->key_size;
1900
1901 std::vector<keylist_struct> keyfree;
1902
1903 bool first = true;
1904
1905 for (size_t i=1; i<keylist.size(); i++) {
1906
1907 size_t hthis = keylist[i-1].hkey;
1908 size_t sthis = ALIGN8(keylist[i-1].skey);
1909 size_t hend = hthis + sthis;
1910
1911 if (first) {
1912 first = false;
1913 if (hthis == odb_key_start) {
1914 // first entry is at the start of odb data area
1915 // printf("FIRST!\n");
1916 } else {
1917 // insert a free block between start of odb key area and first used block
1918 keylist_struct ds;
1919 ds.hkey = odb_key_start;
1920 ds.skey = hthis - odb_key_start;
1921 keyfree.push_back(ds);
1922 }
1923 }
1924
1925 size_t hnext = keylist[i].hkey;
1926
1927 if (hnext == hend) {
1928 // no free space
1929 continue;
1930 }
1931
1932 if (hnext > hend) {
1933 // a free block
1934 keylist_struct ds;
1935 ds.hkey = hend;
1936 ds.skey = hnext - hend;
1937 keyfree.push_back(ds);
1938 continue;
1939 }
1940
1941 // data areas overlap
1942
1943 size_t overlap = hend - hnext;
1944
1945 std::string this_path = db_get_path_locked(pheader, keylist[i-1].hkey);
1946 std::string next_path = db_get_path_locked(pheader, keylist[i].hkey);
1947
1948 //printf("this %zu, %zu+%zu is %zu next is %zu, overlap %zu\n", i, hthis, sthis, hend, hnext, overlap);
1949
1950 db_msg(msg, MERROR, "db_validate_key_area", "Error: ODB key area is corrupted, \"%s\" (%zu+%zu) overlaps \"%s\" (%zu) by %zu bytes, cannot repair", this_path.c_str(), hthis, sthis, next_path.c_str(), hnext, overlap);
1951 flag = false;
1952 }
1953
1954 if (1) {
1955 size_t ilast = keylist.size()-1;
1956 HNDLE last_used = keylist[ilast].hkey + ALIGN8(keylist[ilast].skey);
1957 keylist_struct ds;
1958 ds.hkey = last_used;
1959 ds.skey = odb_key_end - last_used;
1960 if (ds.skey != 0) {
1961 //printf("ADD LAST!\n");
1962 keyfree.push_back(ds);
1963 }
1964 }
1965
1966 if (debug) {
1967 printf("\n");
1968 for (size_t i=0; i<keyfree.size(); i++) {
1969 printf("keyfree[%zu] hkey %9d, key size %9zu\n", i, keyfree[i].hkey, keyfree[i].skey);
1970 }
1971
1972 printf("\n");
1973 for (size_t i=0; i<keyfreelist.size(); i++) {
1974 printf("keyfreelist[%zu] hkey %9d, key size %9zu\n", i, keyfreelist[i].hkey, keyfreelist[i].skey);
1975 }
1976 }
1977
1978 bool needs_repair = false;
1979
1980 size_t key_check_size = keyfree.size();
1981
1982 if (keyfree.size() != keyfreelist.size()) {
1983 db_msg(msg, MERROR, "db_validate_key_area", "Error: ODB key free list is corrupted, key free list length %zu should be %zu", keyfreelist.size(), keyfree.size());
1984 flag = false;
1985 needs_repair = true;
1986 key_check_size = MIN(keyfree.size(), keyfreelist.size());
1987 }
1988
1989 for (size_t i=0; i<key_check_size; i++) {
1990 if (keyfree[i].hkey != keyfreelist[i].hkey) {
1991 db_msg(msg, MERROR, "db_validate_key_area", "Error: ODB key free list is corrupted, entry %zu mismatch %d should be %d", i, keyfreelist[i].hkey, keyfree[i].hkey);
1992 flag = false;
1993 needs_repair = true;
1994 break;
1995 } else if (keyfree[i].skey != keyfreelist[i].skey) {
1996 db_msg(msg, MERROR, "db_validate_key_area", "Error: ODB key free list is corrupted, entry %zu size mismatch %zu should be %zu", i, keyfreelist[i].skey, keyfree[i].skey);
1997 flag = false;
1998 needs_repair = true;
1999 break;
2000 } else {
2001 // good
2002 }
2003 }
2004
2005 if (needs_repair && check_free_list_repair(msg)) {
2006 db_msg(msg, MINFO, "db_validate_key_area", "Rewriting corrupted ODB key free list");
2007
2008 for (size_t i=0; i<keyfree.size(); i++) {
2009 HNDLE hh = keyfree[i].hkey;
2010 size_t ss = keyfree[i].skey;
2011 HNDLE hnext = 0;
2012 if (i<keyfree.size()-1) {
2013 hnext = keyfree[i+1].hkey;
2014 }
2015 //printf("hh %d, ss %zu, hnext %d\n", hh, ss, hnext);
2016 FREE_DESCRIP* pfree = (FREE_DESCRIP *) ((char *) pheader + hh);
2017 pfree->size = ss;
2018 pfree->next_free = hnext;
2019 }
2020
2021 // remember to update pheader->first_free_key !!!
2022
2023 if (keyfree.size() > 0)
2024 pheader->first_free_key = keyfree[0].hkey;
2025 else
2026 pheader->first_free_key = 0;
2027 }
2028
2029 return flag;
2030}
2031
2032static bool db_validate_data_area_wlocked(DATABASE_HEADER * pheader, const keylist_vector& keylist, const keylist_vector& datafreelist, db_err_msg **msg)
2033{
2034 bool debug = false;
2035 bool flag = true;
2036
2037 if (debug) {
2038 for (size_t i=0; i<keylist.size(); i++) {
2039 size_t gap = 0;
2040 if (i>0)
2041 gap = keylist[i].hdata - (keylist[i-1].hdata + ALIGN8(keylist[i-1].sdata));
2042 printf("keylist[%zu] type %2d, hkey %7d, key size %3zu, hdata %9d, data size %9zu, data gap %zu\n", i, keylist[i].type, keylist[i].hkey, keylist[i].skey, keylist[i].hdata, keylist[i].sdata, gap);
2043 }
2044 }
2045
2046 size_t odb_data_start = sizeof(DATABASE_HEADER) + pheader->key_size;
2047 size_t odb_data_end = sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size;
2048
2049 std::vector<keylist_struct> datafree;
2050
2051 bool first = true;
2052
2053 HNDLE last_used = 0;
2054
2055 for (size_t i=1; i<keylist.size(); i++) {
2056
2057 if (keylist[i-1].hdata == 0)
2058 continue;
2059
2060 if (keylist[i].hdata == 0)
2061 continue;
2062
2063 size_t hthis = keylist[i-1].hdata;
2064 size_t sthis = ALIGN8(keylist[i-1].sdata);
2065 size_t hend = hthis + sthis;
2066
2067 last_used = keylist[i].hdata + ALIGN8(keylist[i].sdata);
2068
2069 if (first) {
2070 first = false;
2071 if (hthis == odb_data_start) {
2072 // first entry is at the start of odb data area
2073 //printf("FIRST!\n");
2074 } else {
2075 // insert a free block between start of odb data area and first used block
2076 keylist_struct ds;
2077 ds.hdata = odb_data_start;
2078 ds.sdata = hthis - odb_data_start;
2079 datafree.push_back(ds);
2080 }
2081 }
2082
2083 size_t hnext = keylist[i].hdata;
2084
2085 if (hnext == hend) {
2086 // no free space
2087 continue;
2088 }
2089
2090 if (hnext > hend) {
2091 // a free block
2092 keylist_struct ds;
2093 ds.hdata = hend;
2094 ds.sdata = hnext - hend;
2095 datafree.push_back(ds);
2096 continue;
2097 }
2098
2099 // data areas overlap
2100
2101 size_t overlap = hend - hnext;
2102
2103 std::string this_path = db_get_path_locked(pheader, keylist[i-1].hkey);
2104 std::string next_path = db_get_path_locked(pheader, keylist[i].hkey);
2105
2106 //printf("this %zu, %zu+%zu is %zu next is %zu, overlap %zu\n", i, hthis, sthis, hend, hnext, overlap);
2107
2108 db_msg(msg, MERROR, "db_validate_data_area", "Error: ODB data area is corrupted, \"%s\" (%zu+%zu) overlaps \"%s\" (%zu) by %zu bytes", this_path.c_str(), hthis, sthis, next_path.c_str(), hnext, overlap);
2109 flag = false;
2110
2111 if (check_free_list_repair(msg)) {
2112 db_msg(msg, MERROR, "db_validate_data_area", "Error: ODB data area is corrupted, clearing overlapping data of \"%s\"", this_path.c_str());
2113 KEY* pkey = (KEY*)db_get_pkey(pheader, keylist[i-1].hkey, NULL, "db_validate_data_area", msg);
2114 if (pkey) {
2115 pkey->num_values = 0;
2116 pkey->data = 0;
2117 pkey->total_size = 0;
2118 pkey->item_size = 0;
2119 pkey->last_written = 0;
2120 }
2121 }
2122 }
2123
2124 if (1) {
2125 //size_t ilast = keylist.size()-1;
2126 //HNDLE last_used = keylist[ilast].hdata + ALIGN8(keylist[ilast].sdata);
2127
2128 if (last_used == 0) { // empty ODB
2129 last_used = odb_data_start;
2130 }
2131
2132 keylist_struct ds;
2133 ds.hdata = last_used;
2134 ds.sdata = odb_data_end - last_used;
2135 if (ds.sdata != 0) {
2136 datafree.push_back(ds);
2137 }
2138 }
2139
2140 if (debug) {
2141 printf("\n");
2142 for (size_t i=0; i<datafree.size(); i++) {
2143 printf("datafree[%zu] hdata %9d, data size %9zu\n", i, datafree[i].hdata, datafree[i].sdata);
2144 }
2145
2146 printf("\n");
2147 for (size_t i=0; i<datafreelist.size(); i++) {
2148 printf("datafreelist[%zu] hdata %9d, data size %9zu\n", i, datafreelist[i].hdata, datafreelist[i].sdata);
2149 }
2150 }
2151
2152 bool needs_repair = false;
2153
2154 size_t data_check_size = datafree.size();
2155
2156 if (datafree.size() != datafreelist.size()) {
2157 db_msg(msg, MERROR, "db_validate_data_area", "Error: ODB data free list is corrupted, data free list length %zu should be %zu", datafreelist.size(), datafree.size());
2158 data_check_size = MIN(datafree.size(), datafreelist.size());
2159 flag = false;
2160 needs_repair = true;
2161 }
2162
2163 for (size_t i=0; i<data_check_size; i++) {
2164 if (datafree[i].hdata != datafreelist[i].hdata) {
2165 db_msg(msg, MERROR, "db_validate_data_area", "Error: ODB data free list is corrupted, entry %zu mismatch %d should be %d", i, datafreelist[i].hdata, datafree[i].hdata);
2166 flag = false;
2167 needs_repair = true;
2168 break;
2169 } else if (datafree[i].sdata != datafreelist[i].sdata) {
2170 db_msg(msg, MERROR, "db_validate_data_area", "Error: ODB data free list is corrupted, entry %zu size mismatch %zu should be %zu", i, datafreelist[i].sdata, datafree[i].sdata);
2171 flag = false;
2172 needs_repair = true;
2173 break;
2174 } else {
2175 // good
2176 }
2177 }
2178
2179 if (needs_repair && check_free_list_repair(msg)) {
2180 db_msg(msg, MINFO, "db_validate_data_area", "Rewriting corrupted ODB data free list");
2181
2182 for (size_t i=0; i<datafree.size(); i++) {
2183 HNDLE hh = datafree[i].hdata;
2184 size_t ss = datafree[i].sdata;
2185 HNDLE hnext = 0;
2186 if (i<datafree.size()-1) {
2187 hnext = datafree[i+1].hdata;
2188 }
2189 //printf("hh %d, ss %zu, hnext %d\n", hh, ss, hnext);
2190 FREE_DESCRIP* pfree = (FREE_DESCRIP *) ((char *) pheader + hh);
2191 pfree->size = ss;
2192 pfree->next_free = hnext;
2193 }
2194
2195 // remember to update pheader->first_free_data !!!
2196
2197 if (datafree.size() > 0)
2198 pheader->first_free_data = datafree[0].hdata;
2199 else
2200 pheader->first_free_data = 0;
2201
2202 //printf("first_free_data %d %d\n", pheader->first_free_data, datafree[0].hdata);
2203 }
2204
2205 return flag;
2206}
2207
2209{
2210 bool flag = true;
2211
2212 /* validate size of data structures (miscompiled, 32/64 bit mismatch, etc */
2213
2215
2216 /* validate the key free list */
2217
2218 std::vector<keylist_struct> keyfreelist;
2219
2220 flag &= db_get_key_free_list_locked(pheader, &keyfreelist, msg);
2221
2222 int total_size_key = 0;
2223
2224 for (const auto e : keyfreelist) {
2225 total_size_key += e.skey;
2226 }
2227
2228 double key_ratio = ((double) (pheader->key_size - total_size_key)) / ((double) pheader->key_size);
2229
2230 if (key_ratio > 0.9)
2231 db_msg(msg, MERROR, "db_validate_db", "Warning: database key area is %.0f%% full", key_ratio * 100.0);
2232
2233 if (total_size_key > pheader->key_size) {
2234 db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, total_size_key %d is bigger than pheader->key_size %d", total_size_key, pheader->key_size);
2235 flag = false;
2236 }
2237
2238 /* validate the data free list */
2239
2240 std::vector<keylist_struct> datafreelist;
2241
2242 flag &= db_get_data_free_list_locked(pheader, &datafreelist, msg);
2243
2244 int total_size_data = 0;
2245
2246 for (const auto e : datafreelist) {
2247 total_size_data += e.sdata;
2248 }
2249
2250 double ratio_data = ((double) (pheader->data_size - total_size_data)) / ((double) pheader->data_size);
2251 if (ratio_data > 0.9)
2252 db_msg(msg, MERROR, "db_validate_db", "Warning: database data area is %.0f%% full", ratio_data * 100.0);
2253
2254 if (total_size_data > pheader->data_size) {
2255 db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, total_size_data %d is bigger than pheader->data_size 0x%08X", total_size_data, pheader->data_size);
2256 flag = false;
2257 }
2258
2259 /* validate the tree of keys, starting from the root key */
2260
2261 if (!db_validate_key_offset(pheader, pheader->root_key)) {
2262 db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, pheader->root_key 0x%08X is invalid", pheader->root_key - (int)sizeof(DATABASE_HEADER));
2263 return false;
2264 }
2265
2266 keylist_vector keylist;
2267
2268 flag &= db_validate_and_repair_key_wlocked(pheader, 1, "", 0, pheader->root_key, (KEY *) ((char *) pheader + pheader->root_key), msg, &keylist);
2269
2270 if (!keylist.empty()) { // check key free list
2271
2272 for (size_t i=0; i<keylist.size(); i++) {
2273 keylist[i].hsort = keylist[i].hkey; // sort by hkey
2274 }
2275
2276 std::sort(keylist.begin(), keylist.end());
2277
2278 flag &= db_validate_key_area_wlocked(pheader, keylist, keyfreelist, msg);
2279 }
2280
2281 if (!keylist.empty()) { // check data free list
2282
2283 for (size_t i=0; i<keylist.size(); i++) {
2284 keylist[i].hsort = keylist[i].hdata; // sort by hdata
2285 }
2286
2287 std::sort(keylist.begin(), keylist.end());
2288
2289 flag &= db_validate_data_area_wlocked(pheader, keylist, datafreelist, msg);
2290 }
2291
2292 if (!flag) {
2293 db_msg(msg, MERROR, "db_validate_db", "Error: ODB corruption detected, see previous messages");
2294 }
2295
2296 return flag;
2297}
2298
2299#endif // LOCAL_ROUTINES
2300
2302#endif /* DOXYGEN_SHOULD_SKIP_THIS */
2303
2304/********************************************************************/
2315INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE * hDB, const char *client_name)
2316{
2317 if (rpc_is_remote())
2318 return rpc_call(RPC_DB_OPEN_DATABASE, xdatabase_name, database_size, hDB, client_name);
2319
2320#ifdef LOCAL_ROUTINES
2321 {
2322 INT i, status;
2323 HNDLE handle;
2324 DATABASE_CLIENT *pclient;
2325 BOOL shm_created;
2326 DATABASE_HEADER *pheader;
2327 KEY *pkey;
2328 KEYLIST *pkeylist;
2329 FREE_DESCRIP *pfree;
2330 BOOL call_watchdog;
2331 DWORD timeout;
2332 char database_name[NAME_LENGTH];
2333
2334 /* restrict name length */
2335 mstrlcpy(database_name, xdatabase_name, NAME_LENGTH);
2336
2337 int odb_size_limit = 100*1000*1000;
2338 if (database_size < 0 || database_size > odb_size_limit) {
2339 cm_msg(MERROR, "db_open_database", "invalid database size: %d bytes. ODB size limit is %d bytes", database_size, odb_size_limit);
2340 return DB_INVALID_PARAM;
2341 }
2342
2343 if (strlen(client_name) >= NAME_LENGTH) {
2344 cm_msg(MERROR, "db_open_database", "client name \'%s\' is longer than %d characters", client_name, NAME_LENGTH-1);
2345 return DB_INVALID_PARAM;
2346 }
2347
2348 if (strchr(client_name, '/') != NULL) {
2349 cm_msg(MERROR, "db_open_database", "client name \'%s\' should not contain the slash \'/\' character", client_name);
2350 return DB_INVALID_PARAM;
2351 }
2352
2353 /* allocate new space for the new database descriptor */
2354 if (_database_entries == 0) {
2355 _database = (DATABASE *) malloc(sizeof(DATABASE));
2356 assert(_database != NULL);
2357 memset(_database, 0, sizeof(DATABASE));
2358 if (_database == NULL) {
2359 *hDB = 0;
2360 return DB_NO_MEMORY;
2361 }
2362
2364 i = 0;
2365 } else {
2366 /* check if database already open */
2367 for (i = 0; i < _database_entries; i++)
2368 if (_database[i].attached && equal_ustring(_database[i].name, database_name)) {
2369 /* check if database belongs to this thread */
2370 *hDB = i + 1;
2371 return DB_SUCCESS;
2372 }
2373
2374 /* check for a deleted entry */
2375 for (i = 0; i < _database_entries; i++)
2376 if (!_database[i].attached)
2377 break;
2378
2379 /* if not found, create new one */
2380 if (i == _database_entries) {
2381 _database = (DATABASE *) realloc(_database, sizeof(DATABASE) * (_database_entries + 1));
2382 assert(_database != NULL);
2383
2384 memset(&_database[_database_entries], 0, sizeof(DATABASE));
2385
2387 if (_database == NULL) {
2389 *hDB = 0;
2390 return DB_NO_MEMORY;
2391 }
2392 }
2393 }
2394
2395 handle = (HNDLE) i;
2396
2397 _database[handle].hDB = handle + 1;
2398
2399 /* open shared memory region */
2400 void* shm_adr = NULL;
2401 size_t shm_size = 0;
2402 HNDLE shm_handle;
2403
2404 status = ss_shm_open(database_name, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2), &shm_adr, &shm_size, &shm_handle, TRUE);
2405
2406 if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
2407 *hDB = 0;
2408 return DB_INVALID_NAME;
2409 }
2410
2411 _database[handle].shm_adr = shm_adr;
2412 _database[handle].shm_size = shm_size;
2413 _database[handle].shm_handle = shm_handle;
2414
2415 _database[handle].database_header = (DATABASE_HEADER *) shm_adr;
2416
2417 /* shortcut to header */
2418 pheader = _database[handle].database_header;
2419
2420 /* save name */
2421 strcpy(_database[handle].name, database_name);
2422
2423 shm_created = (status == SS_CREATED);
2424
2425 /* clear memeory for debugging */
2426 /* memset(pheader, 0, sizeof(DATABASE_HEADER) + 2*ALIGN8(database_size/2)); */
2427
2428 if (shm_created && pheader->name[0] == 0) {
2429 /* setup header info if database was created */
2430 memset(pheader, 0, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2));
2431
2432 strcpy(pheader->name, database_name);
2433 pheader->version = DATABASE_VERSION;
2434 pheader->key_size = ALIGN8(database_size / 2);
2435 pheader->data_size = ALIGN8(database_size / 2);
2436 pheader->root_key = sizeof(DATABASE_HEADER);
2437 pheader->first_free_key = sizeof(DATABASE_HEADER);
2438 pheader->first_free_data = sizeof(DATABASE_HEADER) + pheader->key_size;
2439
2440 /* set up free list */
2441 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
2442 pfree->size = pheader->key_size;
2443 pfree->next_free = 0;
2444
2445 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
2446 pfree->size = pheader->data_size;
2447 pfree->next_free = 0;
2448
2449 /* create root key */
2450 pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_open_database_A");
2451 assert(pkey);
2452
2453 /* set key properties */
2454 pkey->type = TID_KEY;
2455 pkey->num_values = 1;
2457 strcpy(pkey->name, "root");
2458 pkey->parent_keylist = 0;
2459
2460 /* create keylist */
2461 pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST), "db_open_database_B");
2462 assert(pkeylist);
2463
2464 /* store keylist in data field */
2465 pkey->data = (POINTER_T) pkeylist - (POINTER_T) pheader;
2466 pkey->item_size = sizeof(KEYLIST);
2467 pkey->total_size = sizeof(KEYLIST);
2468
2469 pkeylist->parent = (POINTER_T) pkey - (POINTER_T) pheader;
2470 pkeylist->num_keys = 0;
2471 pkeylist->first_key = 0;
2472 }
2473
2474 /* check database version */
2475 if (pheader->version != DATABASE_VERSION) {
2476 cm_msg(MERROR, "db_open_database",
2477 "Different database format: Shared memory is %d, program is %d", pheader->version, DATABASE_VERSION);
2478 return DB_VERSION_MISMATCH;
2479 }
2480
2481 /* check database size vs shared memory size */
2482 if (_database[handle].shm_size < (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size) {
2483 cm_msg(MERROR, "db_open_database", "Invalid database, shared memory size %d is smaller than database size %d (header: %d, key area: %d, data area: %d). Delete this shared memory (odbedit -R), create a new odb (odbinit) and reload it from the last odb save file.", _database[handle].shm_size, (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size, (int)sizeof(DATABASE_HEADER), pheader->key_size, pheader->data_size);
2484 return DB_VERSION_MISMATCH;
2485 }
2486
2487 /* check root key */
2488 if (!db_validate_key_offset(pheader, pheader->root_key)) {
2489 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key offset %d is invalid", pheader->root_key);
2490 return DB_VERSION_MISMATCH;
2491 } else {
2492 pkey = (KEY*)((char*)pheader + pheader->root_key);
2493
2494 if (pkey->type != TID_KEY) {
2495 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key type %d is not TID_KEY", pkey->type);
2496 return DB_VERSION_MISMATCH;
2497 }
2498
2499 if (strcmp(pkey->name, "root") != 0) {
2500 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key name \"%s\" is not \"root\"", pkey->name);
2501 return DB_VERSION_MISMATCH;
2502 }
2503
2504 // what if we are connecting to an incompatible ODB?
2505 // A call to db_validate_and_repair_key() maybe will
2506 // corrupt it here. But we have no choice,
2507 // if we skip it here and continue,
2508 // db_validate_and_repair_db() will call it later anyway... K.O.
2509
2510 db_err_msg* msg = NULL;
2511 bool ok = db_validate_and_repair_key_wlocked(pheader, 0, "", 0, pheader->root_key, pkey, &msg, NULL);
2512 if (msg)
2513 db_flush_msg(&msg);
2514 if (!ok) {
2515 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key is invalid");
2516 return DB_VERSION_MISMATCH;
2517 }
2518 }
2519
2520 /* set default mutex and semaphore timeout */
2521 _database[handle].timeout = 10000;
2522
2523 /* create mutexes for the database */
2524 status = ss_mutex_create(&_database[handle].mutex, TRUE);
2525 if (status != SS_SUCCESS && status != SS_CREATED) {
2526 *hDB = 0;
2527 return DB_NO_SEMAPHORE;
2528 }
2529
2530 /* create semaphore for the database */
2531 status = ss_semaphore_create(database_name, &(_database[handle].semaphore));
2532 if (status != SS_SUCCESS && status != SS_CREATED) {
2533 *hDB = 0;
2534 return DB_NO_SEMAPHORE;
2535 }
2536 _database[handle].lock_cnt = 0;
2537
2538 _database[handle].protect = FALSE;
2539 _database[handle].protect_read = FALSE;
2540 _database[handle].protect_write = FALSE;
2541
2542 /* first lock database */
2543 status = db_lock_database(handle + 1);
2544 if (status != DB_SUCCESS)
2545 return status;
2546
2547 /* we have the database locked, without write protection */
2548
2549 /*
2550 Now we have a DATABASE_HEADER, so let's setup a CLIENT
2551 structure in that database. The information there can also
2552 be seen by other processes.
2553 */
2554
2555 /*
2556 update the client count
2557 */
2558 int num_clients = 0;
2559 int max_client_index = 0;
2560 for (i = 0; i < MAX_CLIENTS; i++) {
2561 if (pheader->client[i].pid == 0)
2562 continue;
2563 num_clients++;
2564 max_client_index = i + 1;
2565 }
2566 pheader->num_clients = num_clients;
2567 pheader->max_client_index = max_client_index;
2568
2569 /*fprintf(stderr,"num_clients: %d, max_client: %d\n",pheader->num_clients,pheader->max_client_index); */
2570
2571 /* remove dead clients */
2572 for (i = 0; i < MAX_CLIENTS; i++) {
2573 if (pheader->client[i].pid == 0)
2574 continue;
2575 if (!ss_pid_exists(pheader->client[i].pid)) {
2576 char client_name_tmp[NAME_LENGTH];
2577 int client_pid;
2578
2579 mstrlcpy(client_name_tmp, pheader->client[i].name, sizeof(client_name_tmp));
2580 client_pid = pheader->client[i].pid;
2581
2582 // removed: /* decrement notify_count for open records and clear exclusive mode */
2583 // open records are corrected later, by db_validate_open_records()
2584
2585 /* clear entry from client structure in database header */
2586 memset(&(pheader->client[i]), 0, sizeof(DATABASE_CLIENT));
2587
2588 cm_msg(MERROR, "db_open_database", "Removed ODB client \'%s\', index %d because process pid %d does not exists", client_name_tmp, i, client_pid);
2589 }
2590 }
2591
2592 /*
2593 Look for empty client slot
2594 */
2595 for (i = 0; i < MAX_CLIENTS; i++)
2596 if (pheader->client[i].pid == 0)
2597 break;
2598
2599 if (i == MAX_CLIENTS) {
2600 db_unlock_database(handle + 1);
2601 *hDB = 0;
2602 cm_msg(MERROR, "db_open_database", "maximum number of clients exceeded");
2603 return DB_NO_SLOT;
2604 }
2605
2606 /* store slot index in _database structure */
2607 _database[handle].client_index = i;
2608
2609 /*
2610 Save the index of the last client of that database so that later only
2611 the clients 0..max_client_index-1 have to be searched through.
2612 */
2613 pheader->num_clients++;
2614 if (i + 1 > pheader->max_client_index)
2615 pheader->max_client_index = i + 1;
2616
2617 /* setup database header and client structure */
2618 pclient = &pheader->client[i];
2619
2620 memset(pclient, 0, sizeof(DATABASE_CLIENT));
2621 mstrlcpy(pclient->name, client_name, sizeof(pclient->name));
2622 pclient->pid = ss_getpid();
2623 pclient->num_open_records = 0;
2624
2625 ss_suspend_get_odb_port(&pclient->port);
2626
2627 pclient->last_activity = ss_millitime();
2628
2629 cm_get_watchdog_params(&call_watchdog, &timeout);
2630 pclient->watchdog_timeout = timeout;
2631
2632 /* check ODB for corruption */
2633 db_err_msg* msg = NULL;
2634 bool ok = db_validate_and_repair_db_wlocked(pheader, &msg);
2635 if (msg)
2636 db_flush_msg(&msg);
2637 if (!ok) {
2638 /* do not treat corrupted odb as a fatal error- allow the user
2639 to preceed at own risk- the database is already corrupted,
2640 so no further harm can possibly be made. */
2641 /*
2642 db_unlock_database(handle + 1);
2643 *hDB = 0;
2644 return DB_CORRUPTED;
2645 */
2646 }
2647
2648 /* setup _database entry */
2649 _database[handle].database_data = _database[handle].database_header + 1;
2650 _database[handle].attached = TRUE;
2651 _database[handle].protect = FALSE;
2652 _database[handle].protect_read = FALSE;
2653 _database[handle].protect_write = FALSE;
2654
2655 *hDB = (handle + 1);
2656
2658 if (status != DB_SUCCESS) {
2659 db_unlock_database(handle + 1);
2660 if (msg)
2661 db_flush_msg(&msg);
2662 cm_msg(MERROR, "db_open_database", "Error: db_validate_open_records() status %d", status);
2663 return status;
2664 }
2665
2666 db_unlock_database(handle + 1);
2667
2668 if (msg)
2669 db_flush_msg(&msg);
2670
2671 if (shm_created)
2672 return DB_CREATED;
2673 }
2674#endif /* LOCAL_ROUTINES */
2675
2676 return DB_SUCCESS;
2677}
2678
2679/********************************************************************/
2686{
2687 if (rpc_is_remote())
2689
2690#ifdef LOCAL_ROUTINES
2691 else {
2692 INT destroy_flag, i, j;
2693 char xname[256];
2694
2695 if (hDB > _database_entries || hDB <= 0) {
2696 cm_msg(MERROR, "db_close_database", "invalid database handle %d", hDB);
2697 return DB_INVALID_HANDLE;
2698 }
2699
2700 /* flush database to disk */
2702
2703 /* first lock database */
2705
2706 DATABASE* pdb = &_database[hDB - 1];
2707
2708 if (!pdb->attached) {
2710 cm_msg(MERROR, "db_close_database", "database not attached");
2711 return DB_INVALID_HANDLE;
2712 }
2713
2714 DATABASE_HEADER *pheader = pdb->database_header;
2716
2717 db_allow_write_locked(pdb, "db_close_database");
2718
2719 /* close all open records */
2720 for (i = 0; i < pclient->max_index; i++)
2721 if (pclient->open_record[i].handle)
2722 db_remove_open_record_wlocked(pdb, pheader, pclient->open_record[i].handle);
2723
2724 /* mark entry in _database as empty */
2725 pdb->attached = FALSE;
2726
2727 /* clear entry from client structure in database header */
2728 memset(pclient, 0, sizeof(DATABASE_CLIENT));
2729
2730 /* calculate new max_client_index entry */
2731 for (i = MAX_CLIENTS - 1; i >= 0; i--)
2732 if (pheader->client[i].pid != 0)
2733 break;
2734 pheader->max_client_index = i + 1;
2735
2736 /* count new number of clients */
2737 for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
2738 if (pheader->client[i].pid != 0)
2739 j++;
2740 pheader->num_clients = j;
2741
2742 destroy_flag = (pheader->num_clients == 0);
2743
2744 /* flush shared memory to disk before it gets deleted */
2745 if (destroy_flag)
2746 ss_shm_flush(pheader->name, pdb->shm_adr, pdb->shm_size, pdb->shm_handle, true);
2747
2748 mstrlcpy(xname, pheader->name, sizeof(xname));
2749
2750 /* unmap shared memory, delete it if we are the last */
2751 ss_shm_close(xname, pdb->shm_adr, pdb->shm_size, pdb->shm_handle, destroy_flag);
2752
2753 pheader = NULL; // after ss_shm_close(), pheader points nowhere
2754 pdb->database_header = NULL; // ditto
2755
2756 /* unlock database */
2758
2759 /* delete semaphore */
2760 ss_semaphore_delete(pdb->semaphore, destroy_flag);
2761
2762 /* delete mutex */
2763 ss_mutex_delete(pdb->mutex);
2764
2765 /* update _database_entries */
2766 if (hDB == _database_entries)
2768
2769 if (_database_entries > 0)
2770 _database = (DATABASE *) realloc(_database, sizeof(DATABASE) * (_database_entries));
2771 else {
2772 free(_database);
2773 _database = NULL;
2774 }
2775
2776 /* if we are the last one, also delete other semaphores */
2777 if (destroy_flag) {
2778 //extern INT _semaphore_elog, _semaphore_alarm, _semaphore_history, _semaphore_msg;
2780
2781 if (_semaphore_elog)
2783 if (_semaphore_alarm)
2787 //if (_semaphore_msg)
2788 // ss_semaphore_delete(_semaphore_msg, TRUE);
2789 }
2790
2791 }
2792#endif /* LOCAL_ROUTINES */
2793
2794 return DB_SUCCESS;
2795}
2796
2798#ifndef DOXYGEN_SHOULD_SKIP_THIS
2799
2800/*------------------------------------------------------------------*/
2802/********************************************************************\
2803
2804 Routine: db_flush_database
2805
2806 Purpose: Flushes the shared memory of a database to its disk file.
2807
2808 Input:
2809 HNDLE hDB Handle to the database, which is used as
2810 an index to the _database array.
2811
2812 Output:
2813 none
2814
2815 Function value:
2816 DB_SUCCESS Successful completion
2817 DB_INVALID_HANDLE Database handle is invalid
2818 RPC_NET_ERROR Network error
2819
2820\********************************************************************/
2821{
2822 if (rpc_is_remote())
2824
2825#ifdef LOCAL_ROUTINES
2826 else {
2827 int status, size;
2828 uint32_t flush_period = 60;
2829 uint32_t last_flush = 0;
2830
2831 if (hDB > _database_entries || hDB <= 0) {
2832 cm_msg(MERROR, "db_flush_database", "invalid database handle");
2833 return DB_INVALID_HANDLE;
2834 }
2835
2836 /* create keys if not present */
2837 size = sizeof(flush_period);
2838 db_get_value(hDB, 0, "/System/Flush/Flush period", &flush_period, &size, TID_UINT32, true);
2839 size = sizeof(last_flush);
2840 db_get_value(hDB, 0, "/System/Flush/Last flush", &last_flush, &size, TID_UINT32, true);
2841
2842 HNDLE hkey;
2843 status = db_find_key(hDB, 0, "/System/Flush/Last flush", &hkey);
2844 if (status != DB_SUCCESS) {
2845 cm_msg(MERROR, "db_flush_database", "Cannot obtain key /System/Flush/Last flush");
2846 return DB_INVALID_HANDLE;
2847 }
2848
2850 DATABASE *pdb = &_database[hDB - 1];
2851 DATABASE_HEADER *pheader = pdb->database_header;
2852
2853 if (!_database[hDB - 1].attached) {
2855 cm_msg(MERROR, "db_flush_database", "invalid database handle");
2856 return DB_INVALID_HANDLE;
2857 }
2858
2859 db_err_msg *msg = nullptr;
2860
2861 const KEY *pkey = db_get_pkey(pheader, hkey, &status, "db_flush_database", &msg);
2862
2863 if (!pkey) {
2865 if (msg)
2866 db_flush_msg(&msg);
2867 return CM_NO_CLIENT;
2868 }
2869
2870 size = sizeof(last_flush);
2871 status = db_get_data_locked(pheader, pkey, 0, &last_flush, &size, TID_UINT32, &msg);
2872
2873 /* only flush if period has expired */
2874 if (ss_time() > last_flush + flush_period) {
2875 db_allow_write_locked(pdb, "db_flush_database");
2876
2877 /* update last flush time in ODB */
2878 last_flush = ss_time();
2879 db_set_value_wlocked(pdb, 0, "/System/Flush/Last flush", &last_flush, sizeof(last_flush), 1, TID_UINT32, &msg);
2880
2881 /* flush shared memory to disk */
2882 ss_shm_flush(pheader->name, _database[hDB - 1].shm_adr, _database[hDB - 1].shm_size, _database[hDB - 1].shm_handle, false);
2883 }
2884
2886 }
2887#endif /* LOCAL_ROUTINES */
2888
2889 return DB_SUCCESS;
2890}
2891
2892/*------------------------------------------------------------------*/
2894/********************************************************************\
2895
2896 Routine: db_close_all_databases
2897
2898 Purpose: Close all open databases and open records
2899
2900 Input:
2901 none
2902
2903 Output:
2904 none
2905
2906 Function value:
2907 DB_SUCCESS Successful completion
2908
2909\********************************************************************/
2910{
2911 INT status;
2912
2913 if (rpc_is_remote()) {
2915 if (status != DB_SUCCESS)
2916 return status;
2917 }
2918
2921
2922#ifdef LOCAL_ROUTINES
2923 {
2924 INT i;
2925
2926 for (i = _database_entries; i > 0; i--)
2928 }
2929#endif /* LOCAL_ROUTINES */
2930
2931 return DB_SUCCESS;
2932}
2933
2934/*------------------------------------------------------------------*/
2935INT db_set_client_name(HNDLE hDB, const char *client_name)
2936/********************************************************************\
2937
2938 Routine: db_set_client_name
2939
2940 Purpose: Set client name for a database. Used by cm_connect_experiment
2941 if a client name is duplicate and changed.
2942
2943 Input:
2944 INT hDB Handle to database
2945 char *client_name Name of this application
2946
2947 Output:
2948
2949 Function value:
2950 DB_SUCCESS Successful completion
2951 RPC_NET_ERROR Network error
2952
2953\********************************************************************/
2954{
2955 if (rpc_is_remote())
2956 return rpc_call(RPC_DB_SET_CLIENT_NAME, hDB, client_name);
2957
2958#ifdef LOCAL_ROUTINES
2959 {
2960 if (hDB > _database_entries || hDB <= 0) {
2961 cm_msg(MERROR, "db_set_client_name", "invalid database handle %d", hDB);
2962 return DB_INVALID_HANDLE;
2963 }
2964
2966
2967 DATABASE *pdb = &_database[hDB - 1];
2969 mstrlcpy(pclient->name, client_name, sizeof(pclient->name));
2970
2972 }
2973#endif /* LOCAL_ROUTINES */
2974
2975 return DB_SUCCESS;
2976}
2977
2979#endif /* DOXYGEN_SHOULD_SKIP_THIS */
2980
2981/********************************************************************/
2989{
2990#ifdef LOCAL_ROUTINES
2991 int status = 0;
2992 DATABASE* pdb = db_lock_database(hDB, &status, "db_lock_database", false);
2993 if (!pdb)
2994 return status;
2995 return DB_SUCCESS;
2996#else
2997 return DB_SUCCESS;
2998#endif
2999}
3000
3001#ifdef LOCAL_ROUTINES
3002
3003DATABASE* db_lock_database(HNDLE hDB, int *pstatus, const char* caller, bool check_attached)
3004{
3005 if (hDB > _database_entries || hDB <= 0) {
3006 cm_msg(MERROR, caller, "invalid database handle %d, aborting...", hDB);
3008 abort();
3009 if (pstatus)
3010 *pstatus = DB_INVALID_HANDLE;
3011 return NULL;
3012 }
3013
3014 DATABASE* pdb = &_database[hDB-1];
3015
3016 if (check_attached && !pdb->attached) {
3017 cm_msg(MERROR, caller, "invalid database handle %d, aborting...", hDB);
3019 abort();
3020 if (pstatus)
3021 *pstatus = DB_INVALID_HANDLE;
3022 return NULL;
3023 }
3024
3025 if (hDB != pdb->hDB) {
3026 cm_msg(MERROR, "db_lock_database", "database handle mismatch %d vs %d, aborting...", hDB, pdb->hDB);
3028 abort();
3029 if (pstatus)
3030 *pstatus = DB_INVALID_HANDLE;
3031 return NULL;
3032 }
3033
3034 /* obtain access mutex in multi-thread applications */
3035 int status = ss_mutex_wait_for(pdb->mutex, pdb->timeout);
3036 if (status != SS_SUCCESS) {
3037 cm_msg(MERROR, "db_lock_database", "cannot lock ODB mutex, timeout %d ms, status %d, aborting...", pdb->timeout, status);
3039 abort();
3040 }
3041
3042 /* protect this function against recursive call from signal handlers */
3043 if (pdb->inside_lock_unlock) {
3044 fprintf(stderr, "db_lock_database: Detected recursive call to db_{lock,unlock}_database() while already inside db_{lock,unlock}_database(). Maybe this is a call from a signal handler. Cannot continue, aborting...\n");
3045 abort();
3046 }
3047
3048 pdb->inside_lock_unlock = 1;
3049
3050 //static int x = 0;
3051 //x++;
3052 //if (x > 5000) {
3053 // printf("inside db_lock_database(), press Ctrl-C now!\n");
3054 // sleep(5);
3055 //}
3056
3057 // test recursive locking
3058 // static int out=0;
3059 // out++;
3060 // printf("HERE %d!\n", out);
3061 // if (out>10) abort();
3062 // db_lock_database(hDB);
3063 // printf("OUT %d!\n", out);
3064
3065 if (pdb->lock_cnt == 0) {
3066 pdb->lock_cnt = 1;
3067 /* wait max. 5 minutes for semaphore (required if locking process is being debugged) */
3069 if (status == SS_TIMEOUT) {
3070 cm_msg(MERROR, "db_lock_database", "cannot lock ODB semaphore, timeout %d ms, aborting...", pdb->timeout);
3072 //exit(1); // exit() calls ataxit() handlers, including midas disconnect experiment which will crash because ODB locking is hosed. cannot continue, must abort(). K.O. 13-OCT-2024.
3073 abort();
3074 }
3075 if (status != SS_SUCCESS) {
3076 cm_msg(MERROR, "db_lock_database", "cannot lock ODB semaphore, timeout %d ms, ss_semaphore_wait_for() status %d, aborting...", pdb->timeout, status);
3078 abort();
3079 }
3080 } else {
3081 pdb->lock_cnt++; // we have already the lock (recursive call), so just increase counter
3082 }
3083
3084#ifdef CHECK_LOCK_COUNT
3085 {
3086 std::string str = msprintf(str, "db_lock_database, lock_cnt=%d", pdb->lock_cnt);
3087 ss_stack_history_entry(str.c_str());
3088 }
3089#endif
3090
3091 if (pdb->protect) {
3092 if (pdb->database_header == NULL) {
3093 int status;
3094 assert(!pdb->protect_read);
3095 assert(!pdb->protect_write);
3096 status = ss_shm_unprotect(pdb->shm_handle, &pdb->shm_adr, pdb->shm_size, TRUE, FALSE, "db_lock_database");
3097 if (status != SS_SUCCESS) {
3098 cm_msg(MERROR, "db_lock_database", "cannot lock ODB, ss_shm_unprotect(TRUE,FALSE) failed with status %d, aborting...", status);
3101 abort();
3102 }
3103 pdb->database_header = (DATABASE_HEADER *) pdb->shm_adr;
3104 pdb->protect_read = TRUE;
3105 pdb->protect_write = FALSE;
3106 }
3107 }
3108
3109 pdb->inside_lock_unlock = 0;
3110
3111 return pdb;
3112}
3113
3114INT db_allow_write_locked(DATABASE* p, const char* caller_name)
3115{
3116 assert(p);
3117 if (p->protect && !p->protect_write) {
3118 int status;
3119 assert(p->lock_cnt > 0);
3120 assert(p->database_header != NULL);
3121 assert(p->protect_read);
3122 status = ss_shm_unprotect(p->shm_handle, &p->shm_adr, p->shm_size, TRUE, TRUE, caller_name);
3123 if (status != SS_SUCCESS) {
3124 cm_msg(MERROR, "db_allow_write_locked", "cannot write to ODB, ss_shm_unprotect(TRUE,TRUE) failed with status %d, aborting...", status);
3127 abort();
3128 }
3130 p->protect_read = TRUE;
3131 p->protect_write = TRUE;
3132 }
3133 return DB_SUCCESS;
3134}
3135#endif /* LOCAL_ROUTINES */
3136
3137/********************************************************************/
3144{
3145#ifdef LOCAL_ROUTINES
3146
3147 if (hDB > _database_entries || hDB <= 0) {
3148 cm_msg(MERROR, "db_unlock_database", "invalid database handle %d", hDB);
3149 return DB_INVALID_HANDLE;
3150 }
3151
3152 DATABASE* pdb = &_database[hDB - 1];
3153
3154 db_unlock_database(pdb, "db_unlock_database");
3155
3156 return DB_SUCCESS;
3157
3158#else
3159 return DB_SUCCESS;
3160#endif
3161}
3162
3163#ifdef LOCAL_ROUTINES
3164
3165void db_unlock_database(DATABASE* pdb, const char* caller)
3166{
3167#ifdef CHECK_LOCK_COUNT
3168 {
3169 std::string str = msprintf("db_unlock_database, lock_cnt=%d", _database[hDB - 1].lock_cnt);
3170 ss_stack_history_entry(str.c_str());
3171 }
3172#endif
3173
3174 /* protect this function against recursive call from signal handlers */
3175 if (pdb->inside_lock_unlock) {
3176 fprintf(stderr, "db_unlock_database: Detected recursive call to db_{lock,unlock}_database() while already inside db_{lock,unlock}_database(). Maybe this is a call from a signal handler. Cannot continue, aborting...\n");
3177 abort();
3178 }
3179
3180 pdb->inside_lock_unlock = 1;
3181
3182 //static int x = 0;
3183 //x++;
3184 //if (x > 5000) {
3185 // printf("inside db_unlock_database(), press Ctrl-C now!\n");
3186 // sleep(5);
3187 //}
3188
3189 if (pdb->lock_cnt == 1) {
3191
3192 if (pdb->protect && pdb->database_header) {
3193 int status;
3194 assert(pdb->protect_read);
3195 assert(pdb->database_header);
3196 pdb->database_header = NULL;
3197 status = ss_shm_protect(pdb->shm_handle, pdb->shm_adr, pdb->shm_size);
3198 if (status != SS_SUCCESS) {
3199 cm_msg(MERROR, "db_unlock_database", "cannot unlock ODB, ss_shm_protect() failed with status %d, aborting...", status);
3201 abort();
3202 }
3203 pdb->protect_read = FALSE;
3204 pdb->protect_write = FALSE;
3205 }
3206 }
3207
3208 assert(pdb->lock_cnt > 0);
3209 pdb->lock_cnt--;
3210
3211 pdb->inside_lock_unlock = 0;
3212
3213 /* release mutex for multi-thread applications */
3214 ss_mutex_release(pdb->mutex);
3215}
3216
3217#endif /* LOCAL_ROUTINES */
3218
3219/********************************************************************/
3220#if 0
3221INT db_get_lock_cnt(HNDLE hDB)
3222{
3223#ifdef LOCAL_ROUTINES
3224
3225 /* return zero if no ODB is open or we run remotely */
3226 if (_database_entries == 0)
3227 return 0;
3228
3229 if (hDB > _database_entries || hDB <= 0) {
3230 cm_msg(MERROR, "db_get_lock_cnt", "invalid database handle %d, aborting...", hDB);
3231 fprintf(stderr, "db_get_lock_cnt: invalid database handle %d, aborting...\n", hDB);
3232 abort();
3233 return DB_INVALID_HANDLE;
3234 }
3235
3236 return _database[hDB - 1].lock_cnt;
3237#else
3238 return 0;
3239#endif
3240}
3241#endif
3242
3243INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
3244{
3245#ifdef LOCAL_ROUTINES
3246
3247 /* return zero if no ODB is open or we run remotely */
3248 if (_database_entries == 0)
3249 return 0;
3250
3251 if (hDB > _database_entries || hDB <= 0) {
3252 cm_msg(MERROR, "db_set_lock_timeout", "invalid database handle %d", hDB);
3253 return 0;
3254 }
3255
3256 if (timeout_millisec > 0) {
3257 _database[hDB - 1].timeout = timeout_millisec;
3258 }
3259
3260 return _database[hDB - 1].timeout;
3261#else
3262 return 0;
3263#endif
3264}
3265
3266#ifdef LOCAL_ROUTINES
3267
3272{
3273 bool found = false;
3274 int pid = ss_getpid();
3275 for (int i = 0; i < _database_entries; i++) {
3276 if (_database[i].attached) {
3277 db_lock_database(i + 1);
3278 db_allow_write_locked(&_database[i], "db_update_last_activity");
3279 assert(_database[i].database_header);
3280 /* update the last_activity entry to show that we are alive */
3281 for (int j=0; j<_database[i].database_header->max_client_index; j++) {
3283 //printf("client %d pid %d vs our pid %d\n", j, pdbclient->pid, pid);
3284 if (pdbclient->pid == pid) {
3285 pdbclient->last_activity = millitime;
3286 found = true;
3287 }
3288 }
3289 db_unlock_database(i + 1);
3290 }
3291 }
3292
3293 if (!found) {
3294 cm_msg(MERROR, "db_update_last_activity", "Did not find this client in any database. Maybe this client was removed by a timeout, see midas.log. Cannot continue, aborting...");
3295 abort();
3296 }
3297
3298 return DB_SUCCESS;
3299}
3300
3301#endif // LOCAL_ROUTINES
3302
3303#ifdef LOCAL_ROUTINES
3304static void db_delete_client_wlocked(DATABASE_HEADER* pheader, int jclient, db_err_msg** msg)
3305{
3306 DATABASE_CLIENT* pdbclient = &pheader->client[jclient];
3307
3308 /* decrement notify_count for open records and clear exclusive mode */
3309 int k;
3310 for (k = 0; k < pdbclient->max_index; k++)
3311 if (pdbclient->open_record[k].handle) {
3312 KEY* pkey = (KEY *) ((char *) pheader + pdbclient->open_record[k].handle);
3313 if (pkey->notify_count > 0)
3314 pkey->notify_count--;
3315
3316 if (pdbclient->open_record[k].access_mode & MODE_WRITE)
3317 db_set_mode_wlocked(pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2, msg);
3318 }
3319
3320 /* clear entry from client structure in buffer header */
3321 memset(pdbclient, 0, sizeof(DATABASE_CLIENT));
3322
3323 /* calculate new max_client_index entry */
3324 for (k = MAX_CLIENTS - 1; k >= 0; k--)
3325 if (pheader->client[k].pid != 0)
3326 break;
3327 pheader->max_client_index = k + 1;
3328
3329 /* count new number of clients */
3330 int nc;
3331 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
3332 if (pheader->client[k].pid != 0)
3333 nc++;
3334 pheader->num_clients = nc;
3335}
3336#endif
3337
3338#ifdef LOCAL_ROUTINES
3339static int db_delete_client_info_wlocked(DATABASE* pdb, int pid, db_err_msg** msg)
3340{
3341 if (!pid)
3342 pid = ss_getpid();
3343
3344 int status = 0;
3345
3346 std::string str = msprintf("System/Clients/%0d", pid);
3347
3348 DATABASE_HEADER* pheader = pdb->database_header;
3349
3350 KEY* pkey = (KEY*)db_find_pkey_locked(pheader, NULL, str.c_str(), true, &status, msg);
3351 if (!pkey) {
3352 return status;
3353 }
3354
3355 status = db_set_mode_wlocked(pheader, pkey, MODE_READ | MODE_WRITE | MODE_DELETE, 2, msg);
3356 status = db_delete_key_locked(pdb, pkey, 0, msg);
3357 int32_t data = 0;
3358 db_set_value_wlocked(pdb, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32, msg);
3359
3360 return status;
3361}
3362#endif
3363
3364/********************************************************************/
3372{
3373#ifdef LOCAL_ROUTINES
3374 if (hDB > _database_entries || hDB <= 0) {
3375 cm_msg(MERROR, "db_delete_client_info", "invalid database handle");
3376 return DB_INVALID_HANDLE;
3377 }
3378
3379 if (!_database[hDB - 1].attached) {
3380 cm_msg(MERROR, "db_delete_client_info", "invalid database handle");
3381 return DB_INVALID_HANDLE;
3382 }
3383
3384 /* lock database */
3386
3387 DATABASE *pdb = &_database[hDB - 1];
3388
3389 db_allow_write_locked(pdb, "db_delete_client_info");
3390
3391 db_err_msg* msg = NULL;
3392
3393 int status = db_delete_client_info_wlocked(pdb, pid, &msg);
3394
3396
3397 if (msg)
3398 db_flush_msg(&msg);
3399
3400 return status;
3401#else
3402 return DB_SUCCESS;
3403#endif
3404}
3405
3406void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
3407{
3408#ifdef LOCAL_ROUTINES
3409 int i;
3410 /* check online databases */
3411 for (i = 0; i < _database_entries; i++) {
3412 DATABASE *pdb = &_database[i];
3413 if (!pdb->attached) // empty slot
3414 continue;
3415
3416 db_lock_database(i + 1);
3417 db_allow_write_locked(pdb, "db_cleanup");
3418
3419 DATABASE_HEADER* pheader = pdb->database_header;
3421
3422 /* update the last_activity entry to show that we are alive */
3423 pclient->last_activity = actual_time;
3424
3425 /* don't check other clients if interval is stange */
3426 if (wrong_interval) {
3427 db_unlock_database(i + 1);
3428 continue;
3429 }
3430
3431 db_err_msg *msg = NULL;
3432
3433 /* now check other clients */
3434 int j;
3435 for (j = 0; j < pheader->max_client_index; j++) {
3436 DATABASE_CLIENT *pdbclient = &pheader->client[j];
3437 int client_pid = pdbclient->pid;
3438 if (client_pid == 0) // empty slot
3439 continue;
3440
3441 if (!ss_pid_exists(client_pid)) {
3442 db_msg(&msg, MINFO, "db_cleanup", "Client \'%s\' on database \'%s\' pid %d does not exist and db_cleanup called by %s removed it", pdbclient->name, pheader->name, client_pid, who);
3443
3444 db_delete_client_wlocked(pheader, j, &msg);
3445 db_delete_client_info_wlocked(pdb, client_pid, &msg);
3446
3447 continue;
3448 }
3449
3450 /* now make again the check with the buffer locked */
3452 if ((pdbclient->watchdog_timeout &&
3453 actual_time > pdbclient->last_activity &&
3454 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout)
3455 ) {
3456 db_msg(&msg, MINFO, "db_cleanup", "Client \'%s\' on database \'%s\' pid %d timed out and db_cleanup called by %s removed it (idle %1.1lfs,TO %1.0lfs)",
3457 pdbclient->name, pheader->name, client_pid,
3458 who,
3459 (actual_time - pdbclient->last_activity) / 1000.0,
3460 pdbclient->watchdog_timeout / 1000.0);
3461
3462 db_delete_client_wlocked(pheader, j, &msg);
3463 db_delete_client_info_wlocked(pdb, client_pid, &msg);
3464 }
3465 }
3466
3467 db_unlock_database(i + 1);
3468 if (msg)
3469 db_flush_msg(&msg);
3470 }
3471#endif
3472}
3473
3474#ifdef LOCAL_ROUTINES
3475
3476void db_cleanup2(const char* client_name, int ignore_timeout, DWORD actual_time, const char *who)
3477{
3478 /* check online databases */
3479 int i;
3480 for (i = 0; i < _database_entries; i++) {
3481 if (_database[i].attached) {
3482 /* update the last_activity entry to show that we are alive */
3483
3484 db_lock_database(i + 1);
3485
3486 db_err_msg *msg = NULL;
3487
3488 DATABASE* pdb = &_database[i];
3489
3490 db_allow_write_locked(pdb, "db_cleanup2");
3491
3492 DWORD now = ss_millitime();
3493
3494 DATABASE_HEADER* pheader = pdb->database_header;
3496 pclient->last_activity = now;
3497
3498 /* now check other clients */
3499 int j;
3500 for (j = 0; j < pheader->max_client_index; j++) {
3501 DATABASE_CLIENT* pdbclient = &pheader->client[j];
3502 if (j == pdb->client_index) // do not check ourselves
3503 continue;
3504 if (!pdbclient->pid) // empty slot
3505 continue;
3506 if ((client_name == NULL || client_name[0] == 0
3507 || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
3508 int client_pid = pdbclient->pid;
3509
3510 if (!ss_pid_exists(client_pid)) {
3511 db_msg(&msg, MINFO, "db_cleanup2", "Client \'%s\' on database \'%s\' pid %d does not exist and db_cleanup2 called by %s removed it",
3512 pdbclient->name, pheader->name,
3513 client_pid,
3514 who);
3515
3516 db_delete_client_wlocked(pheader, j, &msg);
3517 db_delete_client_info_wlocked(pdb, client_pid, &msg);
3518
3519 /* go again though whole list */
3520 j = 0;
3521 continue;
3522 }
3523
3524 DWORD interval;
3525 if (ignore_timeout)
3526 interval = 2 * WATCHDOG_INTERVAL;
3527 else
3528 interval = pdbclient->watchdog_timeout;
3529
3530 /* If client process has no activity, clear its buffer entry. */
3531
3532 if ((interval > 0 && now - pdbclient->last_activity > interval)) {
3533 db_msg(&msg, MINFO, "db_cleanup2", "Client \'%s\' on database \'%s\' timed out and db_cleanup2 called by %s removed it (idle %1.1lfs,TO %1.0lfs)",
3534 pdbclient->name, pheader->name,
3535 who,
3536 (now - pdbclient->last_activity) / 1000.0, interval / 1000.0);
3537
3538 db_delete_client_wlocked(pheader, j, &msg);
3539 db_delete_client_info_wlocked(pdb, client_pid, &msg);
3540
3541 /* go again though whole list */
3542 j = 0;
3543 continue;
3544 }
3545 }
3546 }
3547
3548 db_unlock_database(i + 1);
3549 if (msg)
3550 db_flush_msg(&msg);
3551 }
3552 }
3553}
3554
3556{
3557 /* set watchdog flag of all open databases */
3558 for (int i = _database_entries; i > 0; i--) {
3559
3561
3562 DATABASE* pdb = &_database[i - 1];
3563
3564 if (!pdb->attached) {
3566 continue;
3567 }
3568
3570
3571 db_allow_write_locked(pdb, "db_set_watchdog_params");
3572
3573 /* clear entry from client structure in buffer header */
3574 pclient->watchdog_timeout = timeout;
3575
3576 /* show activity */
3577 pclient->last_activity = ss_millitime();
3578
3580 }
3581}
3582
3583/********************************************************************/
3593INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD * timeout, DWORD * last)
3594{
3595 if (hDB > _database_entries || hDB <= 0) {
3596 cm_msg(MERROR, "db_get_watchdog_info", "invalid database handle");
3597 return DB_INVALID_HANDLE;
3598 }
3599
3600 if (!_database[hDB - 1].attached) {
3601 cm_msg(MERROR, "db_get_watchdog_info", "invalid database handle");
3602 return DB_INVALID_HANDLE;
3603 }
3604
3605 /* lock database */
3607
3608 DATABASE *pdb = &_database[hDB - 1];
3609 DATABASE_HEADER *pheader = pdb->database_header;
3610
3611 /* find client */
3612 for (int i = 0; i < pheader->max_client_index; i++) {
3613 DATABASE_CLIENT *pclient = &pheader->client[i];
3614 if (pclient->pid && equal_ustring(pclient->name, client_name)) {
3615 *timeout = pclient->watchdog_timeout;
3616 *last = ss_millitime() - pclient->last_activity;
3618 return DB_SUCCESS;
3619 }
3620 }
3621
3622 *timeout = *last = 0;
3623
3625
3626 return CM_NO_CLIENT;
3627}
3628
3629/********************************************************************/
3639{
3640 if (hDB > _database_entries || hDB <= 0) {
3641 cm_msg(MERROR, "db_check_client", "invalid database handle");
3642 return DB_INVALID_HANDLE;
3643 }
3644
3645 if (!_database[hDB - 1].attached) {
3646 cm_msg(MERROR, "db_check_client", "invalid database handle");
3647 return DB_INVALID_HANDLE;
3648 }
3649
3650 int status;
3651
3653
3654 db_err_msg* msg = NULL;
3655
3656 DATABASE *pdb = &_database[hDB - 1];
3657 DATABASE_HEADER *pheader = pdb->database_header;
3658
3659 const KEY* pkey = db_get_pkey(pheader, hKeyClient, &status, "db_check_client", &msg);
3660
3661 if (!pkey) {
3663 if (msg)
3664 db_flush_msg(&msg);
3665 return CM_NO_CLIENT;
3666 }
3667
3668 int client_pid = atoi(pkey->name);
3669
3670 const KEY* pkey_name = db_find_pkey_locked(pheader, pkey, "Name", true, &status, &msg);
3671
3672 if (!pkey_name) {
3674 if (msg)
3675 db_flush_msg(&msg);
3676 return CM_NO_CLIENT;
3677 }
3678
3679 char name[256];
3680 name[0] = 0;
3681 int size = sizeof(name);
3682 status = db_get_data_locked(pheader, pkey_name, 0, name, &size, TID_STRING, &msg);
3683
3684 //fprintf(stderr, "db_check_client: hkey %d, status %d, pid %d, name \'%s\'\n", hKeyClient, status, client_pid, name);
3685
3686 if (status != DB_SUCCESS) {
3688 if (msg)
3689 db_flush_msg(&msg);
3690 return CM_NO_CLIENT;
3691 }
3692
3693 bool dead = false;
3694 bool found = false;
3695
3696 /* loop through clients */
3697 for (int i = 0; i < pheader->max_client_index; i++) {
3698 DATABASE_CLIENT *pclient = &pheader->client[i];
3699 if (pclient->pid == client_pid) {
3700 found = true;
3701 break;
3702 }
3703 }
3704
3705 if (found) {
3706 /* check that the client is still running: PID still exists */
3707 if (!ss_pid_exists(client_pid)) {
3708 dead = true;
3709 }
3710 }
3711
3713
3714 if (!found || dead) {
3715 /* client not found : delete ODB stucture */
3716
3717 db_allow_write_locked(pdb, "db_check_client");
3718
3719 status = db_delete_client_info_wlocked(pdb, client_pid, &msg);
3720
3721 if (status != DB_SUCCESS)
3722 db_msg(&msg, MERROR, "db_check_client", "Cannot delete client info for client \'%s\', pid %d, db_delete_client_info() status %d", name, client_pid, status);
3723 else if (!found)
3724 db_msg(&msg, MINFO, "db_check_client", "Deleted entry \'/System/Clients/%d\' for client \'%s\' because it is not connected to ODB", client_pid, name);
3725 else if (dead)
3726 db_msg(&msg, MINFO, "db_check_client", "Deleted entry \'/System/Clients/%d\' for client \'%s\' because process pid %d does not exists", client_pid, name, client_pid);
3727
3729 }
3730
3732 if (msg)
3733 db_flush_msg(&msg);
3734
3735 return status;
3736}
3737
3738#endif // LOCAL_ROUTINES
3739
3740/********************************************************************/
3747{
3748 if (rpc_is_remote())
3749 return DB_SUCCESS;
3750
3751#ifdef LOCAL_ROUTINES
3752 if (hDB > _database_entries || hDB <= 0) {
3753 cm_msg(MERROR, "db_protect_database", "invalid database handle %d", hDB);
3754 return DB_INVALID_HANDLE;
3755 }
3756
3757 _database[hDB - 1].protect = TRUE;
3758 ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].database_header, _database[hDB - 1].shm_size);
3759 _database[hDB - 1].database_header = NULL;
3760#endif /* LOCAL_ROUTINES */
3761 return DB_SUCCESS;
3762}
3763
3764/*---- helper routines ---------------------------------------------*/
3765
3766const char *extract_key(const char *key_list, char *key_name, int key_name_length)
3767{
3768 int i = 0;
3769
3770 if (*key_list == '/')
3771 key_list++;
3772
3773 while (*key_list && *key_list != '/' && ++i < key_name_length)
3774 *key_name++ = *key_list++;
3775 *key_name = 0;
3776
3777 return key_list;
3778}
3779
3780BOOL equal_ustring(const char *str1, const char *str2)
3781{
3782 if (str1 == NULL && str2 != NULL)
3783 return FALSE;
3784 if (str1 != NULL && str2 == NULL)
3785 return FALSE;
3786 if (str1 == NULL && str2 == NULL)
3787 return TRUE;
3788 if (strlen(str1) != strlen(str2))
3789 return FALSE;
3790
3791 while (*str1)
3792 if (toupper(*str1++) != toupper(*str2++))
3793 return FALSE;
3794
3795 if (*str2)
3796 return FALSE;
3797
3798 return TRUE;
3799}
3800
3801BOOL ends_with_ustring(const char *str, const char *suffix)
3802{
3803 int len_str = strlen(str);
3804 int len_suffix = strlen(suffix);
3805
3806 // suffix is longer than the string
3807 if (len_suffix > len_str)
3808 return FALSE;
3809
3810 return equal_ustring(str + len_str - len_suffix, suffix);
3811}
3812
3813//utility function to match string against wildcard pattern
3814BOOL strmatch(char* pattern, char* str){
3815 switch(*pattern){
3816 case 0:
3817 if(*str == 0){
3818 //end of pattern
3819 return true;
3820 }else
3821 return false;
3822 case '*':
3823 {
3824 int i=0;
3825 // check for end of the string
3826 if(pattern[1] == 0) return true;
3827 // loop on all possible matches
3828 while(str[i] != 0) if(strmatch(pattern + 1, str+(i++))) return true;
3829 return false;
3830 }
3831 case '?':
3832 if(*str == 0){
3833 //end of string
3834 return false;
3835 } else {
3836 return strmatch(pattern+1, str+1);
3837 }
3838 default:
3839 if(*str == 0){
3840 //end of string
3841 return false;
3842 } else if(toupper(*str) == toupper(*pattern)){
3843 //recursion
3844 return strmatch(pattern+1, str+1);
3845 } else {
3846 return false;
3847 }
3848 }
3849}
3850
3851//utility function to extract array indexes
3852void strarrayindex(char* odbpath, int* index1, int* index2){
3853 char* pc;
3854
3855 *index1 = *index2 = 0;
3856 if (odbpath[strlen(odbpath) - 1] == ']') {
3857 if (strchr(odbpath, '[')) {
3858 if (*(strchr(odbpath, '[') + 1) == '*')
3859 *index1 = -1;
3860 else if (strchr((strchr(odbpath, '[') + 1), '.') ||
3861 strchr((strchr(odbpath, '[') + 1), '-')) {
3862 *index1 = atoi(strchr(odbpath, '[') + 1);
3863 pc = strchr(odbpath, '[') + 1;
3864 while (*pc != '.' && *pc != '-')
3865 pc++;
3866 while (*pc == '.' || *pc == '-')
3867 pc++;
3868 *index2 = atoi(pc);
3869 } else
3870 *index1 = atoi(strchr(odbpath, '[') + 1);
3871 }
3872
3873 //remove everything after bracket
3874 *strchr(odbpath, '[') = 0;
3875 }
3876}
3877
3878/********************************************************************/
3887INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
3888{
3889
3890 if (rpc_is_remote())
3891 return rpc_call(RPC_DB_CREATE_KEY, hDB, hKey, key_name, type);
3892
3893#ifdef LOCAL_ROUTINES
3894 {
3895 int status;
3896
3897 if (hDB > _database_entries || hDB <= 0) {
3898 cm_msg(MERROR, "db_create_key", "invalid database handle");
3899 return DB_INVALID_HANDLE;
3900 }
3901
3902 if (!_database[hDB - 1].attached) {
3903 cm_msg(MERROR, "db_create_key", "invalid database handle");
3904 return DB_INVALID_HANDLE;
3905 }
3906
3907 /* lock database */
3909
3910 DATABASE* pdb = &_database[hDB - 1];
3911 DATABASE_HEADER* pheader = pdb->database_header;
3912
3913 db_err_msg *msg = NULL;
3914
3915 KEY* pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_create_key", &msg);
3916
3917 if (!pkey) {
3919 if (msg)
3920 db_flush_msg(&msg);
3921 return DB_INVALID_HANDLE;
3922 }
3923
3924 db_allow_write_locked(pdb, "db_create_key");
3925
3926 KEY* newpkey = NULL;
3927
3928 status = db_create_key_wlocked(pheader, pkey, key_name, type, &newpkey, &msg);
3929
3931
3932 if (msg)
3933 db_flush_msg(&msg);
3934
3935 return status;
3936 }
3937#endif /* LOCAL_ROUTINES */
3938
3939 return DB_SUCCESS;
3940}
3941
3942#ifdef LOCAL_ROUTINES
3943
3944int db_create_key_wlocked(DATABASE_HEADER* pheader, KEY* parentKey, const char *key_name, DWORD type, KEY** pnewkey, db_err_msg** msg)
3945{
3946 int status;
3947
3948 if (pnewkey)
3949 *pnewkey = NULL;
3950
3951 if (parentKey== NULL) {
3952 parentKey = (KEY*) db_get_pkey(pheader, pheader->root_key, &status, "db_create_key_wlocked", msg);
3953 if (!parentKey) {
3954 return status;
3955 }
3956 }
3957
3958 //printf("db_create_key_wlocked: parent \"%s\", name \"%s\", type %d\n", db_get_path_pkey(pheader, parentKey).c_str(), key_name, type);
3959
3960 status = db_validate_name(key_name, TRUE, "db_create_key_wlocked", msg);
3961 if (status != DB_SUCCESS) {
3962 return status;
3963 }
3964
3965 /* check type */
3966 if (type <= 0 || type >= TID_LAST) {
3967 db_msg(msg, MERROR, "db_create_key", "invalid key type %d to create \'%s\' in \'%s\'", type, key_name, db_get_path_pkey(pheader, parentKey).c_str());
3968 return DB_INVALID_PARAM;
3969 }
3970
3971 const KEY* pdir = parentKey;
3972
3973 if (pdir->type != TID_KEY) {
3974 db_msg(msg, MERROR, "db_create_key", "cannot create \'%s\' in \'%s\' tid is %d, not a directory", key_name, db_get_path_pkey(pheader, pdir).c_str(), pdir->type);
3975 return DB_NO_KEY;
3976 }
3977
3978 KEY* pcreated = NULL;
3979
3980 const char* pkey_name = key_name;
3981 do {
3982 // key name is limited to NAME_LENGTH, but buffer has to be slightly longer
3983 // to prevent truncation before db_validate_name checks for correct length. K.O.
3984 char name[NAME_LENGTH+100];
3985
3986 /* extract single key from key_name */
3987 pkey_name = extract_key(pkey_name, name, sizeof(name));
3988
3989 status = db_validate_name(name, FALSE, "db_create_key", msg);
3990 if (status != DB_SUCCESS) {
3991 return status;
3992 }
3993
3994 /* do not allow empty names, like '/dir/dir//dir/' */
3995 if (name[0] == 0) {
3996 return DB_INVALID_PARAM;
3997 }
3998
3999 /* check if parent or current directory */
4000 if (strcmp(name, "..") == 0) {
4001 pdir = db_get_parent(pheader, pdir, &status, "db_create_key", msg);
4002 if (!pdir) {
4003 return status;
4004 }
4005 continue;
4006 }
4007 if (strcmp(name, ".") == 0)
4008 continue;
4009
4010 KEY* pprev = NULL;
4011 const KEY* pitem = db_enum_first_locked(pheader, pdir, msg);
4012
4013 while (pitem) {
4014 if (equal_ustring(name, pitem->name)) {
4015 break;
4016 }
4017 pprev = (KEY*)pitem;
4018 pitem = db_enum_next_locked(pheader, pdir, pitem, msg);
4019 }
4020
4021 if (!pitem) {
4022 /* not found: create new key */
4023
4024 /* check parent for write access */
4025 const KEY* pkeyparent = db_get_parent(pheader, pdir, &status, "db_create_key", msg);
4026 if (!pkeyparent) {
4027 return status;
4028 }
4029
4030 if (!(pkeyparent->access_mode & MODE_WRITE) || (pkeyparent->access_mode & MODE_EXCLUSIVE)) {
4031 return DB_NO_ACCESS;
4032 }
4033
4034 KEYLIST* pkeylist = (KEYLIST*)db_get_pkeylist(pheader, pdir, "db_create_key_wlocked", msg);
4035
4036 if (!pkeylist) {
4037 return DB_CORRUPTED;
4038 }
4039
4040 pkeylist->num_keys++;
4041
4042 if (*pkey_name == '/' || type == TID_KEY) {
4043 /* create new key with keylist */
4044 KEY* pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_create_key_subdir");
4045
4046 if (pkey == NULL) {
4047 if (pkeylist->num_keys > 0)
4048 pkeylist->num_keys--;
4049
4050 db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s\'", key_name, db_get_path_pkey(pheader, parentKey).c_str());
4051 return DB_FULL;
4052 }
4053
4054 /* append key to key list */
4055 if (pprev)
4056 pprev->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
4057 else
4058 pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
4059
4060 /* set key properties */
4061 pkey->type = TID_KEY;
4062 pkey->num_values = 1;
4064 mstrlcpy(pkey->name, name, sizeof(pkey->name));
4065 pkey->parent_keylist = (POINTER_T) pkeylist - (POINTER_T) pheader;
4066
4067 /* find space for new keylist */
4068 pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST), "db_create_key_B");
4069
4070 if (pkeylist == NULL) {
4071 db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s'", key_name, db_get_path_pkey(pheader, parentKey).c_str());
4072 return DB_FULL;
4073 }
4074
4075 /* store keylist in data field */
4076 pkey->data = (POINTER_T) pkeylist - (POINTER_T) pheader;
4077 pkey->item_size = sizeof(KEYLIST);
4078 pkey->total_size = sizeof(KEYLIST);
4079
4080 pkeylist->parent = (POINTER_T) pkey - (POINTER_T) pheader;
4081 pkeylist->num_keys = 0;
4082 pkeylist->first_key = 0;
4083
4084 pcreated = pkey;
4085 pdir = pkey; // descend into newly created subdirectory
4086 } else {
4087 /* create new key with data */
4088 KEY* pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_create_key_data");
4089
4090 if (pkey == NULL) {
4091 if (pkeylist->num_keys > 0)
4092 pkeylist->num_keys--;
4093
4094 db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s\'", key_name, db_get_path_pkey(pheader, parentKey).c_str());
4095 return DB_FULL;
4096 }
4097
4098 /* append key to key list */
4099 if (pprev)
4100 pprev->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
4101 else
4102 pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
4103
4104 pkey->type = type;
4105 pkey->num_values = 1;
4107 mstrlcpy(pkey->name, name, sizeof(pkey->name));
4108 pkey->parent_keylist = (POINTER_T) pkeylist - (POINTER_T) pheader;
4109
4110 /* allocate data */
4111 pkey->item_size = rpc_tid_size(type);
4112 if (pkey->item_size > 0) {
4113 void* data = malloc_data(pheader, pkey->item_size);
4114 if (data == NULL) {
4115 pkey->total_size = 0;
4116 db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s\'", key_name, db_get_path_pkey(pheader, parentKey).c_str());
4117 return DB_FULL;
4118 }
4119
4120 pkey->total_size = pkey->item_size;
4121 pkey->data = (POINTER_T)data - (POINTER_T)pheader;
4122 } else {
4123 /* first data is empty */
4124 pkey->item_size = 0;
4125 pkey->total_size = 0;
4126 pkey->data = 0;
4127 }
4128 pcreated = pkey;
4129 }
4130 } else {
4131 /* key found: descend */
4132
4133 /* resolve links */
4134 if (pitem->type == TID_LINK && pkey_name[0]) {
4135 pdir = db_resolve_link_locked(pheader, pitem, &status, msg);
4136 if (!pdir) {
4137 return status;
4138 }
4139 continue;
4140 }
4141
4142 if (!(*pkey_name == '/')) {
4143 if (pitem->type != type) {
4144 db_msg(msg, MERROR, "db_create_key", "object of type %d already exists at \"%s\" while creating \'%s\' of type %d in \'%s\'", pitem->type, db_get_path_pkey(pheader, pitem).c_str(), key_name, type, db_get_path_pkey(pheader, parentKey).c_str());
4145 return DB_TYPE_MISMATCH;
4146 }
4147 //db_print_pkey(pheader, pitem);
4148
4149 if (pnewkey)
4150 *pnewkey = (KEY*)pitem;
4151
4152 return DB_KEY_EXIST;
4153 }
4154
4155 if (pitem->type != TID_KEY) {
4156 db_msg(msg, MERROR, "db_create_key", "path element \"%s\" in \"%s\" is not a subdirectory at \"%s\" while creating \'%s\' in \'%s\'", name, key_name, db_get_path_pkey(pheader, pitem).c_str(), key_name, db_get_path_pkey(pheader, parentKey).c_str());
4157 return DB_TYPE_MISMATCH;
4158 }
4159
4160 pdir = pitem;
4161 }
4162 } while (*pkey_name == '/');
4163
4164 assert(pcreated != NULL);
4165
4166 if (pnewkey)
4167 *pnewkey = pcreated;
4168
4169 return DB_SUCCESS;
4170}
4171
4172#endif /* LOCAL_ROUTINES */
4173
4174/********************************************************************/
4183INT db_create_link(HNDLE hDB, HNDLE hKey, const char *link_name, const char *destination)
4184{
4185 HNDLE hkey;
4186 int status;
4187
4188 if (rpc_is_remote())
4189 return rpc_call(RPC_DB_CREATE_LINK, hDB, hKey, link_name, destination);
4190
4191 if (destination == NULL) {
4192 cm_msg(MERROR, "db_create_link", "link destination name is NULL");
4193 return DB_INVALID_NAME;
4194 }
4195
4196 if (destination[0] != '/') {
4197 cm_msg(MERROR, "db_create_link", "link destination name \'%s\' should start with \'/\', relative links are forbidden", destination);
4198 return DB_INVALID_NAME;
4199 }
4200
4201 if (strlen(destination) < 1) {
4202 cm_msg(MERROR, "db_create_link", "link destination name \'%s\' is too short", destination);
4203 return DB_INVALID_NAME;
4204 }
4205
4206 if ((destination[0] == '/') && (destination[1] == 0)) {
4207 cm_msg(MERROR, "db_create_link", "links to \"/\" are forbidden");
4208 return DB_INVALID_NAME;
4209 }
4210
4211 /* check if destination exists */
4212 status = db_find_key(hDB, hKey, destination, &hkey);
4213 if (status != DB_SUCCESS) {
4214 cm_msg(MERROR, "db_create_link", "Link destination \"%s\" does not exist", destination);
4215 return DB_NO_KEY;
4216 }
4217
4218 //printf("db_create_link: [%s] hkey %d\n", destination, hkey);
4219
4220 /* check if link already exists */
4221 status = db_find_link(hDB, hKey, link_name, &hkey);
4222 if (status != DB_SUCCESS) {
4223 // create new link
4224 status = db_set_value(hDB, hKey, link_name, destination, strlen(destination) + 1, 1, TID_LINK);
4225 } else {
4226 // check if key is TID_LINK
4227 KEY key;
4228 db_get_key(hDB, hkey, &key);
4229 if (key.type != TID_LINK) {
4230 cm_msg(MERROR, "db_create_link", "Existing key \"%s\" is not a link", link_name);
4231 return DB_KEY_EXIST;
4232 }
4233
4234 // modify existing link
4235 status = db_set_link_data(hDB, hkey, destination, strlen(destination)+1, 1, TID_LINK);
4236 }
4237
4238 return status;
4239}
4240
4241/********************************************************************/
4252#ifdef LOCAL_ROUTINES
4253static int db_check_open_record(const DATABASE_HEADER* pheader, const KEY* pkey, db_err_msg** msg)
4254{
4255 int status = 0;
4256
4257 while (1) {
4258#ifdef CHECK_OPEN_RECORD
4259 if (pkey->notify_count) {
4260 return DB_OPEN_RECORD;
4261 }
4262#endif
4263 if (pkey->parent_keylist == 0) // top level
4264 return DB_SUCCESS;
4265
4266 const KEY* pparent = db_get_parent(pheader, pkey, &status, "db_delete_key_locked", msg);
4267
4268 if (!pparent)
4269 return status;
4270
4271 pkey = pparent;
4272 };
4273
4274 return DB_SUCCESS;
4275}
4276
4277static int db_delete_key_locked(DATABASE* pdb, const KEY *pkey, int level, db_err_msg** msg)
4278{
4279 INT status;
4280
4281 DATABASE_HEADER* pheader = pdb->database_header;
4282
4283 //printf("db_delete_key_locked: path \"%s\", level %d\n", db_get_path_pkey(pheader, pkey).c_str(), level);
4284
4285 /* check if someone has opened key or parent */
4286 if (level == 0) {
4287 status = db_check_open_record(pheader, pkey, msg);
4288 if (status != DB_SUCCESS) {
4289 return status;
4290 }
4291 }
4292
4293 bool deny_delete = false;
4294
4295 if (pkey->type == TID_KEY) { // delete contents of subdirectory
4296
4297 const KEY* pkey1 = db_enum_first_locked(pheader, pkey, msg);
4298
4299 while (pkey1) {
4300 const KEY* pkeynext = db_enum_next_locked(pheader, pkey, pkey1, msg);
4301
4302 status = db_delete_key_locked(pdb, pkey1, level + 1, msg);
4303
4304 if (status == DB_NO_ACCESS)
4305 deny_delete = true;
4306
4307 pkey1 = pkeynext;
4308 }
4309 }
4310
4311 HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
4312
4313 /* return if key was already deleted by cyclic link */
4314
4315 if (pkey->parent_keylist == 0) {
4316 return DB_SUCCESS;
4317 }
4318
4319 /* do not delete the root */
4320
4321 if (hKey == pheader->root_key) {
4322 return DB_SUCCESS;
4323 }
4324
4325 /* check delete permission */
4326
4327 if (!(pkey->access_mode & MODE_DELETE) || deny_delete) {
4328 return DB_NO_ACCESS;
4329 }
4330
4331 /* check open record */
4332
4333#ifdef CHECK_OPEN_RECORD
4334 if (pkey->notify_count) {
4335 return DB_OPEN_RECORD;
4336 }
4337#endif
4338
4339 /* now delete key */
4340
4341 /* make ODB writable */
4342
4343 db_allow_write_locked(pdb, "db_delete_key_locked");
4344
4345 /* delete key data */
4346
4347 // FIXME: validate pkey->data and pkey->total_size.
4348
4349 if (pkey->type == TID_KEY) {
4350 free_key(pheader, (char *) pheader + pkey->data, pkey->total_size);
4351 } else {
4352 free_data(pheader, (char *) pheader + pkey->data, pkey->total_size, "db_delete_key_locked");
4353 }
4354
4355 /* unlink key from parent directory key list */
4356
4357 const KEY *pkey_parent = db_get_parent(pheader, pkey, &status, "db_delete_key_locked", msg);
4358
4359 const KEYLIST* pkeylist = db_get_pkeylist(pheader, pkey_parent, "db_delete_key_locked", msg);
4360
4361 if (pkeylist->first_key == hKey) {
4362 /* key is first in list */
4363 ((KEYLIST*)pkeylist)->first_key = pkey->next_key;
4364 } else {
4365 const KEY* pkey1 = db_enum_first_locked(pheader, pkey_parent, msg);
4366
4367 while (pkey1) {
4368 if (pkey1->next_key == hKey) {
4369 ((KEY*)pkey1)->next_key = pkey->next_key;
4370 break;
4371 }
4372
4373 pkey1 = db_enum_next_locked(pheader, pkey_parent, pkey1, msg);
4374 }
4375 }
4376
4377 ((KEYLIST*)pkeylist)->num_keys--;
4378
4379 /* poison deleted key */
4380
4381 KEY* wpkey = (KEY*)pkey;
4382
4383 wpkey->type = 9999;
4384 wpkey->num_values = 0;
4385 wpkey->data = 0;
4386 wpkey->total_size = 0;
4387 wpkey->item_size = 0;
4388 wpkey->next_key = 0;
4389 wpkey->parent_keylist = 0;
4390 wpkey->access_mode = 0;
4391 wpkey->notify_count = 0;
4392 wpkey->last_written = 0;
4393
4394 /* free key memory */
4395
4396 free_key(pheader, wpkey, sizeof(KEY));
4397
4398 return DB_SUCCESS;
4399}
4400
4401#endif /* LOCAL_ROUTINES */
4402
4403/********************************************************************/
4429{
4430 if (rpc_is_remote())
4432
4433#ifdef LOCAL_ROUTINES
4434
4435 int status = DB_SUCCESS;
4436
4437 DATABASE* pdb = db_lock_database(hDB, &status, "db_delete_key");
4438
4439 if (!pdb)
4440 return status;
4441
4442 db_err_msg *msg = NULL;
4443
4444 const KEY* pkey = db_get_pkey(pdb->database_header, hKey, &status, "db_delete_key", &msg);
4445
4446 if (pkey) {
4447 status = db_delete_key_locked(pdb, pkey, 0, &msg);
4448 }
4449
4450 db_unlock_database(pdb, "db_delete_key");
4451
4452 if (msg)
4453 db_flush_msg(&msg);
4454
4455 return status;
4456#else
4457 return DB_SUCCESS;
4458#endif
4459}
4460
4462{
4463 if (rpc_is_remote())
4465
4466#ifdef LOCAL_ROUTINES
4467
4468 int status = DB_SUCCESS;
4469
4470 DATABASE* pdb = db_lock_database(hDB, &status, "db_delete_key");
4471
4472 if (!pdb)
4473 return status;
4474
4475 db_err_msg *msg = NULL;
4476
4477 const KEY* pkey = db_get_pkey(pdb->database_header, hKey, &status, "db_delete_key", &msg);
4478
4479 if (pkey) {
4480 status = db_delete_key_locked(pdb, pkey, 0, &msg);
4481 }
4482
4483 db_unlock_database(pdb, "db_delete_key");
4484
4485 if (msg)
4486 db_flush_msg(&msg);
4487
4488 return status;
4489#else
4490 return DB_SUCCESS;
4491#endif
4492}
4493
4494INT db_delete(HNDLE hDB, HNDLE hKeyRoot, const char* odb_path)
4495{
4496 if (rpc_is_remote()) {
4497 HNDLE hKey;
4498 int status = db_find_link(hDB, hKeyRoot, odb_path, &hKey);
4499 if (status != DB_SUCCESS)
4500 return status;
4501 return db_delete_key(hDB, hKey);
4502 }
4503
4504#ifdef LOCAL_ROUTINES
4505
4506 int status = DB_SUCCESS;
4507
4508 DATABASE* pdb = db_lock_database(hDB, &status, "db_delete");
4509
4510 if (!pdb)
4511 return status;
4512
4513 db_err_msg *msg = NULL;
4514
4515 const KEY* pkeyroot = db_get_pkey(pdb->database_header, hKeyRoot, &status, "db_delete", &msg);
4516
4517 if (pkeyroot) {
4518 const KEY* pkey = db_find_pkey_locked(pdb->database_header, pkeyroot, odb_path, false, &status, &msg);
4519
4520 if (pkey) {
4521 status = db_delete_key_locked(pdb, pkey, 0, &msg);
4522 }
4523 }
4524
4525 db_unlock_database(pdb, "db_delete");
4526
4527 if (msg)
4528 db_flush_msg(&msg);
4529
4530 return status;
4531#else
4532 return DB_SUCCESS;
4533#endif
4534}
4535
4536#ifdef LOCAL_ROUTINES
4537static const KEY* db_find_pkey_locked(const DATABASE_HEADER *pheader, const KEY* pkey, const char *key_name, bool follow_links, int *pstatus, db_err_msg **msg)
4538{
4539 if (pkey == NULL) {
4540 pkey = db_get_pkey(pheader, pheader->root_key, pstatus, "db_find_key", msg);
4541 if (!pkey) {
4542 return NULL;
4543 }
4544 }
4545
4546 if (pkey->type != TID_KEY) {
4547 DWORD tid = pkey->type;
4548 std::string path = db_get_path_pkey(pheader, pkey);
4549 db_msg(msg, MERROR, "db_find_key", "Path \"%s\" tid %d is not a directory, looking for \"%s\"", path.c_str(), tid, key_name);
4550 if (pstatus)
4551 *pstatus = DB_NO_KEY;
4552 return NULL;
4553 }
4554
4555 if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
4556 if (!(pkey->access_mode & MODE_READ)) {
4557 if (pstatus)
4558 *pstatus = DB_NO_ACCESS;
4559 return NULL;
4560 }
4561 return pkey;
4562 }
4563
4564 const KEYLIST *pkeylist = db_get_pkeylist(pheader, pkey, "db_find_key", msg);
4565 if (!pkeylist) {
4566 if (pstatus)
4567 *pstatus = DB_CORRUPTED;
4568 return NULL;
4569 }
4570
4571 HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
4572
4573 HNDLE last_good_hkey = hKey;
4574
4575 const char *pkey_name = key_name;
4576 do {
4577 assert(pkeylist!=NULL); // should never happen!
4578
4579 char str[MAX_ODB_PATH];
4580
4581 /* extract single subkey from key_name */
4582 pkey_name = extract_key(pkey_name, str, sizeof(str));
4583
4584 /* strip trailing '[n]' */
4585 if (strchr(str, '[') && str[strlen(str) - 1] == ']')
4586 *strchr(str, '[') = 0;
4587
4588 /* check if parent or current directory */
4589 if (strcmp(str, "..") == 0) {
4590 if (pkey->parent_keylist) {
4591 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4592 // FIXME: validate pkeylist->parent
4593 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
4594 }
4595 continue;
4596 }
4597 if (strcmp(str, ".") == 0)
4598 continue;
4599
4600 last_good_hkey = hKey;
4601
4602 hKey = pkeylist->first_key;
4603
4604 if (hKey == 0) {
4605 // empty subdirectory
4606 if (pstatus)
4607 *pstatus = DB_NO_KEY;
4608 return NULL;
4609 }
4610
4611 int i;
4612 for (i = 0; i < pkeylist->num_keys; i++) {
4613 pkey = db_get_pkey(pheader, hKey, pstatus, "db_find_key", msg);
4614
4615 if (!pkey) {
4616 std::string path = db_get_path_locked(pheader, last_good_hkey);
4617 db_msg(msg, MERROR, "db_find_key", "hkey %d path \"%s\" invalid subdirectory entry hkey %d, looking for \"%s\"", last_good_hkey, path.c_str(), hKey, key_name);
4618 return NULL;
4619 }
4620
4621 if (!db_validate_key_offset(pheader, pkey->next_key)) {
4622 std::string path = db_get_path_locked(pheader, hKey);
4623 db_msg(msg, MERROR, "db_find_key", "hkey %d path \"%s\" invalid next_key %d, looking for \"%s\"", hKey, path.c_str(), pkey->next_key, key_name);
4624 if (pstatus)
4625 *pstatus = DB_CORRUPTED;
4626 return NULL;
4627 }
4628
4629 if (equal_ustring(str, pkey->name))
4630 break;
4631
4632 if (pkey->next_key == 0) {
4633 if (pstatus)
4634 *pstatus = DB_NO_KEY;
4635 return NULL;
4636 }
4637
4638 hKey = pkey->next_key;
4639 }
4640
4641 if (i == pkeylist->num_keys) {
4642 if (pstatus)
4643 *pstatus = DB_NO_KEY;
4644 return NULL;
4645 }
4646
4647 /* resolve links */
4648 if (pkey->type == TID_LINK && follow_links) {
4649 /* copy destination, strip '/' */
4650 mstrlcpy(str, (char *) pheader + pkey->data, sizeof(str));
4651 if (str[strlen(str) - 1] == '/')
4652 str[strlen(str) - 1] = 0;
4653
4654 /* if link is pointer to array index, return link instead of destination */
4655 if (str[strlen(str) - 1] == ']')
4656 break;
4657
4658 /* append rest of key name if existing */
4659 if (pkey_name[0]) {
4660 mstrlcat(str, pkey_name, sizeof(str));
4661 return db_find_pkey_locked(pheader, NULL, str, follow_links, pstatus, msg);
4662 } else {
4663 /* if last key in chain is a link, return its destination */
4664 int status = 0;
4665 const KEY *plink = db_find_pkey_locked(pheader, NULL, str, follow_links, &status, msg);
4666 if (pstatus) {
4667 if (status == DB_NO_KEY)
4668 *pstatus = DB_INVALID_LINK;
4669 else
4670 *pstatus = status;
4671 }
4672 return plink;
4673 }
4674 }
4675
4676 /* key found: check if last in chain */
4677 if (*pkey_name == '/') {
4678 if (pkey->type != TID_KEY) {
4679 if (pstatus)
4680 *pstatus = DB_NO_KEY;
4681 return NULL;
4682 }
4683 }
4684
4685 if (pkey->type == TID_KEY) {
4686 /* descend one level */
4687 pkeylist = db_get_pkeylist(pheader, pkey, "db_find_key", msg);
4688
4689 if (!pkeylist) {
4690 if (pstatus)
4691 *pstatus = DB_CORRUPTED;
4692 return NULL;
4693 }
4694 } else {
4695 pkeylist = NULL;
4696 }
4697
4698 } while (*pkey_name == '/' && *(pkey_name + 1));
4699
4700 return pkey;
4701}
4702
4703static int db_find_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, const char *key_name, bool follow_links, HNDLE *subhKey, db_err_msg **msg)
4704{
4705 int status;
4706 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_find_key", msg);
4707 if (!pkey) {
4708 if (subhKey)
4709 *subhKey = 0;
4710 return status;
4711 }
4712
4713 const KEY* plink = db_find_pkey_locked(pheader, pkey, key_name, follow_links, &status, msg);
4714
4715 if (!plink) {
4716 *subhKey = 0;
4717 return status;
4718 }
4719
4720 *subhKey = db_pkey_to_hkey(pheader, plink);
4721
4722 return DB_SUCCESS;
4723}
4724#endif /* LOCAL_ROUTINES */
4725
4726/********************************************************************/
4751INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4752{
4753 if (rpc_is_remote())
4754 return rpc_call(RPC_DB_FIND_KEY, hDB, hKey, key_name, subhKey);
4755
4756#ifdef LOCAL_ROUTINES
4757 {
4758 if (subhKey)
4759 *subhKey = 0;
4760
4762
4763 DATABASE* pdb = db_lock_database(hDB, &status, "db_find_key");
4764
4765 if (!pdb)
4766 return status;
4767
4768 db_err_msg *msg = NULL;
4769
4770 status = db_find_key_locked(pdb->database_header, hKey, key_name, true, subhKey, &msg);
4771
4772 db_unlock_database(pdb, "db_find_key");
4773
4774 if (msg)
4775 db_flush_msg(&msg);
4776
4777 return status;
4778 }
4779#endif /* LOCAL_ROUTINES */
4780
4781 return DB_SUCCESS;
4782}
4783
4785#ifndef DOXYGEN_SHOULD_SKIP_THIS
4786
4787/*------------------------------------------------------------------*/
4788INT db_find_link(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4789/********************************************************************\
4790
4791 Routine: db_find_link
4792
4793 Purpose: Find a key or link by name and return its handle
4794 (internal address). The only difference of this routine
4795 compared with db_find_key is that if the LAST key in
4796 the chain is a link, it is NOT evaluated. Links not being
4797 the last in the chain are evaluated.
4798
4799 Input:
4800 HNDLE bufer_handle Handle to the database
4801 HNDLE hKey Key handle to start the search
4802 char *key_name Name of key in the form "/key/key/key"
4803
4804 Output:
4805 INT *handle Key handle
4806
4807 Function value:
4808 DB_SUCCESS Successful completion
4809 DB_INVALID_HANDLE Database handle is invalid
4810 DB_NO_KEY Key doesn't exist
4811 DB_NO_ACCESS No access to read key
4812
4813\********************************************************************/
4814{
4815 if (rpc_is_remote())
4816 return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
4817
4818#ifdef LOCAL_ROUTINES
4819 {
4820 if (subhKey)
4821 *subhKey = 0;
4822
4824
4825 DATABASE* pdb = db_lock_database(hDB, &status, "db_find_link");
4826
4827 if (!pdb)
4828 return status;
4829
4830 db_err_msg *msg = NULL;
4831
4832 status = db_find_key_locked(pdb->database_header, hKey, key_name, false, subhKey, &msg);
4833
4834 db_unlock_database(pdb, "db_find_link");
4835
4836 if (msg)
4837 db_flush_msg(&msg);
4838
4839 return status;
4840 }
4841#endif /* LOCAL_ROUTINES */
4842
4843 return DB_SUCCESS;
4844}
4845
4846/*------------------------------------------------------------------*/
4847INT db_find_keys(HNDLE hDB, HNDLE hKeyRoot, const char* odbpath, std::vector<HNDLE> &hKeyVector)
4848/********************************************************************\
4849
4850 Routine: db_find_keys
4851
4852 Purpose: finds all ODB keys matching an odb path
4853 '*' and '?' wildcard expansion available
4854
4855 Input:
4856 HNDLE hDB Handle to the database
4857 HNDLE hKeyRoot Key handle to start the search
4858 char *odbpath Pattern of keys in the form "/key/key/key", can include '*' and '?'
4859
4860 Output:
4861 std::vector<HNDLE> &hKeyVector Key handle
4862
4863 Function value:
4864 DB_SUCCESS Successful completion
4865 DB_INVALID_HANDLE Database handle is invalid
4866 DB_NO_KEY No access to any key
4867 DB_NO_ACCESS No access to a read key
4868
4869\********************************************************************/
4870{
4871 HNDLE hKey;
4872 INT status;
4873 char *pattern = NULL;
4874 char *subkeypath = NULL;
4875 char *parentkeypath = NULL;
4876 char localpath[MAX_ODB_PATH];
4877
4878 //make work on a local copy od odbpath to allow recursion
4879 mstrlcpy(localpath, odbpath, sizeof(localpath));
4880 parentkeypath = localpath;
4881
4882 //printf("localpath [%s]\n", localpath);
4883
4884 char *wildcard = strpbrk(localpath, "*?");
4885 if (wildcard) {
4886 //wildcard found
4887 subkeypath = strchr(wildcard, '/');
4888 if (subkeypath) {
4889 //truncate the string at slash
4890 *subkeypath = 0;
4891 subkeypath++;
4892 }
4893 parentkeypath = strrchr(localpath, '/');
4894 if (parentkeypath) {
4895 //truncate there too
4896 *parentkeypath = 0;
4897 pattern = parentkeypath+1;
4898 if((parentkeypath-1) == localpath){
4899 //path starts with '/', no parent path
4900 parentkeypath = NULL;
4901 } else {
4902 parentkeypath = localpath;
4903 }
4904 } else {
4905 //wildcard at top level, start with pattern
4906 pattern = localpath;
4907 parentkeypath = NULL;
4908 }
4909 }
4910
4911 //printf("pattern [%s]\n", pattern);
4912 //printf("parentkeypath [%s]\n", parentkeypath);
4913 //printf("subkeypath [%s]\n", subkeypath);
4914
4915 //if available search for parent key path
4916 if (parentkeypath) {
4917 status = db_find_key(hDB, hKeyRoot, parentkeypath, &hKey);
4918 if (status != DB_SUCCESS)
4919 return status;
4920 } else {
4921 hKey = hKeyRoot;
4922 }
4923
4924 //printf("hKeyRoot %d, hKey %d\n", hKeyRoot, hKey);
4925
4926 if (!pattern) {
4927 //no pattern: hKey matches!
4928 hKeyVector.push_back(hKey);
4929 return DB_SUCCESS;
4930 }
4931
4932 // try match all subkeys
4933
4934 for (int i=0 ; ; i++) {
4935 HNDLE hSubKey;
4936
4937 db_enum_key(hDB, hKey, i, &hSubKey);
4938
4939 if (!hSubKey)
4940 break; // end of list reached
4941
4942 KEY key;
4943
4944 status = db_get_key(hDB, hSubKey, &key);
4945
4946 if (status != DB_SUCCESS)
4947 continue;
4948
4949 bool match = strmatch(pattern, key.name);
4950
4951 //printf("match pattern [%s] name [%s] yields %d\n", pattern, key.name, match);
4952
4953 if (match) {
4954 if (!subkeypath) {
4955 //found
4956 hKeyVector.push_back(hSubKey);
4957 } else if (key.type == TID_KEY) {
4958 //recurse with hSubKey as root key and subkeypath as path
4959 db_find_keys(hDB, hSubKey, subkeypath, hKeyVector);
4960 }
4961 }
4962 }
4963
4964 if (hKeyVector.empty())
4965 return DB_NO_KEY;
4966
4967 return DB_SUCCESS;
4968}
4969
4970/*------------------------------------------------------------------*/
4972/********************************************************************\
4973
4974 Routine: db_get_parent
4975
4976 Purpose: return an handle to the parent key
4977
4978 Input:
4979 HNDLE bufer_handle Handle to the database
4980 HNDLE hKey Key handle of the key
4981
4982 Output:
4983 INT *handle Parent key handle
4984
4985 Function value:
4986 DB_SUCCESS Successful completion
4987
4988\********************************************************************/
4989{
4990 if (rpc_is_remote())
4991 return rpc_call(RPC_DB_GET_PARENT, hDB, hKey, parenthKey);
4992
4993#ifdef LOCAL_ROUTINES
4994 {
4995
4996 DATABASE_HEADER *pheader;
4997 const KEY *pkey;
4998
4999 if (hDB > _database_entries || hDB <= 0) {
5000 cm_msg(MERROR, "db_get_parent", "invalid database handle");
5001 return DB_INVALID_HANDLE;
5002 }
5003
5004 if (!_database[hDB - 1].attached) {
5005 cm_msg(MERROR, "db_get_parent", "invalid database handle");
5006 return DB_INVALID_HANDLE;
5007 }
5008
5009 if (hKey < (int) sizeof(DATABASE_HEADER)) {
5010 cm_msg(MERROR, "db_get_parent", "invalid key handle");
5011 return DB_INVALID_HANDLE;
5012 }
5013
5015
5016 pheader = _database[hDB - 1].database_header;
5017 pkey = (const KEY *) ((char *) pheader + hKey);
5018
5019 /* find parent key */
5020 const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
5021
5022 if (!db_validate_hkey(pheader, pkeylist->parent)) {
5024 return DB_INVALID_HANDLE;
5025 }
5026
5027 pkey = (const KEY *) ((char *) pheader + pkeylist->parent);
5028
5029 *parenthKey = (POINTER_T) pkey - (POINTER_T) pheader;
5030
5032 }
5033#endif
5034
5035 return DB_SUCCESS;
5036}
5037
5038/*------------------------------------------------------------------*/
5040/********************************************************************\
5041
5042 Routine: db_scan_tree
5043
5044 Purpose: Scan a subtree recursively and call 'callback' for each key
5045
5046 Input:
5047 HNDLE hDB Handle to the database
5048 HNDLE hKeyRoot Key to start scan from, 0 for root
5049 INT level Recursion level
5050 void *callback Callback routine called with params:
5051 hDB Copy of hDB
5052 hKey Copy of hKey
5053 key Key associated with hKey
5054 INT Recursion level
5055 info Copy of *info
5056 void *info Optional data copied to callback routine
5057
5058 Output:
5059 implicit via callback
5060
5061 Function value:
5062 DB_SUCCESS Successful completion
5063 <all error codes of db_get_key>
5064
5065\********************************************************************/
5066{
5067 HNDLE hSubkey;
5068 KEY key;
5069 INT i, status;
5070
5072 if (status != DB_SUCCESS)
5073 return status;
5074
5075 status = callback(hDB, hKey, &key, level, info);
5076 if (status == 0)
5077 return status;
5078
5079 if (key.type == TID_KEY) {
5080 for (i = 0;; i++) {
5082
5083 if (!hSubkey)
5084 break;
5085
5086 db_scan_tree(hDB, hSubkey, level + 1, callback, info);
5087 }
5088 }
5089
5090 return DB_SUCCESS;
5091}
5092
5093#ifdef LOCAL_ROUTINES
5094
5095int db_scan_tree_locked(const DATABASE_HEADER* pheader, const KEY* pkey, int level, int(*callback) (const DATABASE_HEADER* pheader, const KEY *, int, void *, db_err_msg** msg), void *info, db_err_msg** msg)
5096{
5097 assert(pkey != NULL);
5098
5099 if (level >= MAX_ODB_PATH) {
5100 db_msg(msg, MERROR, "db_scan_tree_locked", "Max ODB depth level %d exceeded", MAX_ODB_PATH);
5101 return DB_TRUNCATED;
5102 }
5103
5104 int status = callback(pheader, pkey, level, info, msg);
5105 if (status == 0)
5106 return status;
5107
5108 if (pkey->type == TID_KEY) {
5109 const KEY* subkey = db_enum_first_locked(pheader, pkey, msg);
5110 while (subkey != NULL) {
5111 db_scan_tree_locked(pheader, subkey, level + 1, callback, info, msg);
5112 subkey = db_enum_next_locked(pheader, pkey, subkey, msg);
5113 }
5114 }
5115
5116 return DB_SUCCESS;
5117}
5118
5119#endif // LOCAL_ROUTINES
5120
5121/*------------------------------------------------------------------*/
5122INT db_scan_tree_link(HNDLE hDB, HNDLE hKey, INT level, void (*callback) (HNDLE, HNDLE, KEY *, INT, void *), void *info)
5123/********************************************************************\
5124
5125 Routine: db_scan_tree_link
5126
5127 Purpose: Scan a subtree recursively and call 'callback' for each key.
5128 Similar to db_scan_tree but without following links.
5129
5130 Input:
5131 HNDLE hDB Handle to the database
5132 HNDLE hKeyRoot Key to start scan from, 0 for root
5133 INT level Recursion level
5134 void *callback Callback routine called with params:
5135 hDB Copy of hDB
5136 hKey Copy of hKey
5137 key Key associated with hKey
5138 INT Recursion level
5139 info Copy of *info
5140 void *info Optional data copied to callback routine
5141
5142 Output:
5143 implicit via callback
5144
5145 Function value:
5146 DB_SUCCESS Successful completion
5147 <all error codes of db_get_key>
5148
5149\********************************************************************/
5150{
5151 HNDLE hSubkey;
5152 KEY key;
5153 INT i, status;
5154
5156 if (status != DB_SUCCESS)
5157 return status;
5158
5159 callback(hDB, hKey, &key, level, info);
5160
5161 if (key.type == TID_KEY) {
5162 for (i = 0;; i++) {
5164
5165 if (!hSubkey)
5166 break;
5167
5168 db_scan_tree_link(hDB, hSubkey, level + 1, callback, info);
5169 }
5170 }
5171
5172 return DB_SUCCESS;
5173}
5174
5175#ifdef LOCAL_ROUTINES
5176/*------------------------------------------------------------------*/
5177static std::string db_get_path_pkey(const DATABASE_HEADER* pheader, const KEY* pkey)
5178{
5179 std::string path = "";
5180 int level = 0;
5181 while (1) {
5182 //printf("db_get_path_pkey: hkey %d, pkey name \"%s\", type %d, parent %d, path \"%s\"\n", hKey, pkey->name, pkey->type, pkey->parent_keylist, path.c_str());
5183
5184 /* check key type */
5185 if (pkey->type <= 0 || pkey->type >= TID_LAST) {
5186 std::string xpath = msprintf("(INVALID_KEY_TYPE_%d)", pkey->type);;
5187 if (path.length() > 0) {
5188 xpath += "/";
5189 xpath += path;
5190 }
5191 return xpath;
5192 }
5193
5194 /* add key name in front of path */
5195 std::string str = path;
5196 path = "";
5197 if (pkey->name[0] == 0) {
5198 path += "(EMPTY_NAME)";
5199 } else {
5200 path += pkey->name;
5201 }
5202 if (str.length() > 0)
5203 path += "/";
5204 path += str;
5205
5206 if (!pkey->parent_keylist) {
5207 return path;
5208 }
5209
5210 if (!db_validate_data_offset(pheader, pkey->parent_keylist)) {
5211 return "(INVALID_PARENT_KEYLIST)/" + path;
5212 }
5213
5214 /* find parent key */
5215 const KEYLIST* pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
5216
5217 if (pkeylist->parent == pheader->root_key) {
5218 return "/" + path;
5219 }
5220
5221 if (pkeylist->parent == 0) {
5222 return "(NULL_PARENT)/" + path;
5223 }
5224
5225 if (!db_validate_key_offset(pheader, pkeylist->parent)) {
5226 return "(INVALID_PARENT)/" + path;
5227 }
5228
5229 pkey = (const KEY *) ((char *) pheader + pkeylist->parent);
5230
5231 level ++;
5232
5233 if (level >= MAX_ODB_PATH) {
5234 return "(TRUNCATED)/" + path;
5235 }
5236 };
5237
5238 /* NOT REACHED */
5239}
5240
5241/*------------------------------------------------------------------*/
5242static std::string db_get_path_locked(const DATABASE_HEADER* pheader, HNDLE hKey)
5243{
5244 //printf("db_get_path_locked: hkey %d\n", hKey);
5245
5246 if (!hKey)
5247 hKey = pheader->root_key;
5248
5249 if (hKey == pheader->root_key) {
5250 return "/";
5251 }
5252
5253 /* check if hKey argument is correct */
5254 if (hKey == 0) {
5255 return "(ZERO_HKEY)";
5256 }
5257
5258 /* check if hKey argument is correct */
5259 if (!db_validate_key_offset(pheader, hKey)) {
5260 return "(INVALID_HKEY)";
5261 }
5262
5263 const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
5264
5265 return db_get_path_pkey(pheader, pkey);
5266}
5267#endif /* LOCAL_ROUTINES */
5268
5269/*------------------------------------------------------------------*/
5270INT db_get_path(HNDLE hDB, HNDLE hKey, char *path, INT buf_size)
5271/********************************************************************\
5272
5273 Routine: db_get_path
5274
5275 Purpose: Get full path of a key
5276
5277 Input:
5278 HNDLE hDB Handle to the database
5279 HNDLE hKey Key handle
5280 INT buf_size Maximum size of path buffer (including
5281 trailing zero)
5282
5283 Output:
5284 char path[buf_size] Path string
5285
5286 Function value:
5287 DB_SUCCESS Successful completion
5288 DB_INVALID_HANDLE Database handle is invalid
5289 DB_NO_MEMORY path buffer is to small to contain full
5290 string
5291
5292\********************************************************************/
5293{
5294 if (rpc_is_remote())
5295 return rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, buf_size);
5296
5297#ifdef LOCAL_ROUTINES
5298 {
5299 int status = 0;
5300
5301 DATABASE* pdb = db_lock_database(hDB, &status, "db_get_path");
5302
5303 if (!pdb)
5304 return status;
5305
5306 std::string xpath = db_get_path_locked(pdb->database_header, hKey);
5307
5308 db_unlock_database(pdb, "db_get_path");
5309
5310 mstrlcpy(path, xpath.c_str(), buf_size);
5311
5312 return DB_SUCCESS;
5313 }
5314#endif /* LOCAL_ROUTINES */
5315
5316 return DB_SUCCESS;
5317}
5318
5319/*------------------------------------------------------------------*/
5321/********************************************************************\
5322
5323 Routine: db_get_path
5324
5325 Purpose: Get full path of a key
5326
5327 Input:
5328 HNDLE hDB Handle to the database
5329 HNDLE hKey Key handle
5330
5331 Function value: Path string
5332
5333\********************************************************************/
5334{
5335 if (rpc_is_remote()) {
5336 char path[MAX_ODB_PATH];
5337 int status = rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, sizeof(path));
5338 if (status != DB_SUCCESS) {
5339 return msprintf("(RPC_DB_GET_PATH status %d)", status);
5340 }
5341 return path;
5342 }
5343
5344#ifdef LOCAL_ROUTINES
5345 {
5346 int status = DB_SUCCESS;
5347
5348 DATABASE* pdb = db_lock_database(hDB, &status, "db_get_path");
5349
5350 if (!pdb)
5351 return "(CANNOT LOCK ODB)";
5352
5353 std::string xpath = db_get_path_locked(pdb->database_header, hKey);
5354
5355 db_unlock_database(pdb, "db_get_path");
5356
5357 return xpath;
5358 }
5359#endif /* LOCAL_ROUTINES */
5360
5361 return "(no LOCAL_ROUTINES)";
5362}
5363
5364#ifdef LOCAL_ROUTINES
5365/*------------------------------------------------------------------*/
5366static int db_find_open_records(HNDLE hDB, HNDLE hKey, KEY * key, INT level, void *xresult)
5367{
5368 /* check if this key has notify count set */
5369 if (key->notify_count) {
5370
5372
5374
5375 std::string path = db_get_path_locked(pheader, hKey);
5376
5377 std::string line = msprintf("%s open %d times by", path.c_str(), key->notify_count);
5378
5379 //printf("path [%s] key.name [%s]\n", path, key->name);
5380
5381 int count = 0;
5382 for (int i = 0; i < pheader->max_client_index; i++) {
5383 DATABASE_CLIENT *pclient = &pheader->client[i];
5384 for (int j = 0; j < pclient->max_index; j++)
5385 if (pclient->open_record[j].handle == hKey) {
5386 count++;
5387 line += " \"";
5388 line += pclient->name;
5389 line += "\"";
5390 //sprintf(line + strlen(line), ", handle %d, mode %d ", pclient->open_record[j].handle, pclient->open_record[j].access_mode);
5391 }
5392 }
5393
5394 if (count < 1) {
5395 line += " a deleted client";
5396 }
5397
5398 line += "\n";
5399
5400 std::string *result = (std::string*)xresult;
5401 *result += line;
5402
5404 }
5405 return DB_SUCCESS;
5406}
5407
5408static int db_fix_open_records(HNDLE hDB, HNDLE hKey, KEY * key, INT level, void *xresult)
5409{
5410 std::string *result = (std::string*)xresult;
5411
5412 /* check if this key has notify count set */
5413 if (key->notify_count) {
5416 db_allow_write_locked(&_database[hDB - 1], "db_fix_open_records");
5417
5418 int i;
5419 for (i = 0; i < pheader->max_client_index; i++) {
5420 DATABASE_CLIENT *pclient = &pheader->client[i];
5421 int j;
5422 for (j = 0; j < pclient->max_index; j++)
5423 if (pclient->open_record[j].handle == hKey)
5424 break;
5425 if (j < pclient->max_index)
5426 break;
5427 }
5428 if (i == pheader->max_client_index) {
5429 /* check if hKey argument is correct */
5430 if (!db_validate_hkey(pheader, hKey)) {
5432 return DB_SUCCESS;
5433 }
5434
5435 /* reset notify count */
5436 KEY *pkey = (KEY *) ((char *) pheader + hKey);
5437 pkey->notify_count = 0;
5438
5439 std::string path = db_get_path_locked(pheader, hKey);
5440 *result += path;
5441 *result += " fixed\n";
5442 }
5443
5445 }
5446 return DB_SUCCESS;
5447}
5448#endif /* LOCAL_ROUTINES */
5449
5451/********************************************************************\
5452
5453 Routine: db_get_open_records
5454
5455 Purpose: Return a string with all open records
5456
5457 Input:
5458 HNDLE hDB Handle to the database
5459 HNDLE hKey Key to start search from, 0 for root
5460 INT buf_size Size of string
5461 INT fix If TRUE, fix records which are open
5462 but have no client belonging to it.
5463
5464 Output:
5465 char *str Result string. Individual records are
5466 separated with new lines.
5467
5468 Function value:
5469 DB_SUCCESS Successful completion
5470
5471\********************************************************************/
5472{
5473 str[0] = 0;
5474
5475 if (rpc_is_remote())
5476 return rpc_call(RPC_DB_GET_OPEN_RECORDS, hDB, hKey, str, buf_size);
5477
5478 std::string result;
5479
5480#ifdef LOCAL_ROUTINES
5481
5482 if (fix)
5483 db_scan_tree(hDB, hKey, 0, db_fix_open_records, &result); // FIXME: should use db_scan_tree_wlocked()
5484 else
5485 db_scan_tree(hDB, hKey, 0, db_find_open_records, &result); // FIXME: should use db_scan_tree_locked()
5486
5487#endif
5488
5489 mstrlcpy(str, result.c_str(), buf_size);
5490
5491 return DB_SUCCESS;
5492}
5493
5495#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5496
5497
5498/********************************************************************/
5523INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data,
5524 INT data_size, INT num_values, DWORD type)
5525{
5526 if (rpc_is_remote())
5527 return rpc_call(RPC_DB_SET_VALUE, hDB, hKeyRoot, key_name, data, data_size, num_values, type);
5528
5529#ifdef LOCAL_ROUTINES
5530 {
5531 INT status;
5532
5533 if (num_values == 0)
5534 return DB_INVALID_PARAM;
5535
5537
5538 DATABASE* pdb = &_database[hDB - 1];
5539 DATABASE_HEADER* pheader = pdb->database_header;
5540
5541 db_allow_write_locked(pdb, "db_set_value");
5542
5543 db_err_msg* msg = NULL;
5544
5545 KEY* pkey_root = (KEY*)db_get_pkey(pheader, hKeyRoot, &status, "db_set_value", &msg);
5546
5547 if (pkey_root) {
5548 status = db_set_value_wlocked(pdb, pkey_root, key_name, data, data_size, num_values, type, &msg);
5549 }
5550
5552 if (msg)
5553 db_flush_msg(&msg);
5554
5555 return status;
5556 }
5557#endif /* LOCAL_ROUTINES */
5558
5559 return DB_SUCCESS;
5560}
5561
5562#ifdef LOCAL_ROUTINES
5563static int db_set_value_wlocked(DATABASE* pdb, KEY* pkey_root, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type, db_err_msg** msg)
5564{
5565 INT status;
5566
5567 if (num_values == 0)
5568 return DB_INVALID_PARAM;
5569
5570 DATABASE_HEADER* pheader = pdb->database_header;
5571
5572 KEY* pkey = (KEY*)db_find_pkey_locked(pheader, pkey_root, key_name, true, &status, msg);
5573
5574 if (!pkey) {
5575 status = db_create_key_wlocked(pheader, pkey_root, key_name, type, &pkey, msg);
5576 if (status != DB_SUCCESS && status != DB_CREATED)
5577 return status;
5578 }
5579
5580 if (data_size == 0) {
5581 db_msg(msg, MERROR, "db_set_value", "zero data size not allowed");
5582 return DB_TYPE_MISMATCH;
5583 }
5584
5585 if (type != TID_STRING && type != TID_LINK && data_size != rpc_tid_size(type) * num_values) {
5586 db_msg(msg, MERROR, "db_set_value", "\"%s\" data_size %d does not match tid %d size %d times num_values %d", key_name, data_size, type, rpc_tid_size(type), num_values);
5587 return DB_TYPE_MISMATCH;
5588 }
5589
5590 status = db_check_set_data_locked(pheader, pkey, data, data_size, num_values, type, "db_set_value", msg);
5591
5592 if (status != DB_SUCCESS)
5593 return status;
5594
5595 status = db_set_data_wlocked(pheader, pkey, data, data_size, num_values, type, "db_set_value", msg);
5596
5597 if (status != DB_SUCCESS)
5598 return status;
5599
5600 db_notify_clients_locked(pdb, db_pkey_to_hkey(pheader, pkey), -1, TRUE, msg);
5601
5602 return DB_SUCCESS;
5603}
5604#endif /* LOCAL_ROUTINES */
5605
5606/********************************************************************/
5630INT db_set_value_index(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data,
5631 INT data_size, INT idx, DWORD type, BOOL trunc)
5632{
5633 int status;
5634 HNDLE hkey;
5635
5636 status = db_find_key(hDB, hKeyRoot, key_name, &hkey);
5637 if (!hkey) {
5638 status = db_create_key(hDB, hKeyRoot, key_name, type);
5639 status = db_find_key(hDB, hKeyRoot, key_name, &hkey);
5640 if (status != DB_SUCCESS)
5641 return status;
5642 } else
5643 if (trunc) {
5644 status = db_set_num_values(hDB, hkey, idx + 1);
5645 if (status != DB_SUCCESS)
5646 return status;
5647 }
5648
5649 return db_set_data_index(hDB, hkey, data, data_size, idx, type);
5650}
5651
5652/********************************************************************/
5680INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT * buf_size, DWORD type, BOOL create)
5681{
5682 if (rpc_is_remote())
5683 return rpc_call(RPC_DB_GET_VALUE, hDB, hKeyRoot, key_name, data, buf_size, type, create);
5684
5685#ifdef LOCAL_ROUTINES
5686 {
5687 INT status;
5688
5689 if (hDB > _database_entries || hDB <= 0) {
5690 cm_msg(MERROR, "db_get_value", "invalid database handle %d", hDB);
5691 return DB_INVALID_HANDLE;
5692 }
5693
5694 if (!_database[hDB - 1].attached) {
5695 cm_msg(MERROR, "db_get_value", "invalid database handle %d", hDB);
5696 return DB_INVALID_HANDLE;
5697 }
5698
5699 /* check if key name contains index */
5700 char keyname[MAX_ODB_PATH];
5701 mstrlcpy(keyname, key_name, sizeof(keyname));
5702 int idx = -1;
5703 if (strchr(keyname, '[') && strchr(keyname, ']')) {
5704 char* p;
5705 for (p = strchr(keyname, '[') + 1; *p && *p != ']'; p++)
5706 if (!isdigit(*p))
5707 break;
5708
5709 if (*p && *p == ']') {
5710 idx = atoi(strchr(keyname, '[') + 1);
5711 *strchr(keyname, '[') = 0;
5712 }
5713 }
5714
5715 /* now lock database */
5717
5718 DATABASE* pdb = &_database[hDB - 1];
5719 DATABASE_HEADER* pheader = pdb->database_header;
5720 db_err_msg* msg = NULL;
5721
5722 const KEY* pkey_root = db_get_pkey(pheader, hKeyRoot, &status, "db_get_value", &msg);
5723
5724 if (!pkey_root) {
5726 if (msg)
5727 db_flush_msg(&msg);
5728 return status;
5729 }
5730
5731 const KEY* pkey = db_find_pkey_locked(pheader, pkey_root, keyname, true, &status, &msg);
5732
5733 if (!pkey) {
5734 if (create) {
5735 db_allow_write_locked(&_database[hDB-1], "db_get_value");
5736 /* set default value if key was created */
5737
5738 /* get string size from data size */
5739 int size;
5740 if (type == TID_STRING || type == TID_LINK)
5741 size = *buf_size;
5742 else
5743 size = rpc_tid_size(type);
5744
5745 status = db_set_value_wlocked(pdb, (KEY*)pkey_root, keyname, data, *buf_size, *buf_size / size, type, &msg);
5747 if (msg)
5748 db_flush_msg(&msg);
5749 return status;
5750 } else {
5752 if (msg)
5753 db_flush_msg(&msg);
5754 return DB_NO_KEY;
5755 }
5756 }
5757
5758 status = db_get_data_locked(pheader, pkey, idx, data, buf_size, type, &msg);
5759
5761 if (msg)
5762 db_flush_msg(&msg);
5763
5764 return status;
5765 }
5766#endif /* LOCAL_ROUTINES */
5767
5768 return DB_SUCCESS;
5769}
5770
5771#ifdef LOCAL_ROUTINES
5772static INT db_get_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, void *data, INT * buf_size, DWORD type, db_err_msg** msg)
5773{
5774 /* check for correct type */
5775 if (pkey->type != (type)) {
5776 db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" is of type %s, not %s", db_get_path_pkey(pheader, pkey).c_str(), rpc_tid_name(pkey->type), rpc_tid_name(type));
5777 return DB_TYPE_MISMATCH;
5778 }
5779
5780 /* check for correct type */
5781 if (pkey->type == TID_KEY) {
5782 db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" is of type %s, cannot contain data", db_get_path_pkey(pheader, pkey).c_str(), rpc_tid_name(pkey->type));
5783 return DB_TYPE_MISMATCH;
5784 }
5785
5786 /* check for read access */
5787 if (!(pkey->access_mode & MODE_READ)) {
5788 db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" has no read access", db_get_path_pkey(pheader, pkey).c_str());
5789 return DB_NO_ACCESS;
5790 }
5791
5792 /* check if buffer is too small */
5793 if ((idx == -1 && pkey->num_values * pkey->item_size > *buf_size) || (idx != -1 && pkey->item_size > *buf_size)) {
5794 memcpy(data, (char *) pheader + pkey->data, *buf_size);
5795 db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" data truncated, size is %d (%d*%d), buffer size is only %d", db_get_path_pkey(pheader, pkey).c_str(), pkey->num_values * pkey->item_size, pkey->num_values, pkey->item_size, *buf_size);
5796 return DB_TRUNCATED;
5797 }
5798
5799 /* check if index in boundaries */
5800 if (idx != -1 && idx >= pkey->num_values) {
5801 cm_msg(MERROR, "db_get_data_locked", "odb entry \"%s\" index %d is out of valid range 0..%d", db_get_path_pkey(pheader, pkey).c_str(), idx, pkey->num_values-1);
5802 return DB_INVALID_PARAM;
5803 }
5804
5805 /* copy key data */
5806 if (idx == -1) {
5807 memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
5808 *buf_size = pkey->num_values * pkey->item_size;
5809 } else {
5810 memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, pkey->item_size);
5811 *buf_size = pkey->item_size;
5812 }
5813
5814 return DB_SUCCESS;
5815}
5816#endif /* LOCAL_ROUTINES */
5817
5818/********************************************************************/
5852INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE * subkey_handle)
5853{
5854 if (rpc_is_remote())
5855 return rpc_call(RPC_DB_ENUM_KEY, hDB, hKey, idx, subkey_handle);
5856
5857#ifdef LOCAL_ROUTINES
5858 {
5859 DATABASE_HEADER *pheader;
5860 INT i;
5861 HNDLE parent;
5862
5863 if (hDB > _database_entries || hDB <= 0) {
5864 cm_msg(MERROR, "db_enum_key", "invalid database handle");
5865 return DB_INVALID_HANDLE;
5866 }
5867
5868 if (!_database[hDB - 1].attached) {
5869 cm_msg(MERROR, "db_enum_key", "invalid database handle");
5870 return DB_INVALID_HANDLE;
5871 }
5872
5873 *subkey_handle = 0;
5874
5875 /* first lock database */
5877
5878 pheader = _database[hDB - 1].database_header;
5879 if (!hKey)
5880 hKey = pheader->root_key;
5881
5882 /* check if hKey argument is correct */
5883 if (!db_validate_hkey(pheader, hKey)) {
5885 return DB_INVALID_HANDLE;
5886 }
5887
5888 db_err_msg *msg = NULL;
5889 int status;
5890
5891 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_enum_key", &msg);
5892
5893 if (!pkey) {
5895 db_flush_msg(&msg);
5896 return DB_NO_MORE_SUBKEYS;
5897 }
5898
5899 if (pkey->type != TID_KEY) {
5901 return DB_NO_MORE_SUBKEYS;
5902 }
5903
5904 const KEYLIST* pkeylist = db_get_pkeylist(pheader, pkey, "db_enum_key", &msg);
5905
5906 if (!pkeylist) {
5908 db_flush_msg(&msg);
5909 return DB_NO_MORE_SUBKEYS;
5910 }
5911
5912 if (idx >= pkeylist->num_keys) {
5914 return DB_NO_MORE_SUBKEYS;
5915 }
5916
5917 pkey = db_get_pkey(pheader, pkeylist->first_key, &status, "db_enum_key", &msg);
5918
5919 if (!pkey) {
5920 std::string path = db_get_path_locked(pheader, hKey);
5921 HNDLE xfirst_key = pkeylist->first_key;
5923 if (msg)
5924 db_flush_msg(&msg);
5925 cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" invalid first_key %d", hKey, path.c_str(), xfirst_key);
5926 return DB_NO_MORE_SUBKEYS;
5927 }
5928
5929 for (i = 0; i < idx; i++) {
5930 if (pkey->next_key == 0) {
5931 std::string path = db_get_path_locked(pheader, hKey);
5933 cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" unexpected end of key list at index %d", hKey, path.c_str(), i);
5934 return DB_NO_MORE_SUBKEYS;
5935 }
5936
5937 pkey = db_get_pkey(pheader, pkey->next_key, &status, "db_enum_key", &msg);
5938
5939 if (!pkey) {
5940 std::string path = db_get_path_locked(pheader, hKey);
5942 db_flush_msg(&msg);
5943 cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" invalid key list at index %d, next_key %d", hKey, path.c_str(), i, pkey->next_key);
5944 return DB_NO_MORE_SUBKEYS;
5945 }
5946 }
5947
5948 /* resolve links */
5949 if (pkey->type == TID_LINK) {
5950 const char* str = (char *) pheader + pkey->data;
5951
5952 /* no not resolve if link to array index */
5953 if (strlen(str) > 0 && str[strlen(str) - 1] == ']') {
5954 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5956 return DB_SUCCESS;
5957 }
5958
5959 if (*str == '/') {
5960 /* absolute path */
5962 return db_find_key(hDB, 0, str, subkey_handle);
5963 } else {
5964 /* relative path */
5965 if (pkey->parent_keylist) {
5966 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
5967 parent = pkeylist->parent;
5969 return db_find_key(hDB, parent, str, subkey_handle);
5970 } else {
5972 return db_find_key(hDB, 0, str, subkey_handle);
5973 }
5974 }
5975 }
5976
5977 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5979 }
5980#endif /* LOCAL_ROUTINES */
5981
5982 return DB_SUCCESS;
5983}
5984
5986#ifndef DOXYGEN_SHOULD_SKIP_THIS
5987
5988
5989/*------------------------------------------------------------------*/
5990INT db_enum_link(HNDLE hDB, HNDLE hKey, INT idx, HNDLE * subkey_handle)
5991/********************************************************************\
5992
5993 Routine: db_enum_link
5994
5995 Purpose: Enumerate subkeys from a key, don't follow links
5996
5997 Input:
5998 HNDLE hDB Handle to the database
5999 HNDLE hKey Handle of key to enumerate, zero for the
6000 root key
6001 INT idx Subkey index, sould be initially 0, then
6002 incremented in each call until
6003 *subhKey becomes zero and the function
6004 returns DB_NO_MORE_SUBKEYS
6005
6006 Output:
6007 HNDLE *subkey_handle Handle of subkey which can be used in
6008 db_get_key and db_get_data
6009
6010 Function value:
6011 DB_SUCCESS Successful completion
6012 DB_INVALID_HANDLE Database handle is invalid
6013 DB_NO_MORE_SUBKEYS Last subkey reached
6014
6015\********************************************************************/
6016{
6017 if (rpc_is_remote())
6018 return rpc_call(RPC_DB_ENUM_LINK, hDB, hKey, idx, subkey_handle);
6019
6020#ifdef LOCAL_ROUTINES
6021 {
6022 DATABASE_HEADER *pheader;
6023 KEYLIST *pkeylist;
6024 KEY *pkey;
6025 INT i;
6026
6027 if (hDB > _database_entries || hDB <= 0) {
6028 cm_msg(MERROR, "db_enum_link", "invalid database handle");
6029 return DB_INVALID_HANDLE;
6030 }
6031
6032 if (!_database[hDB - 1].attached) {
6033 cm_msg(MERROR, "db_enum_link", "invalid database handle");
6034 return DB_INVALID_HANDLE;
6035 }
6036
6037 *subkey_handle = 0;
6038
6039 /* first lock database */
6041
6042 pheader = _database[hDB - 1].database_header;
6043 if (!hKey)
6044 hKey = pheader->root_key;
6045
6046 /* check if hKey argument is correct */
6047 if (!db_validate_hkey(pheader, hKey)) {
6049 return DB_INVALID_HANDLE;
6050 }
6051
6052 pkey = (KEY *) ((char *) pheader + hKey);
6053
6054 if (pkey->type != TID_KEY) {
6056 return DB_NO_MORE_SUBKEYS;
6057 }
6058 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
6059
6060 if (idx >= pkeylist->num_keys) {
6062 return DB_NO_MORE_SUBKEYS;
6063 }
6064
6065 // FIXME: validate pkeylist->first_key
6066 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
6067 for (i = 0; i < idx; i++) {
6068 // FIXME: validate pkey->next_key
6069 pkey = (KEY *) ((char *) pheader + pkey->next_key);
6070 }
6071
6072 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
6074 }
6075#endif /* LOCAL_ROUTINES */
6076
6077 return DB_SUCCESS;
6078}
6079
6080/*------------------------------------------------------------------*/
6082/********************************************************************\
6083
6084 Routine: db_get_next_link
6085
6086 Purpose: Get next key in ODB after hKey
6087
6088 Input:
6089 HNDLE hDB Handle to the database
6090 HNDLE hKey Handle of key to enumerate, zero for the
6091 root key
6092
6093 Output:
6094 HNDLE *subkey_handle Handle of subkey which can be used in
6095 db_get_key and db_get_data
6096
6097 Function value:
6098 DB_SUCCESS Successful completion
6099 DB_INVALID_HANDLE Database handle is invalid
6100 DB_NO_MORE_SUBKEYS Last subkey reached
6101
6102\********************************************************************/
6103{
6104 if (rpc_is_remote())
6105 return rpc_call(RPC_DB_GET_NEXT_LINK, hDB, hKey, subkey_handle);
6106
6107#ifdef LOCAL_ROUTINES
6108 {
6109 DATABASE_HEADER *pheader;
6110 KEYLIST *pkeylist;
6111 KEY *pkey;
6112 INT descent;
6113
6114 if (hDB > _database_entries || hDB <= 0) {
6115 cm_msg(MERROR, "db_enum_link", "invalid database handle");
6116 return DB_INVALID_HANDLE;
6117 }
6118
6119 if (!_database[hDB - 1].attached) {
6120 cm_msg(MERROR, "db_enum_link", "invalid database handle");
6121 return DB_INVALID_HANDLE;
6122 }
6123
6124 *subkey_handle = 0;
6125
6126 /* first lock database */
6128
6129 pheader = _database[hDB - 1].database_header;
6130 if (!hKey)
6131 hKey = pheader->root_key;
6132
6133 /* check if hKey argument is correct */
6134 if (!db_validate_hkey(pheader, hKey)) {
6136 return DB_INVALID_HANDLE;
6137 }
6138
6139 pkey = (KEY *) ((char *) pheader + hKey);
6140
6141 descent = TRUE;
6142 do {
6143 if (pkey->type != TID_KEY || !descent) {
6144 if (pkey->next_key) {
6145 /* key has next key, return it */
6146 // FIXME: validate pkey->next_key
6147 pkey = (KEY *) ((char *) pheader + pkey->next_key);
6148
6149 if (pkey->type != TID_KEY) {
6150 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
6152 return DB_SUCCESS;
6153 }
6154
6155 /* key has subkeys, so descent on the next iteration */
6156 descent = TRUE;
6157 } else {
6158 if (pkey->parent_keylist == 0) {
6159 /* return if we are back to the root key */
6161 return DB_NO_MORE_SUBKEYS;
6162 }
6163
6164 /* key is last in list, traverse up */
6165 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
6166
6167 // FIXME: validate pkeylist->parent
6168 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
6169 descent = FALSE;
6170 }
6171 } else {
6172 if (descent) {
6173 /* find first subkey */
6174 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
6175
6176 if (pkeylist->num_keys == 0) {
6177 /* if key has no subkeys, look for next key on this level */
6178 descent = FALSE;
6179 } else {
6180 /* get first subkey */
6181 // FIXME: validate pkeylist->first_key
6182 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
6183
6184 if (pkey->type != TID_KEY) {
6185 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
6187 return DB_SUCCESS;
6188 }
6189 }
6190 }
6191 }
6192
6193 } while (TRUE);
6194 }
6195#endif /* LOCAL_ROUTINES */
6196
6197 return DB_SUCCESS;
6198}
6199
6200#ifdef LOCAL_ROUTINES
6201
6202static int db_enum_locked(const DATABASE_HEADER* pheader, const KEY* pkey, std::vector<HNDLE>* sub_handles, std::vector<KEY>* sub_keys, bool follow_links, db_err_msg** msg)
6203{
6204 if (sub_handles)
6205 sub_handles->clear();
6206
6207 if (sub_keys)
6208 sub_keys->clear();
6209
6210 if (pkey->type != TID_KEY) {
6211 return DB_NO_MORE_SUBKEYS;
6212 }
6213
6214 const KEYLIST *pkeylist = db_get_pkeylist(pheader, pkey, "db_enum_locked", msg);
6215
6216 if (!pkeylist)
6217 return DB_CORRUPTED;
6218
6219 if (pkeylist->num_keys > 0) {
6220 HNDLE hsubkey = pkeylist->first_key;
6221
6222 while (hsubkey) {
6223 int status = 0;
6224 const KEY* psubkey = db_get_pkey(pheader, hsubkey, &status, "db_enum_locked", msg);
6225
6226 if (!psubkey)
6227 return status;
6228
6229 if (psubkey->type == TID_LINK && follow_links) {
6230 const KEY *plink = db_resolve_link_locked(pheader, psubkey, &status, msg);
6231
6232 if (plink) {
6233 HNDLE hlink = db_pkey_to_hkey(pheader, plink);
6234 if (sub_handles)
6235 sub_handles->push_back(hlink);
6236 if (sub_keys)
6237 sub_keys->push_back(*plink);
6238 } else {
6239 // link cannot be resolved
6240
6241 if (sub_handles)
6242 sub_handles->push_back(hsubkey);
6243 if (sub_keys)
6244 sub_keys->push_back(*psubkey);
6245 }
6246 } else {
6247 if (sub_handles)
6248 sub_handles->push_back(hsubkey);
6249 if (sub_keys)
6250 sub_keys->push_back(*psubkey);
6251 }
6252
6253 hsubkey = psubkey->next_key;
6254 }
6255 }
6256
6257 return DB_SUCCESS;
6258}
6259
6260#endif
6261
6262/*------------------------------------------------------------------*/
6263INT EXPRT db_enum(HNDLE hDB, HNDLE hKeyRoot, std::vector<HNDLE>* sub_handles, std::vector<KEY>* sub_keys, bool follow_links)
6264/********************************************************************\
6265
6266 Routine: db_enum
6267
6268 Purpose: Enumerate subkeys from a key, follow or not follow links
6269
6270 Input:
6271 HNDLE hDB Handle to the database
6272 HNDLE hKey Handle of key to enumerate, zero for the root key
6273
6274 Output:
6275 std::vector<HNDLE>* sub_handles Handles of subkeys
6276 std::vector<KEY>* sub_keys Keys of subkeys
6277
6278 Function value:
6279 DB_SUCCESS Successful completion
6280 DB_INVALID_HANDLE Database handle is invalid
6281
6282\********************************************************************/
6283{
6284 if (rpc_is_remote()) {
6285 if (sub_handles)
6286 sub_handles->clear();
6287
6288 if (sub_keys)
6289 sub_keys->clear();
6290
6291 for (int i=0 ; ; i++) {
6292 HNDLE hSubkey = 0;
6293
6294 if (follow_links)
6295 db_enum_key(hDB, hKeyRoot, i, &hSubkey);
6296 else
6297 db_enum_link(hDB, hKeyRoot, i, &hSubkey);
6298
6299 if (!hSubkey)
6300 break; // end of list reached
6301
6302 KEY subkey;
6303
6304 int status = db_get_key(hDB, hSubkey, &subkey);
6305
6306 if (status == DB_SUCCESS) {
6307 if (sub_handles)
6308 sub_handles->push_back(hSubkey);
6309 if (sub_keys)
6310 sub_keys->push_back(subkey);
6311 }
6312 }
6313
6314 return DB_SUCCESS;
6315 }
6316
6317#ifdef LOCAL_ROUTINES
6318
6319 if (sub_handles)
6320 sub_handles->clear();
6321
6322 if (sub_keys)
6323 sub_keys->clear();
6324
6325 int status = DB_SUCCESS;
6326
6327 DATABASE* pdb = db_lock_database(hDB, &status, "db_enum");
6328
6329 if (!pdb)
6330 return status;
6331
6332 db_err_msg *msg = NULL;
6333
6334 const KEY* pkeyroot = db_get_pkey(pdb->database_header, hKeyRoot, &status, "db_enum", &msg);
6335
6336 if (pkeyroot) {
6337 status = db_enum_locked(pdb->database_header, pkeyroot, sub_handles, sub_keys, follow_links, &msg);
6338 }
6339
6340 db_unlock_database(pdb, "db_enum");
6341
6342 if (msg)
6343 db_flush_msg(&msg);
6344
6345 return status;
6346#endif
6347
6348 return DB_SUCCESS;
6349}
6350
6351/*------------------------------------------------------------------*/
6352INT EXPRT db_enum(HNDLE hDB, HNDLE hKeyRoot, const char* odb_path, std::vector<HNDLE>* sub_handles, std::vector<KEY>* sub_keys, bool follow_links)
6353/********************************************************************\
6354
6355 Routine: db_enum
6356
6357 Purpose: Enumerate subkeys from a key, follow or not follow links
6358
6359 Input:
6360 HNDLE hDB Handle to the database
6361 HNDLE hKeyRoot Handle of directory, zero for the root key
6362 char* odb_path ODB path under hKeyRoot to enumerate
6363
6364 Output:
6365 std::vector<HNDLE>* sub_handles Handles of subkeys
6366 std::vector<KEY>* sub_keys Keys of subkeys
6367
6368 Function value:
6369 DB_SUCCESS Successful completion
6370 DB_INVALID_HANDLE Database handle is invalid
6371
6372\********************************************************************/
6373{
6374 if (rpc_is_remote()) {
6375 if (sub_handles)
6376 sub_handles->clear();
6377
6378 if (sub_keys)
6379 sub_keys->clear();
6380
6381 int status = 0;
6382
6383 if (follow_links)
6384 status = db_find_key(hDB, hKeyRoot, odb_path, &hKeyRoot);
6385 else
6386 status = db_find_link(hDB, hKeyRoot, odb_path, &hKeyRoot);
6387
6388 if (!hKeyRoot)
6389 return status;
6390
6391 for (int i=0 ; ; i++) {
6392 HNDLE hSubkey = 0;
6393
6394 if (follow_links)
6395 db_enum_key(hDB, hKeyRoot, i, &hSubkey);
6396 else
6397 db_enum_link(hDB, hKeyRoot, i, &hSubkey);
6398
6399 if (!hSubkey)
6400 break; // end of list reached
6401
6402 KEY subkey;
6403
6404 int status = db_get_key(hDB, hSubkey, &subkey);
6405
6406 if (status == DB_SUCCESS) {
6407 if (sub_handles)
6408 sub_handles->push_back(hSubkey);
6409 if (sub_keys)
6410 sub_keys->push_back(subkey);
6411 }
6412 }
6413
6414 return DB_SUCCESS;
6415 }
6416
6417#ifdef LOCAL_ROUTINES
6418
6419 if (sub_handles)
6420 sub_handles->clear();
6421
6422 if (sub_keys)
6423 sub_keys->clear();
6424
6425 int status = DB_SUCCESS;
6426
6427 DATABASE* pdb = db_lock_database(hDB, &status, "db_enum");
6428
6429 if (!pdb)
6430 return status;
6431
6432 db_err_msg *msg = NULL;
6433
6434 const KEY* pkeyroot = db_get_pkey(pdb->database_header, hKeyRoot, &status, "db_enum", &msg);
6435
6436 if (pkeyroot) {
6437 const KEY* pkey = db_find_pkey_locked(pdb->database_header, pkeyroot, odb_path, follow_links, &status, &msg);
6438 if (pkey)
6439 status = db_enum_locked(pdb->database_header, pkey, sub_handles, sub_keys, follow_links, &msg);
6440 }
6441
6442 db_unlock_database(pdb, "db_enum");
6443
6444 if (msg)
6445 db_flush_msg(&msg);
6446
6447 return status;
6448
6449#endif
6450
6451 return DB_SUCCESS;
6452}
6453
6455#endif /* DOXYGEN_SHOULD_SKIP_THIS */
6456
6457/********************************************************************/
6458
6459#ifdef LOCAL_ROUTINES
6460
6462{
6463 if (!hKey)
6464 hKey = pheader->root_key;
6465
6466 /* check if hKey argument is correct */
6467 if (!db_validate_hkey(pheader, hKey)) {
6468 return DB_INVALID_HANDLE;
6469 }
6470
6471 const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
6472
6473 if (pkey->type < 1 || pkey->type >= TID_LAST) {
6474 int pkey_type = pkey->type;
6475 db_msg(msg, MERROR, "db_get_key", "hkey %d invalid key type %d", hKey, pkey_type);
6476 return DB_INVALID_HANDLE;
6477 }
6478
6479 /* check for link to array index */
6480 if (pkey->type == TID_LINK) {
6481 char link_name[MAX_ODB_PATH];
6482 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
6483 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
6484 if (strchr(link_name, '[') == NULL)
6485 return DB_INVALID_LINK;
6486
6487 HNDLE hkeylink;
6488 if (db_find_key_locked(pheader, 0, link_name, true, &hkeylink, msg) != DB_SUCCESS)
6489 return DB_INVALID_LINK;
6490 int status = db_get_key_locked(pheader, hkeylink, key, msg);
6491 key->num_values = 1; // fake number of values
6492 return status;
6493 }
6494 }
6495
6496 memcpy(key, pkey, sizeof(KEY));
6497
6498 return DB_SUCCESS;
6499}
6500#endif /* LOCAL_ROUTINES */
6501
6502/********************************************************************/
6539{
6540 if (rpc_is_remote())
6541 return rpc_call(RPC_DB_GET_KEY, hDB, hKey, key);
6542
6543#ifdef LOCAL_ROUTINES
6544 {
6545 DATABASE_HEADER *pheader;
6546 int status;
6547
6548 if (hDB > _database_entries || hDB <= 0) {
6549 cm_msg(MERROR, "db_get_key", "invalid database handle");
6550 return DB_INVALID_HANDLE;
6551 }
6552
6553 if (!_database[hDB - 1].attached) {
6554 cm_msg(MERROR, "db_get_key", "invalid database handle");
6555 return DB_INVALID_HANDLE;
6556 }
6557
6558 if (hKey < (int) sizeof(DATABASE_HEADER) && hKey != 0) {
6559 cm_msg(MERROR, "db_get_key", "invalid key handle");
6560 return DB_INVALID_HANDLE;
6561 }
6562
6563 db_err_msg *msg = NULL;
6564
6566
6567 pheader = _database[hDB - 1].database_header;
6568
6569 status = db_get_key_locked(pheader, hKey, key, &msg);
6570
6572
6573 if (msg)
6574 db_flush_msg(&msg);
6575
6576 return status;
6577 }
6578#endif /* LOCAL_ROUTINES */
6579
6580 return DB_SUCCESS;
6581}
6582
6583/********************************************************************/
6592{
6593 if (rpc_is_remote())
6594 return rpc_call(RPC_DB_GET_LINK, hDB, hKey, key);
6595
6596#ifdef LOCAL_ROUTINES
6597 {
6598 DATABASE_HEADER *pheader;
6599
6600 if (hDB > _database_entries || hDB <= 0) {
6601 cm_msg(MERROR, "db_get_link", "invalid database handle");
6602 return DB_INVALID_HANDLE;
6603 }
6604
6605 if (!_database[hDB - 1].attached) {
6606 cm_msg(MERROR, "db_get_link", "invalid database handle");
6607 return DB_INVALID_HANDLE;
6608 }
6609
6610 if (hKey < (int) sizeof(DATABASE_HEADER) && hKey != 0) {
6611 cm_msg(MERROR, "db_get_link", "invalid key handle");
6612 return DB_INVALID_HANDLE;
6613 }
6614
6615 db_err_msg *msg = NULL;
6616
6618
6619 pheader = _database[hDB - 1].database_header;
6620
6621 int status = DB_SUCCESS;
6622
6623 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_get_link", &msg);
6624 if (pkey) {
6625 memcpy(key, pkey, sizeof(KEY));
6626 } else {
6627 memset(key, 0, sizeof(KEY));
6628 //abort();
6629 }
6630
6632
6633 if (msg)
6634 db_flush_msg(&msg);
6635
6636 return status;
6637 }
6638#endif /* LOCAL_ROUTINES */
6639
6640 return DB_SUCCESS;
6641}
6642
6643/********************************************************************/
6652{
6653 if (rpc_is_remote())
6654 return rpc_call(RPC_DB_GET_KEY_TIME, hDB, hKey, delta);
6655
6656#ifdef LOCAL_ROUTINES
6657 {
6658 DATABASE_HEADER *pheader;
6659 KEY *pkey;
6660
6661 if (hDB > _database_entries || hDB <= 0) {
6662 cm_msg(MERROR, "db_get_key", "invalid database handle");
6663 return DB_INVALID_HANDLE;
6664 }
6665
6666 if (!_database[hDB - 1].attached) {
6667 cm_msg(MERROR, "db_get_key", "invalid database handle");
6668 return DB_INVALID_HANDLE;
6669 }
6670
6671 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6672 cm_msg(MERROR, "db_get_key", "invalid key handle");
6673 return DB_INVALID_HANDLE;
6674 }
6675
6677
6678 pheader = _database[hDB - 1].database_header;
6679
6680 /* check if hKey argument is correct */
6681 if (!db_validate_hkey(pheader, hKey)) {
6683 return DB_INVALID_HANDLE;
6684 }
6685
6686 pkey = (KEY *) ((char *) pheader + hKey);
6687
6688 *delta = ss_time() - pkey->last_written;
6689
6691
6692 }
6693#endif /* LOCAL_ROUTINES */
6694
6695 return DB_SUCCESS;
6696}
6697
6698/********************************************************************/
6710INT db_get_key_info(HNDLE hDB, HNDLE hKey, char *name, INT name_size, INT * type, INT * num_values, INT * item_size)
6711{
6712 if (rpc_is_remote())
6713 return rpc_call(RPC_DB_GET_KEY_INFO, hDB, hKey, name, name_size, type, num_values, item_size);
6714
6715#ifdef LOCAL_ROUTINES
6716 {
6717 DATABASE_HEADER *pheader;
6718 KEY *pkey;
6719 KEYLIST *pkeylist;
6720
6721 if (hDB > _database_entries || hDB <= 0) {
6722 cm_msg(MERROR, "db_get_key_info", "invalid database handle");
6723 return DB_INVALID_HANDLE;
6724 }
6725
6726 if (!_database[hDB - 1].attached) {
6727 cm_msg(MERROR, "db_get_key_info", "invalid database handle");
6728 return DB_INVALID_HANDLE;
6729 }
6730
6731 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6732 cm_msg(MERROR, "db_get_key_info", "invalid key handle");
6733 return DB_INVALID_HANDLE;
6734 }
6735
6737
6738 pheader = _database[hDB - 1].database_header;
6739
6740 /* check if hKey argument is correct */
6741 if (!db_validate_hkey(pheader, hKey)) {
6743 return DB_INVALID_HANDLE;
6744 }
6745
6746 pkey = (KEY *) ((char *) pheader + hKey);
6747
6748 if ((INT) strlen(pkey->name) + 1 > name_size) {
6749 /* truncate name */
6750 memcpy(name, pkey->name, name_size - 1);
6751 name[name_size] = 0;
6752 } else
6753 strcpy(name, pkey->name);
6754
6755 /* convert "root" to "/" */
6756 if (strcmp(name, "root") == 0)
6757 strcpy(name, "/");
6758
6759 *type = pkey->type;
6760 *num_values = pkey->num_values;
6761 *item_size = pkey->item_size;
6762
6763 if (pkey->type == TID_KEY) {
6764 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
6765 *num_values = pkeylist->num_keys;
6766 }
6767
6769 }
6770#endif /* LOCAL_ROUTINES */
6771
6772 return DB_SUCCESS;
6773}
6774
6776#ifndef DOXYGEN_SHOULD_SKIP_THIS
6777
6778
6779/*------------------------------------------------------------------*/
6781/********************************************************************\
6782
6783 Routine: db_get_key
6784
6785 Purpose: Rename a key
6786
6787 Input:
6788 HNDLE hDB Handle to the database
6789 HNDLE hKey Handle of key
6790 char *name New key name
6791
6792 Output:
6793 <none>
6794
6795 Function value:
6796 DB_SUCCESS Successful completion
6797 DB_INVALID_HANDLE Database handle is invalid
6798 DB_INVALID_NAME Key name contains '/'
6799
6800\********************************************************************/
6801{
6802 if (rpc_is_remote())
6804
6805#ifdef LOCAL_ROUTINES
6806 {
6807 DATABASE_HEADER *pheader;
6808 KEY *pkey;
6809 int status;
6810
6811 if (hDB > _database_entries || hDB <= 0) {
6812 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6813 return DB_INVALID_HANDLE;
6814 }
6815
6816 if (!_database[hDB - 1].attached) {
6817 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6818 return DB_INVALID_HANDLE;
6819 }
6820
6821 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6822 cm_msg(MERROR, "db_rename_key", "invalid key handle");
6823 return DB_INVALID_HANDLE;
6824 }
6825
6826 db_err_msg* msg = NULL;
6827 status = db_validate_name(name, FALSE, "db_rename_key", &msg);
6828 if (msg)
6829 db_flush_msg(&msg);
6830 if (status != DB_SUCCESS)
6831 return status;
6832
6833 if (name == NULL) {
6834 cm_msg(MERROR, "db_rename_key", "key name is NULL");
6835 return DB_INVALID_NAME;
6836 }
6837
6838 if (strlen(name) < 1) {
6839 cm_msg(MERROR, "db_rename_key", "key name is too short");
6840 return DB_INVALID_NAME;
6841 }
6842
6843 if (strchr(name, '/')) {
6844 cm_msg(MERROR, "db_rename_key", "key name may not contain \"/\"");
6845 return DB_INVALID_NAME;
6846 }
6847
6849
6850 pheader = _database[hDB - 1].database_header;
6851
6852 /* check if hKey argument is correct */
6853 if (!db_validate_hkey(pheader, hKey)) {
6855 return DB_INVALID_HANDLE;
6856 }
6857
6858 pkey = (KEY *) ((char *) pheader + hKey);
6859
6860 if (!pkey->type) {
6861 int pkey_type = pkey->type;
6863 cm_msg(MERROR, "db_rename_key", "hkey %d invalid key type %d", hKey, pkey_type);
6864 return DB_INVALID_HANDLE;
6865 }
6866
6867 db_allow_write_locked(&_database[hDB - 1], "db_rename_key");
6868
6869 mstrlcpy(pkey->name, name, NAME_LENGTH);
6870
6872
6873 }
6874#endif /* LOCAL_ROUTINES */
6875
6876 return DB_SUCCESS;
6877}
6878
6879/*------------------------------------------------------------------*/
6881/********************************************************************\
6882
6883 Routine: db_reorder_key
6884
6885 Purpose: Reorder key so that key hKey apprears at position 'index'
6886 in keylist (or at bottom if index<0)
6887
6888 Input:
6889 HNDLE hDB Handle to the database
6890 HNDLE hKey Handle of key
6891 INT idx New positio of key in keylist
6892
6893 Output:
6894 <none>
6895
6896 Function value:
6897 DB_SUCCESS Successful completion
6898 DB_INVALID_HANDLE Database handle is invalid
6899 DB_NO_ACCESS Key is locked for write
6900 DB_OPEN_RECORD Key, subkey or parent key is open
6901
6902\********************************************************************/
6903{
6904 if (rpc_is_remote())
6905 return rpc_call(RPC_DB_REORDER_KEY, hDB, hKey, idx);
6906
6907#ifdef LOCAL_ROUTINES
6908 {
6909 DATABASE_HEADER *pheader;
6910 KEY *pkey, *pnext_key, *pkey_tmp;
6911 KEYLIST *pkeylist;
6912 INT i;
6913
6914 if (hDB > _database_entries || hDB <= 0) {
6915 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6916 return DB_INVALID_HANDLE;
6917 }
6918
6919 if (!_database[hDB - 1].attached) {
6920 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6921 return DB_INVALID_HANDLE;
6922 }
6923
6924 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6925 cm_msg(MERROR, "db_rename_key", "invalid key handle");
6926 return DB_INVALID_HANDLE;
6927 }
6928
6930
6931 pheader = _database[hDB - 1].database_header;
6932
6933 /* check if hKey argument is correct */
6934 if (!db_validate_hkey(pheader, hKey)) {
6936 return DB_INVALID_HANDLE;
6937 }
6938
6939 pkey = (KEY *) ((char *) pheader + hKey);
6940
6941 if (!pkey->type) {
6942 int pkey_type = pkey->type;
6944 cm_msg(MERROR, "db_reorder_key", "hkey %d invalid key type %d", hKey, pkey_type);
6945 return DB_INVALID_HANDLE;
6946 }
6947
6948 if (!(pkey->access_mode & MODE_WRITE)) {
6950 return DB_NO_ACCESS;
6951 }
6952
6953 /* check if someone has opened key or parent */
6954 do {
6955#ifdef CHECK_OPEN_RECORD
6956 if (pkey->notify_count) {
6958 return DB_OPEN_RECORD;
6959 }
6960#endif
6961 if (pkey->parent_keylist == 0)
6962 break;
6963
6964 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
6965 // FIXME: validate pkeylist->parent
6966 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
6967 } while (TRUE);
6968
6969 db_allow_write_locked(&_database[hDB - 1], "db_reorder_key");
6970
6971 pkey = (KEY *) ((char *) pheader + hKey); // NB: hKey is already validated
6972 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
6973
6974 /* first remove key from list */
6975 pnext_key = (KEY *) (POINTER_T) pkey->next_key; // FIXME: what is this pointer cast?
6976
6977 if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
6978 /* key is first in list */
6979 pkeylist->first_key = (POINTER_T) pnext_key;
6980 } else {
6981 /* find predecessor */
6982 // FIXME: validate pkeylist->first_key
6983 pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
6984 while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey) {
6985 // FIXME: validate pkey_tmp->next_key
6986 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
6987 }
6988 pkey_tmp->next_key = (POINTER_T) pnext_key;
6989 }
6990
6991 /* add key to list at proper index */
6992 // FIXME: validate pkeylist->first_key
6993 pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
6994 if (idx < 0 || idx >= pkeylist->num_keys - 1) {
6995 /* add at bottom */
6996
6997 /* find last key */
6998 for (i = 0; i < pkeylist->num_keys - 2; i++) {
6999 // FIXME: validate pkey_tmp->next_key
7000 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
7001 }
7002
7003 pkey_tmp->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
7004 pkey->next_key = 0;
7005 } else {
7006 if (idx == 0) {
7007 /* add at top */
7008 pkey->next_key = pkeylist->first_key;
7009 pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
7010 } else {
7011 /* add at position index */
7012 for (i = 0; i < idx - 1; i++) {
7013 // FIXME: validate pkey_tmp->next_key
7014 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
7015 }
7016
7017 pkey->next_key = pkey_tmp->next_key;
7018 pkey_tmp->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
7019 }
7020 }
7021
7023
7024 }
7025#endif /* LOCAL_ROUTINES */
7026
7027 return DB_SUCCESS;
7028}
7029
7031#endif /* DOXYGEN_SHOULD_SKIP_THIS */
7032
7033
7034/********************************************************************/
7059{
7060 if (rpc_is_remote())
7061 return rpc_call(RPC_DB_GET_DATA, hDB, hKey, data, buf_size, type);
7062
7063#ifdef LOCAL_ROUTINES
7064 {
7065 int status;
7066
7067 if (hDB > _database_entries || hDB <= 0) {
7068 cm_msg(MERROR, "db_get_data", "Invalid database handle");
7069 return DB_INVALID_HANDLE;
7070 }
7071
7072 if (!_database[hDB - 1].attached) {
7073 cm_msg(MERROR, "db_get_data", "invalid database handle");
7074 return DB_INVALID_HANDLE;
7075 }
7076
7077 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7078 cm_msg(MERROR, "db_get_data", "invalid key handle");
7079 return DB_INVALID_HANDLE;
7080 }
7081
7083
7085 db_err_msg* msg = NULL;
7086
7087 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_get_data", &msg);
7088
7089 if (!pkey) {
7091 if (msg)
7092 db_flush_msg(&msg);
7093 return status;
7094 }
7095
7096 /* check for read access */
7097 if (!(pkey->access_mode & MODE_READ)) {
7099 if (msg)
7100 db_flush_msg(&msg);
7101 return DB_NO_ACCESS;
7102 }
7103
7104 /* follow links to array index */
7105 if (pkey->type == TID_LINK) {
7106 std::string link_name = (char *) pheader + pkey->data;
7107 if (link_name.length() > 0 && link_name.back() == ']') {
7108 size_t pos = link_name.rfind("[");
7109 if (pos == std::string::npos) {
7110 db_msg(&msg, MERROR, "db_get_data", "missing \"[\" in symlink to array element \"%s\" in \"%s\"", link_name.c_str(), db_get_path_pkey(pheader, pkey).c_str());
7112 if (msg)
7113 db_flush_msg(&msg);
7114 return DB_INVALID_LINK;
7115 }
7116 int idx = atoi(link_name.c_str()+pos+1);
7117 link_name.resize(pos);
7118 //printf("link name [%s] idx %d\n", link_name.c_str(), idx);
7119
7120 // relative symlinks did not work in the old db_get_data(), make sure they do not work now. K.O.
7121 if (link_name[0] != '/') {
7122 db_msg(&msg, MERROR, "db_get_data", "symlink \"%s\" should start with \"/\" in \"%s\"", link_name.c_str(), db_get_path_pkey(pheader, pkey).c_str());
7124 if (msg)
7125 db_flush_msg(&msg);
7126 return DB_INVALID_LINK;
7127 }
7128
7129 const KEY* pkey = db_find_pkey_locked(pheader, NULL, link_name.c_str(), true, &status, &msg);
7130
7131 if (!pkey) {
7133 if (msg)
7134 db_flush_msg(&msg);
7135 return status;
7136 }
7137
7138 //printf("db_get_data [%s] type [%s] idx %d\n", db_get_path_pkey(pheader, pkey).c_str(), rpc_tid_name(type), idx);
7139
7140 status = db_get_data_locked(pheader, pkey, idx, data, buf_size, type, &msg);
7141
7143 if (msg)
7144 db_flush_msg(&msg);
7145 return status;
7146 }
7147 }
7148
7149 //printf("db_get_data [%s] type [%s]\n", db_get_path_pkey(pheader, pkey).c_str(), rpc_tid_name(type));
7150
7151 status = db_get_data_locked(pheader, pkey, -1, data, buf_size, type, &msg);
7152
7154 if (msg)
7155 db_flush_msg(&msg);
7156
7157 return status;
7158 }
7159#endif /* LOCAL_ROUTINES */
7160
7161 return DB_SUCCESS;
7162}
7163
7164/********************************************************************/
7176{
7177 if (rpc_is_remote())
7178 return rpc_call(RPC_DB_GET_LINK_DATA, hDB, hKey, data, buf_size, type);
7179
7180#ifdef LOCAL_ROUTINES
7181 {
7182 DATABASE_HEADER *pheader;
7183 KEY *pkey;
7184
7185 if (hDB > _database_entries || hDB <= 0) {
7186 cm_msg(MERROR, "db_get_data", "Invalid database handle");
7187 return DB_INVALID_HANDLE;
7188 }
7189
7190 if (!_database[hDB - 1].attached) {
7191 cm_msg(MERROR, "db_get_data", "invalid database handle");
7192 return DB_INVALID_HANDLE;
7193 }
7194
7195 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7196 cm_msg(MERROR, "db_get_data", "invalid key handle");
7197 return DB_INVALID_HANDLE;
7198 }
7199
7201
7202 pheader = _database[hDB - 1].database_header;
7203
7204 /* check if hKey argument is correct */
7205 if (!db_validate_hkey(pheader, hKey)) {
7207 return DB_INVALID_HANDLE;
7208 }
7209
7210 pkey = (KEY *) ((char *) pheader + hKey);
7211
7212 /* check for read access */
7213 if (!(pkey->access_mode & MODE_READ)) {
7215 return DB_NO_ACCESS;
7216 }
7217
7218 if (!pkey->type) {
7219 int pkey_type = pkey->type;
7221 cm_msg(MERROR, "db_get_data", "hkey %d invalid key type %d", hKey, pkey_type);
7222 return DB_INVALID_HANDLE;
7223 }
7224
7225 if (pkey->type != type) {
7226 int pkey_type = pkey->type;
7227 char pkey_name[NAME_LENGTH];
7228 mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
7230 cm_msg(MERROR, "db_get_data", "\"%s\" is of type %s, not %s", pkey_name, rpc_tid_name(pkey_type), rpc_tid_name(type));
7231 return DB_TYPE_MISMATCH;
7232 }
7233
7234 /* keys cannot contain data */
7235 if (pkey->type == TID_KEY) {
7237 cm_msg(MERROR, "db_get_data", "Key cannot contain data");
7238 return DB_TYPE_MISMATCH;
7239 }
7240
7241 /* check if key has data */
7242 if (pkey->data == 0) {
7243 memset(data, 0, *buf_size);
7244 *buf_size = 0;
7246 return DB_SUCCESS;
7247 }
7248
7249 /* check if buffer is too small */
7250 if (pkey->num_values * pkey->item_size > *buf_size) {
7251 int pkey_size = pkey->num_values * pkey->item_size;
7252 memcpy(data, (char *) pheader + pkey->data, *buf_size);
7254 std::string path = db_get_path(hDB, hKey);
7255 cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
7256 return DB_TRUNCATED;
7257 }
7258
7259 /* copy key data */
7260 memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
7261 *buf_size = pkey->num_values * pkey->item_size;
7262
7264
7265 }
7266#endif /* LOCAL_ROUTINES */
7267
7268 return DB_SUCCESS;
7269}
7270
7272#ifndef DOXYGEN_SHOULD_SKIP_THIS
7273
7274/*------------------------------------------------------------------*/
7275INT db_get_data1(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, DWORD type, INT * num_values)
7276/********************************************************************\
7277
7278 Routine: db_get_data1
7279
7280 Purpose: Get key data from a handle, return number of values
7281
7282 Input:
7283 HNDLE hDB Handle to the database
7284 HNDLE hKey Handle of key
7285 INT *buf_size Size of data buffer
7286 DWORD type Type of data
7287
7288 Output:
7289 void *data Key data
7290 INT *buf_size Size of key data
7291 INT *num_values Number of values
7292
7293 Function value:
7294 DB_SUCCESS Successful completion
7295 DB_INVALID_HANDLE Database handle is invalid
7296 DB_TRUNCATED Return buffer is smaller than key data
7297 DB_TYPE_MISMATCH Type mismatch
7298
7299\********************************************************************/
7300{
7301 if (rpc_is_remote())
7302 return rpc_call(RPC_DB_GET_DATA1, hDB, hKey, data, buf_size, type, num_values);
7303
7304#ifdef LOCAL_ROUTINES
7305 {
7306 DATABASE_HEADER *pheader;
7307 KEY *pkey;
7308
7309 if (hDB > _database_entries || hDB <= 0) {
7310 cm_msg(MERROR, "db_get_data", "Invalid database handle");
7311 return DB_INVALID_HANDLE;
7312 }
7313
7314 if (!_database[hDB - 1].attached) {
7315 cm_msg(MERROR, "db_get_data", "invalid database handle");
7316 return DB_INVALID_HANDLE;
7317 }
7318
7319 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7320 cm_msg(MERROR, "db_get_data", "invalid key handle");
7321 return DB_INVALID_HANDLE;
7322 }
7323
7325
7326 pheader = _database[hDB - 1].database_header;
7327
7328 /* check if hKey argument is correct */
7329 if (!db_validate_hkey(pheader, hKey)) {
7331 return DB_INVALID_HANDLE;
7332 }
7333
7334 pkey = (KEY *) ((char *) pheader + hKey);
7335
7336 /* check for read access */
7337 if (!(pkey->access_mode & MODE_READ)) {
7339 return DB_NO_ACCESS;
7340 }
7341
7342 if (!pkey->type) {
7343 int pkey_type = pkey->type;
7345 cm_msg(MERROR, "db_get_data", "hkey %d invalid key type %d", hKey, pkey_type);
7346 return DB_INVALID_HANDLE;
7347 }
7348
7349 if (pkey->type != type) {
7350 int pkey_type = pkey->type;
7351 char pkey_name[NAME_LENGTH];
7352 mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
7354 cm_msg(MERROR, "db_get_data", "\"%s\" is of type %s, not %s", pkey_name, rpc_tid_name(pkey_type), rpc_tid_name(type));
7355 return DB_TYPE_MISMATCH;
7356 }
7357
7358 /* keys cannot contain data */
7359 if (pkey->type == TID_KEY) {
7361 cm_msg(MERROR, "db_get_data", "Key cannot contain data");
7362 return DB_TYPE_MISMATCH;
7363 }
7364
7365 /* check if key has data */
7366 if (pkey->data == 0) {
7367 memset(data, 0, *buf_size);
7368 *buf_size = 0;
7370 return DB_SUCCESS;
7371 }
7372
7373 /* check if buffer is too small */
7374 if (pkey->num_values * pkey->item_size > *buf_size) {
7375 int pkey_size = pkey->num_values * pkey->item_size;
7376 memcpy(data, (char *) pheader + pkey->data, *buf_size);
7378 std::string path = db_get_path(hDB, hKey);
7379 cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
7380 return DB_TRUNCATED;
7381 }
7382
7383 /* copy key data */
7384 memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
7385 *buf_size = pkey->num_values * pkey->item_size;
7386 *num_values = pkey->num_values;
7387
7389
7390 }
7391#endif /* LOCAL_ROUTINES */
7392
7393 return DB_SUCCESS;
7394}
7395
7397#endif /* DOXYGEN_SHOULD_SKIP_THIS */
7398
7399/********************************************************************/
7413{
7414 if (rpc_is_remote())
7415 return rpc_call(RPC_DB_GET_DATA_INDEX, hDB, hKey, data, buf_size, idx, type);
7416
7417#ifdef LOCAL_ROUTINES
7418 {
7419 DATABASE_HEADER *pheader;
7420 KEY *pkey;
7421
7422 if (hDB > _database_entries || hDB <= 0) {
7423 cm_msg(MERROR, "db_get_data", "Invalid database handle");
7424 return DB_INVALID_HANDLE;
7425 }
7426
7427 if (!_database[hDB - 1].attached) {
7428 cm_msg(MERROR, "db_get_data", "invalid database handle");
7429 return DB_INVALID_HANDLE;
7430 }
7431
7432 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7433 cm_msg(MERROR, "db_get_data", "invalid key handle");
7434 return DB_INVALID_HANDLE;
7435 }
7436
7438
7439 pheader = _database[hDB - 1].database_header;
7440
7441 /* check if hKey argument is correct */
7442 if (!db_validate_hkey(pheader, hKey)) {
7444 return DB_INVALID_HANDLE;
7445 }
7446
7447 pkey = (KEY *) ((char *) pheader + hKey);
7448
7449 /* check for read access */
7450 if (!(pkey->access_mode & MODE_READ)) {
7452 return DB_NO_ACCESS;
7453 }
7454
7455 if (!pkey->type) {
7456 int pkey_type = pkey->type;
7458 cm_msg(MERROR, "db_get_data_index", "hkey %d invalid key type %d", hKey, pkey_type);
7459 return DB_INVALID_HANDLE;
7460 }
7461
7462 if (pkey->type != type) {
7463 int pkey_type = pkey->type;
7464 char pkey_name[NAME_LENGTH];
7465 mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
7467 cm_msg(MERROR, "db_get_data_index", "\"%s\" is of type %s, not %s", pkey_name, rpc_tid_name(pkey_type), rpc_tid_name(type));
7468 return DB_TYPE_MISMATCH;
7469 }
7470
7471 /* keys cannot contain data */
7472 if (pkey->type == TID_KEY) {
7474 cm_msg(MERROR, "db_get_data_index", "Key cannot contain data");
7475 return DB_TYPE_MISMATCH;
7476 }
7477
7478 /* check if key has data */
7479 if (pkey->data == 0) {
7480 memset(data, 0, *buf_size);
7481 *buf_size = 0;
7483 return DB_SUCCESS;
7484 }
7485
7486 /* check if index in range */
7487 if (idx < 0 || idx >= pkey->num_values) {
7488 int pkey_num_values = pkey->num_values;
7489 memset(data, 0, *buf_size);
7491
7492 std::string path = db_get_path(hDB, hKey);
7493 cm_msg(MERROR, "db_get_data_index", "index (%d) exceeds array length (%d) for key \"%s\"", idx, pkey_num_values, path.c_str());
7494 return DB_OUT_OF_RANGE;
7495 }
7496
7497 /* check if buffer is too small */
7498 if (pkey->item_size > *buf_size) {
7499 int pkey_size = pkey->item_size;
7500 /* copy data */
7501 memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, *buf_size);
7503 std::string path = db_get_path(hDB, hKey);
7504 cm_msg(MERROR, "db_get_data_index", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
7505 return DB_TRUNCATED;
7506 }
7507
7508 /* copy key data */
7509 memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, pkey->item_size);
7510 *buf_size = pkey->item_size;
7511
7513
7514 }
7515#endif /* LOCAL_ROUTINES */
7516
7517 return DB_SUCCESS;
7518}
7519
7520#ifdef LOCAL_ROUTINES
7521/********************************************************************/
7533static INT db_set_data_wlocked(DATABASE_HEADER* pheader, KEY* pkey, const void *data, INT data_size, INT num_values, DWORD type, const char* caller, db_err_msg** msg)
7534{
7535 /* if no buf_size given (Java!), calculate it */
7536 if (data_size == 0)
7537 data_size = pkey->item_size * num_values;
7538
7539 /* resize data size if necessary */
7540 if (pkey->total_size != data_size) {
7541 // FIXME: validate pkey->data!
7542 pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, data_size, caller);
7543
7544 if (pkey->data == 0) {
7545 pkey->total_size = 0;
7546 db_msg(msg, MERROR, caller, "Cannot reallocate \"%s\" with new size %d bytes, online database full", db_get_path_pkey(pheader, pkey).c_str(), data_size);
7547 return DB_FULL;
7548 }
7549
7550 pkey->data -= (POINTER_T) pheader;
7551 pkey->total_size = data_size;
7552 }
7553
7554 /* set number of values */
7555 pkey->num_values = num_values;
7556
7557 if (type == TID_STRING || type == TID_LINK)
7558 pkey->item_size = data_size / num_values;
7559 else
7560 pkey->item_size = rpc_tid_size(type);
7561
7562 if ((type == TID_STRING || type == TID_LINK) && pkey->num_values == 1) {
7563 /* copy string up to NUL termination */
7564 mstrlcpy((char *) pheader + pkey->data, (const char*)data, data_size);
7565 } else {
7566 /* copy data */
7567 memcpy((char *) pheader + pkey->data, data, data_size);
7568 }
7569
7570 /* update time */
7571 pkey->last_written = ss_time();
7572
7573 return DB_SUCCESS;
7574}
7575
7576static INT db_set_data_index_wlocked(DATABASE_HEADER* pheader, KEY* pkey, int idx, const void *data, INT data_size, DWORD type, const char* caller, db_err_msg** msg)
7577{
7578 /* increase key size if necessary */
7579 if (idx >= pkey->num_values || pkey->item_size == 0) {
7580 // FIXME: validate pkey->data
7581 pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, data_size * (idx + 1), caller);
7582
7583 if (pkey->data == 0) {
7584 pkey->total_size = 0;
7585 pkey->num_values = 0;
7586 db_msg(msg, MERROR, caller, "Cannot reallocate \"%s\" with new num_values %d and new size %d bytes, online database full", db_get_path_pkey(pheader, pkey).c_str(), idx + 1, data_size * (idx + 1));
7587 return DB_FULL;
7588 }
7589
7590 pkey->data -= (POINTER_T) pheader;
7591 if (!pkey->item_size)
7592 pkey->item_size = data_size;
7593 pkey->total_size = data_size * (idx + 1);
7594 pkey->num_values = idx + 1;
7595 }
7596
7597#if 0
7598 /* cut strings which are too long */
7599 if ((type == TID_STRING || type == TID_LINK) && (int) strlen((char *) data) + 1 > pkey->item_size)
7600 *((char *) data + pkey->item_size - 1) = 0;
7601
7602 /* copy data */
7603 memcpy((char *) pheader + pkey->data + idx * pkey->item_size, data, pkey->item_size);
7604#endif
7605
7606 if ((type == TID_STRING || type == TID_LINK)) {
7607 /* cut strings which are too long */
7608 mstrlcpy((char *) pheader + pkey->data + idx * pkey->item_size, (char*)data, pkey->item_size);
7609 } else {
7610 /* copy data */
7611 memcpy((char *) pheader + pkey->data + idx * pkey->item_size, data, pkey->item_size);
7612 }
7613
7614 /* update time */
7615 pkey->last_written = ss_time();
7616
7617 return DB_SUCCESS;
7618}
7619
7620static INT db_check_set_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, const void *data, INT data_size, INT num_values, DWORD type, const char* caller, db_err_msg** msg)
7621{
7622 /* check for write access */
7623 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7624 return DB_NO_ACCESS;
7625 }
7626
7627 if (pkey->type != type) {
7628 db_msg(msg, MERROR, caller, "\"%s\" is of type %s, not %s", db_get_path_pkey(pheader, pkey).c_str(), rpc_tid_name(pkey->type), rpc_tid_name(type));
7629 return DB_TYPE_MISMATCH;
7630 }
7631
7632 /* keys cannot contain data */
7633 if (pkey->type == TID_KEY) {
7634 db_msg(msg, MERROR, caller, "\"%s\" of type TID_KEY cannot contain data", db_get_path_pkey(pheader, pkey).c_str());
7635 return DB_TYPE_MISMATCH;
7636 }
7637
7638 if (type == TID_STRING || type == TID_LINK) {
7639 if (num_values > 1) {
7640 int item_size = pkey->item_size;
7641 if (data_size > 0 && num_values > 0)
7642 item_size = data_size/num_values;
7643 //printf("db_check_set_data for %s: utf8 check for odb \"%s\" string array size %d, item size %d\n", caller, db_get_path_pkey(pheader, pkey).c_str(), num_values, item_size);
7644 for (int i=0; i<num_values; i++) {
7645 const char* value = ((const char*)data) + i * item_size;
7646 //printf("db_check_set_data for %s: utf8 check for odb \"%s\" string array size %d item_size %d, index %d, value \"%s\"\n", caller, db_get_path_pkey(pheader, pkey).c_str(), num_values, item_size, i, value);
7647 if (!is_utf8(value)) {
7648 db_msg(msg, MERROR, caller, "\"%s\" index %d is not a valid UTF-8 Unicode string", db_get_path_pkey(pheader, pkey).c_str(), i);
7649 // just a warning for now. K.O.
7650 //return DB_TYPE_MISMATCH;
7651 }
7652 }
7653 } else {
7654 const char* value = (const char*)data;
7655 //printf("db_check_set_data for %s: utf8 check for odb \"%s\" value \"%s\" size %d\n", caller, db_get_path_pkey(pheader, pkey).c_str(), value, data_size);
7656 if (!is_utf8(value)) {
7657 db_msg(msg, MERROR, caller, "\"%s\" is not a valid UTF-8 Unicode string", db_get_path_pkey(pheader, pkey).c_str());
7658 // just a warning for now. K.O.
7659 //return DB_TYPE_MISMATCH;
7660 }
7661 }
7662 }
7663
7664 return DB_SUCCESS;
7665}
7666
7667static INT db_check_set_data_index_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, const void *data, INT data_size, DWORD type, const char* caller, db_err_msg** msg)
7668{
7669 /* check for write access */
7670 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7671 return DB_NO_ACCESS;
7672 }
7673
7674 if (pkey->type != type) {
7675 db_msg(msg, MERROR, caller, "\"%s\" is of type %s, not %s", db_get_path_pkey(pheader, pkey).c_str(), rpc_tid_name(pkey->type), rpc_tid_name(type));
7676 return DB_TYPE_MISMATCH;
7677 }
7678
7679 /* keys cannot contain data */
7680 if (pkey->type == TID_KEY) {
7681 db_msg(msg, MERROR, "db_set_data_index", "\"%s\" of type TID_KEY cannot contain data", db_get_path_pkey(pheader, pkey).c_str());
7682 return DB_TYPE_MISMATCH;
7683 }
7684
7685 /* check utf-8 encoding */
7686 if (pkey->type == TID_STRING || pkey->type == TID_LINK) {
7687 //printf("db_check_set_data_index for %s: utf8 check for odb \"%s\" value \"%s\"\n", caller, db_get_path_pkey(pheader, pkey).c_str(), data);
7688 const char* value = (const char*)data;
7689 if (!is_utf8(value)) {
7690 db_msg(msg, MERROR, "db_set_data_index", "\"%s\" index %d is not a valid UTF-8 Unicode string", db_get_path_pkey(pheader, pkey).c_str(), idx);
7691 // just a warning for now. K.O.
7692 //return DB_TYPE_MISMATCH;
7693 }
7694 }
7695
7696 /* check for valid idx */
7697 if (idx < 0) {
7698 db_msg(msg, MERROR, caller, "\%s\" given invalid index %d", db_get_path_pkey(pheader, pkey).c_str(), idx);
7699 return DB_INVALID_PARAM;
7700 }
7701
7702 /* check for valid array element size: if new element size
7703 is different from existing size, ODB becomes corrupted */
7704 if (pkey->item_size != 0 && data_size != pkey->item_size) {
7705 db_msg(msg, MERROR, caller, "\"%s\" invalid element data size %d, expected %d", db_get_path_pkey(pheader, pkey).c_str(), data_size, pkey->item_size);
7706 return DB_TYPE_MISMATCH;
7707 }
7708
7709 return DB_SUCCESS;
7710}
7711
7712#endif // LOCAL_ROUTINES
7713
7714/********************************************************************/
7734INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7735{
7736 if (rpc_is_remote())
7737 return rpc_call(RPC_DB_SET_DATA, hDB, hKey, data, buf_size, num_values, type);
7738
7739#ifdef LOCAL_ROUTINES
7740 {
7741 HNDLE hkeylink;
7742 int link_idx;
7743 int status;
7744
7745 if (hDB > _database_entries || hDB <= 0) {
7746 cm_msg(MERROR, "db_set_data", "invalid database handle");
7747 return DB_INVALID_HANDLE;
7748 }
7749
7750 if (!_database[hDB - 1].attached) {
7751 cm_msg(MERROR, "db_set_data", "invalid database handle");
7752 return DB_INVALID_HANDLE;
7753 }
7754
7755 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7756 cm_msg(MERROR, "db_set_data", "invalid key handle");
7757 return DB_INVALID_HANDLE;
7758 }
7759
7760 if (num_values == 0)
7761 return DB_INVALID_PARAM;
7762
7764 db_err_msg* msg = NULL;
7765
7766
7767 DATABASE* pdb = &_database[hDB - 1];
7768 DATABASE_HEADER* pheader = pdb->database_header;
7769
7770 /* check if hKey argument is correct */
7771 if (!db_validate_hkey(pheader, hKey)) {
7773 return DB_INVALID_HANDLE;
7774 }
7775
7776 KEY* pkey = (KEY *) ((char *) pheader + hKey);
7777
7778 /* check for write access */
7779 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7781 return DB_NO_ACCESS;
7782 }
7783
7784 /* check for link to array index */
7785 if (pkey->type == TID_LINK) {
7786 char link_name[256]; // FIXME: use std::string
7787 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7788 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7790 if (strchr(link_name, '[') == NULL)
7791 return DB_INVALID_LINK;
7792 link_idx = atoi(strchr(link_name, '[') + 1);
7793 *strchr(link_name, '[') = 0;
7794 if (db_find_key(hDB, 0, link_name, &hkeylink) != DB_SUCCESS)
7795 return DB_INVALID_LINK;
7796 return db_set_data_index(hDB, hkeylink, data, buf_size, link_idx, type);
7797 }
7798 }
7799
7800 status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_data", &msg);
7801
7802 if (status != DB_SUCCESS) {
7804 if (msg)
7805 db_flush_msg(&msg);
7806 return status;
7807 }
7808
7809 db_allow_write_locked(pdb, "db_set_data");
7810
7811 status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_data", &msg);
7812
7813 if (status != DB_SUCCESS) {
7815 if (msg)
7816 db_flush_msg(&msg);
7817 return status;
7818 }
7819
7820 db_notify_clients_locked(pdb, hKey, -1, TRUE, &msg);
7822 if (msg)
7823 db_flush_msg(&msg);
7824
7825
7826 }
7827#endif /* LOCAL_ROUTINES */
7828
7829 return DB_SUCCESS;
7830}
7831
7832INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7833/*
7834
7835 Same as db_set_data(), but do not notify hot-linked clients
7836
7837 */
7838{
7839 if (rpc_is_remote())
7840 return rpc_call(RPC_DB_SET_DATA1, hDB, hKey, data, buf_size, num_values, type);
7841
7842#ifdef LOCAL_ROUTINES
7843 {
7844 DATABASE_HEADER *pheader;
7845 KEY *pkey;
7846 HNDLE hkeylink;
7847 int link_idx;
7848 char link_name[256];
7849 int status;
7850
7851 if (hDB > _database_entries || hDB <= 0) {
7852 cm_msg(MERROR, "db_set_data1", "invalid database handle");
7853 return DB_INVALID_HANDLE;
7854 }
7855
7856 if (!_database[hDB - 1].attached) {
7857 cm_msg(MERROR, "db_set_data1", "invalid database handle");
7858 return DB_INVALID_HANDLE;
7859 }
7860
7861 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7862 cm_msg(MERROR, "db_set_data1", "invalid key handle");
7863 return DB_INVALID_HANDLE;
7864 }
7865
7866 if (num_values == 0)
7867 return DB_INVALID_PARAM;
7868
7870 db_err_msg* msg = NULL;
7871
7872 pheader = _database[hDB - 1].database_header;
7873
7874 /* check if hKey argument is correct */
7875 if (!db_validate_hkey(pheader, hKey)) {
7877 return DB_INVALID_HANDLE;
7878 }
7879
7880 pkey = (KEY *) ((char *) pheader + hKey);
7881
7882 /* check for write access */
7883 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7885 return DB_NO_ACCESS;
7886 }
7887
7888 /* check for link to array index */
7889 if (pkey->type == TID_LINK) {
7890 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7891 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7893 if (strchr(link_name, '[') == NULL)
7894 return DB_INVALID_LINK;
7895 link_idx = atoi(strchr(link_name, '[') + 1);
7896 *strchr(link_name, '[') = 0;
7897 if (db_find_key(hDB, 0, link_name, &hkeylink) != DB_SUCCESS)
7898 return DB_INVALID_LINK;
7899 return db_set_data_index1(hDB, hkeylink, data, buf_size, link_idx, type, FALSE);
7900 }
7901 }
7902
7903 status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_data1", &msg);
7904
7905 if (status != DB_SUCCESS) {
7907 if (msg)
7908 db_flush_msg(&msg);
7909 return status;
7910 }
7911
7912 db_allow_write_locked(&_database[hDB - 1], "db_set_data1");
7913
7914 status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_data1", &msg);
7915
7916 if (status != DB_SUCCESS) {
7918 if (msg)
7919 db_flush_msg(&msg);
7920 return status;
7921 }
7922
7924 if (msg)
7925 db_flush_msg(&msg);
7926
7927 }
7928#endif /* LOCAL_ROUTINES */
7929
7930 return DB_SUCCESS;
7931}
7932
7933/********************************************************************/
7944INT db_set_link_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7945{
7946 if (rpc_is_remote())
7947 return rpc_call(RPC_DB_SET_LINK_DATA, hDB, hKey, data, buf_size, num_values, type);
7948
7949#ifdef LOCAL_ROUTINES
7950 {
7951 if (hDB > _database_entries || hDB <= 0) {
7952 cm_msg(MERROR, "db_set_data", "invalid database handle");
7953 return DB_INVALID_HANDLE;
7954 }
7955
7956 if (!_database[hDB - 1].attached) {
7957 cm_msg(MERROR, "db_set_data", "invalid database handle");
7958 return DB_INVALID_HANDLE;
7959 }
7960
7961 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7962 cm_msg(MERROR, "db_set_data", "invalid key handle");
7963 return DB_INVALID_HANDLE;
7964 }
7965
7966 if (num_values == 0)
7967 return DB_INVALID_PARAM;
7968
7970 db_err_msg* msg = NULL;
7971
7972 DATABASE* pdb = &_database[hDB - 1];
7973 DATABASE_HEADER* pheader = pdb->database_header;
7974
7975 /* check if hKey argument is correct */
7976 if (!db_validate_hkey(pheader, hKey)) {
7978 return DB_INVALID_HANDLE;
7979 }
7980
7981 KEY* pkey = (KEY *) ((char *) pheader + hKey);
7982
7983 int status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_link_data", &msg);
7984
7985 if (status != DB_SUCCESS) {
7987 if (msg)
7988 db_flush_msg(&msg);
7989 return status;
7990 }
7991
7992 db_allow_write_locked(pdb, "db_set_link_data");
7993
7994 status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_link_data", &msg);
7995
7996 if (status != DB_SUCCESS) {
7998 if (msg)
7999 db_flush_msg(&msg);
8000 return status;
8001 }
8002
8003 db_notify_clients_locked(pdb, hKey, -1, TRUE, &msg);
8005 if (msg)
8006 db_flush_msg(&msg);
8007
8008 }
8009#endif /* LOCAL_ROUTINES */
8010
8011 return DB_SUCCESS;
8012}
8013
8015#ifndef DOXYGEN_SHOULD_SKIP_THIS
8016
8017/*------------------------------------------------------------------*/
8019/********************************************************************\
8020
8021 Routine: db_set_num_values
8022
8023 Purpose: Set number of values in a key. Extend with zeros or truncate.
8024
8025 Input:
8026 HNDLE hDB Handle to the database
8027 HNDLE hKey Handle of key
8028 INT num_values Number of data values
8029
8030 Output:
8031 none
8032
8033 Function value:
8034 DB_SUCCESS Successful completion
8035 DB_INVALID_HANDLE Database handle is invalid
8036
8037\********************************************************************/
8038{
8039 if (rpc_is_remote())
8040 return rpc_call(RPC_DB_SET_NUM_VALUES, hDB, hKey, num_values);
8041
8042#ifdef LOCAL_ROUTINES
8043 {
8044 INT new_size;
8045
8046 if (hDB > _database_entries || hDB <= 0) {
8047 cm_msg(MERROR, "db_set_num_values", "invalid database handle");
8048 return DB_INVALID_HANDLE;
8049 }
8050
8051 if (!_database[hDB - 1].attached) {
8052 cm_msg(MERROR, "db_set_num_values", "invalid database handle");
8053 return DB_INVALID_HANDLE;
8054 }
8055
8056 if (hKey < (int) sizeof(DATABASE_HEADER)) {
8057 cm_msg(MERROR, "db_set_num_values", "invalid key handle");
8058 return DB_INVALID_HANDLE;
8059 }
8060
8061 if (num_values <= 0) {
8062 cm_msg(MERROR, "db_set_num_values", "invalid num_values %d", num_values);
8063 return DB_INVALID_PARAM;
8064 }
8065
8066 if (num_values == 0)
8067 return DB_INVALID_PARAM;
8068
8070 db_err_msg* msg = NULL;
8071
8072 DATABASE* pdb = &_database[hDB - 1];
8073 DATABASE_HEADER* pheader = pdb->database_header;
8074
8075 /* check if hKey argument is correct */
8076 if (!db_validate_hkey(pheader, hKey)) {
8078 return DB_INVALID_HANDLE;
8079 }
8080
8081 KEY* pkey = (KEY *) ((char *) pheader + hKey);
8082
8083 /* check for write access */
8084 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
8086 return DB_NO_ACCESS;
8087 }
8088
8089 /* keys cannot contain data */
8090 if (pkey->type == TID_KEY) {
8092 cm_msg(MERROR, "db_set_num_values", "Key cannot contain data");
8093 return DB_TYPE_MISMATCH;
8094 }
8095
8096 if (pkey->total_size != pkey->item_size * pkey->num_values) {
8098 cm_msg(MERROR, "db_set_num_values", "Corrupted key");
8099 return DB_CORRUPTED;
8100 }
8101
8102 if (pkey->item_size == 0) {
8104 cm_msg(MERROR, "db_set_num_values", "Cannot resize array with item_size equal to zero");
8105 return DB_INVALID_PARAM;
8106 }
8107
8108 db_allow_write_locked(pdb, "db_set_num_values");
8109
8110 /* resize data size if necessary */
8111 if (pkey->num_values != num_values) {
8112 new_size = pkey->item_size * num_values;
8113
8114 pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, new_size, "db_set_num_values");
8115
8116 if (pkey->data == 0) {
8117 pkey->total_size = 0;
8118 pkey->num_values = 0;
8119 db_msg(&msg, MERROR, "db_set_num_values", "Cannot resize \"%s\" with num_values %d and new size %d bytes, online database full", db_get_path_pkey(pheader, pkey).c_str(), num_values, new_size);
8121 if (msg)
8122 db_flush_msg(&msg);
8123 return DB_FULL;
8124 }
8125
8126 pkey->data -= (POINTER_T) pheader;
8127 pkey->total_size = new_size;
8128 pkey->num_values = num_values;
8129 }
8130
8131 /* update time */
8132 pkey->last_written = ss_time();
8133
8134 db_notify_clients_locked(pdb, hKey, -1, TRUE, &msg);
8136 if (msg)
8137 db_flush_msg(&msg);
8138
8139 }
8140#endif /* LOCAL_ROUTINES */
8141
8142 return DB_SUCCESS;
8143}
8144
8146#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8147
8148/********************************************************************/
8163INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
8164{
8165 if (rpc_is_remote())
8166 return rpc_call(RPC_DB_SET_DATA_INDEX, hDB, hKey, data, data_size, idx, type);
8167
8168#ifdef LOCAL_ROUTINES
8169 {
8170 int link_idx;
8171 HNDLE hkeylink;
8172 int status;
8173
8174 if (hDB > _database_entries || hDB <= 0) {
8175 cm_msg(MERROR, "db_set_data_index", "invalid database handle");
8176 return DB_INVALID_HANDLE;
8177 }
8178
8179 if (!_database[hDB - 1].attached) {
8180 cm_msg(MERROR, "db_set_data_index", "invalid database handle");
8181 return DB_INVALID_HANDLE;
8182 }
8183
8184 if (hKey < (int) sizeof(DATABASE_HEADER)) {
8185 cm_msg(MERROR, "db_set_data_index", "invalid key handle");
8186 return DB_INVALID_HANDLE;
8187 }
8188
8190 db_err_msg* msg = NULL;
8191
8192 DATABASE* pdb = &_database[hDB - 1];
8193 DATABASE_HEADER* pheader = pdb->database_header;
8194
8195 /* check if hKey argument is correct */
8196 if (!db_validate_hkey(pheader, hKey)) {
8198 return DB_INVALID_HANDLE;
8199 }
8200
8201 KEY* pkey = (KEY *) ((char *) pheader + hKey);
8202
8203 /* check for write access */
8204 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
8206 return DB_NO_ACCESS;
8207 }
8208
8209 /* check for link to array index */
8210 if (pkey->type == TID_LINK) {
8211 char link_name[256]; // FIXME: use std::string
8212 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
8213 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
8215 if (strchr(link_name, '[') == NULL)
8216 return DB_INVALID_LINK;
8217 link_idx = atoi(strchr(link_name, '[') + 1);
8218 *strchr(link_name, '[') = 0;
8219 if (db_find_key(hDB, 0, link_name, &hkeylink) != DB_SUCCESS)
8220 return DB_INVALID_LINK;
8221 return db_set_data_index(hDB, hkeylink, data, data_size, link_idx, type);
8222 }
8223 }
8224
8225 status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_data_index", &msg);
8226
8227 if (status != DB_SUCCESS) {
8229 if (msg)
8230 db_flush_msg(&msg);
8231 return status;
8232 }
8233
8234 db_allow_write_locked(&_database[hDB-1], "db_set_data_index");
8235
8236 status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_data_index", &msg);
8237
8238 if (status != DB_SUCCESS) {
8240 if (msg)
8241 db_flush_msg(&msg);
8242 return status;
8243 }
8244
8245 db_notify_clients_locked(pdb, hKey, idx, TRUE, &msg);
8247 if (msg)
8248 db_flush_msg(&msg);
8249
8250 }
8251#endif /* LOCAL_ROUTINES */
8252
8253 return DB_SUCCESS;
8254}
8255
8256/********************************************************************/
8269{
8270 if (rpc_is_remote())
8271 return rpc_call(RPC_DB_SET_LINK_DATA_INDEX, hDB, hKey, data, data_size, idx, type);
8272
8273#ifdef LOCAL_ROUTINES
8274 {
8275 if (hDB > _database_entries || hDB <= 0) {
8276 cm_msg(MERROR, "db_set_link_data_index", "invalid database handle");
8277 return DB_INVALID_HANDLE;
8278 }
8279
8280 if (!_database[hDB - 1].attached) {
8281 cm_msg(MERROR, "db_set_link_data_index", "invalid database handle");
8282 return DB_INVALID_HANDLE;
8283 }
8284
8285 if (hKey < (int) sizeof(DATABASE_HEADER)) {
8286 cm_msg(MERROR, "db_set_link_data_index", "invalid key handle");
8287 return DB_INVALID_HANDLE;
8288 }
8289
8291 db_err_msg* msg = NULL;
8292
8293 DATABASE* pdb = &_database[hDB - 1];
8294 DATABASE_HEADER* pheader = pdb->database_header;
8295
8296 /* check if hKey argument is correct */
8297 if (!db_validate_hkey(pheader, hKey)) {
8299 return DB_INVALID_HANDLE;
8300 }
8301
8302 KEY* pkey = (KEY *) ((char *) pheader + hKey);
8303
8304 int status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_link_data_index", &msg);
8305
8306 if (status != DB_SUCCESS) {
8308 if (msg)
8309 db_flush_msg(&msg);
8310 return status;
8311 }
8312
8313 db_allow_write_locked(pdb, "db_set_link_data_index");
8314
8315 status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_link_data_index", &msg);
8316
8317 if (status != DB_SUCCESS) {
8319 if (msg)
8320 db_flush_msg(&msg);
8321 return status;
8322 }
8323
8324 db_notify_clients_locked(pdb, hKey, idx, TRUE, &msg);
8326 if (msg)
8327 db_flush_msg(&msg);
8328
8329 }
8330#endif /* LOCAL_ROUTINES */
8331
8332 return DB_SUCCESS;
8333}
8334
8336#ifndef DOXYGEN_SHOULD_SKIP_THIS
8337
8338/*------------------------------------------------------------------*/
8339INT db_set_data_index1(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type, BOOL bNotify)
8340/********************************************************************\
8341
8342 Routine: db_set_data_index1
8343
8344 Purpose: Set key data for a key which contains an array of values.
8345 Optionally notify clients which have key open.
8346
8347 Input:
8348 HNDLE hDB Handle to the database
8349 HNDLE hKey Handle of key to enumerate
8350 void *data Pointer to single value of data
8351 INT data_size Size of single data element
8352 INT idx Index of array to change [0..n-1]
8353 DWORD type Type of data
8354 BOOL bNotify If TRUE, notify clients
8355
8356 Output:
8357 none
8358
8359 Function value:
8360 DB_SUCCESS Successful completion
8361 DB_INVALID_HANDLE Database handle is invalid
8362 DB_TYPE_MISMATCH Key was created with different type
8363 DB_NO_ACCESS No write access
8364
8365\********************************************************************/
8366{
8367 if (rpc_is_remote())
8368 return rpc_call(RPC_DB_SET_DATA_INDEX1, hDB, hKey, data, data_size, idx, type, bNotify);
8369
8370#ifdef LOCAL_ROUTINES
8371 {
8372 if (hDB > _database_entries || hDB <= 0) {
8373 cm_msg(MERROR, "db_set_data_index1", "invalid database handle");
8374 return DB_INVALID_HANDLE;
8375 }
8376
8377 if (!_database[hDB - 1].attached) {
8378 cm_msg(MERROR, "db_set_data_index1", "invalid database handle");
8379 return DB_INVALID_HANDLE;
8380 }
8381
8382 if (hKey < (int) sizeof(DATABASE_HEADER)) {
8383 cm_msg(MERROR, "db_set_data_index1", "invalid key handle");
8384 return DB_INVALID_HANDLE;
8385 }
8386
8388 db_err_msg* msg = NULL;
8389
8390 DATABASE* pdb = &_database[hDB - 1];
8391 DATABASE_HEADER* pheader = pdb->database_header;
8392
8393 /* check if hKey argument is correct */
8394 if (!db_validate_hkey(pheader, hKey)) {
8396 return DB_INVALID_HANDLE;
8397 }
8398
8399 KEY* pkey = (KEY *) ((char *) pheader + hKey);
8400
8401 int status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_data_index1", &msg);
8402
8403 if (status != DB_SUCCESS) {
8405 if (msg)
8406 db_flush_msg(&msg);
8407 return status;
8408 }
8409
8410 db_allow_write_locked(pdb, "db_set_data_index1");
8411
8412 status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_data_index1", &msg);
8413
8414 if (status != DB_SUCCESS) {
8416 if (msg)
8417 db_flush_msg(&msg);
8418 return status;
8419 }
8420
8421 if (bNotify)
8422 db_notify_clients_locked(pdb, hKey, idx, TRUE, &msg);
8423
8425 if (msg)
8426 db_flush_msg(&msg);
8427
8428 }
8429#endif /* LOCAL_ROUTINES */
8430
8431 return DB_SUCCESS;
8432}
8433
8434/*----------------------------------------------------------------------------*/
8435
8436INT db_merge_data(HNDLE hDB, HNDLE hKeyRoot, const char *name, void *data, INT data_size, INT num_values, INT type)
8437/********************************************************************\
8438
8439 Routine: db_merge_data
8440
8441 Purpose: Merge an array with an ODB array. If the ODB array doesn't
8442 exist, create it and fill it with the array. If it exists,
8443 load it in the array. Adjust ODB array size if necessary.
8444
8445 Input:
8446 HNDLE hDB Handle to the database
8447 HNDLE hKeyRoot Key handle to start with, 0 for root
8448 cha *name Key name relative to hKeyRoot
8449 void *data Pointer to data array
8450 INT data_size Size of data array
8451 INT num_values Number of values in array
8452 DWORD type Type of data
8453
8454 Output:
8455 none
8456
8457 Function value:
8458 <same as db_set_data>
8459
8460\********************************************************************/
8461{
8462 HNDLE hKey;
8463 INT status, old_size;
8464
8465 if (num_values == 0)
8466 return DB_INVALID_PARAM;
8467
8468 status = db_find_key(hDB, hKeyRoot, name, &hKey);
8469 if (status != DB_SUCCESS) {
8470 db_create_key(hDB, hKeyRoot, name, type);
8471 status = db_find_key(hDB, hKeyRoot, name, &hKey);
8472 if (status != DB_SUCCESS)
8473 return status;
8474 status = db_set_data(hDB, hKey, data, data_size, num_values, type);
8475 } else {
8476 old_size = data_size;
8477 db_get_data(hDB, hKey, data, &old_size, type);
8478 status = db_set_data(hDB, hKey, data, data_size, num_values, type);
8479 }
8480
8481 return status;
8482}
8483
8484#ifdef LOCAL_ROUTINES
8485
8486/*------------------------------------------------------------------*/
8487static int db_set_mode_wlocked(DATABASE_HEADER *pheader, KEY *pkey, WORD mode, int recurse, db_err_msg** msg)
8488/********************************************************************\
8489
8490 Routine: db_set_mode_wlocked()
8491
8492 Purpose: Set access mode of key
8493
8494 Input:
8495 pheader Database
8496 pkey Key
8497 DWORD mode Access mode, any or'ed combination of
8498 MODE_READ, MODE_WRITE, MODE_EXCLUSIVE
8499 and MODE_DELETE
8500 recurse Value of 0: do not recurse subtree,
8501 value of 1: recurse subtree, becomes recurse level
8502
8503 Function value:
8504 DB_SUCCESS Successful completion
8505
8506\********************************************************************/
8507{
8508 /* resolve links */
8509 if (pkey->type == TID_LINK) {
8510 int status;
8511 pkey = (KEY*)db_resolve_link_locked(pheader, pkey, &status, msg);
8512 if (!pkey) {
8513 return status;
8514 }
8515 }
8516
8517 if (pkey->type == TID_KEY && recurse) {
8518 // drop "const" from KEY* we are permitted to write to ODB!
8519 KEY* psubkey = (KEY*)db_enum_first_locked(pheader, pkey, msg);
8520 while (psubkey) {
8521 db_set_mode_wlocked(pheader, psubkey, mode, recurse+1, msg);
8522 psubkey = (KEY*)db_enum_next_locked(pheader, pkey, psubkey, msg);
8523 }
8524 }
8525
8526 /* now set mode */
8527 pkey->access_mode = mode;
8528
8529 return DB_SUCCESS;
8530}
8531
8532#endif
8533
8534/*------------------------------------------------------------------*/
8536/********************************************************************\
8537
8538 Routine: db_set_mode
8539
8540 Purpose: Set access mode of key
8541
8542 Input:
8543 HNDLE hDB Handle to the database
8544 HNDLE hKey Key handle
8545 DWORD mode Access mode, any or'ed combination of
8546 MODE_READ, MODE_WRITE, MODE_EXCLUSIVE
8547 and MODE_DELETE
8548 BOOL recurse Value of 0 (FALSE): do not recurse subtree,
8549 value of 1 (TRUE): recurse subtree,
8550 value of 2: recurse subtree, assume database is locked by caller.
8551
8552 Output:
8553 none
8554
8555 Function value:
8556 DB_SUCCESS Successful completion
8557 DB_INVALID_HANDLE Database handle is invalid
8558
8559\********************************************************************/
8560{
8561 if (rpc_is_remote())
8562 return rpc_call(RPC_DB_SET_MODE, hDB, hKey, mode, recurse);
8563
8564#ifdef LOCAL_ROUTINES
8565 {
8566 DATABASE_HEADER *pheader;
8567 BOOL locked = FALSE;
8568
8569 if (hDB > _database_entries || hDB <= 0) {
8570 cm_msg(MERROR, "db_set_mode", "invalid database handle");
8571 return DB_INVALID_HANDLE;
8572 }
8573
8574 if (!_database[hDB - 1].attached) {
8575 cm_msg(MERROR, "db_set_mode", "invalid database handle");
8576 return DB_INVALID_HANDLE;
8577 }
8578
8579 if (recurse < 2) {
8581 locked = TRUE;
8582 }
8583
8584 pheader = _database[hDB - 1].database_header;
8585
8586 db_err_msg* msg = NULL;
8587 int status = 0;
8588
8589 KEY *pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_set_mode", &msg);
8590
8591 if (!pkey) {
8592 if (locked) {
8594 if (msg)
8595 db_flush_msg(&msg);
8596 return status;
8597 }
8598 }
8599
8600 db_allow_write_locked(&_database[hDB-1], "db_set_mode");
8601
8602 status = db_set_mode_wlocked(pheader, pkey, mode, recurse, &msg);
8603
8604 if (locked) {
8606 if (msg)
8607 db_flush_msg(&msg);
8608 }
8609
8610 return status;
8611 }
8612#endif /* LOCAL_ROUTINES */
8613
8614 return DB_SUCCESS;
8615}
8616
8618#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8619
8620/********************************************************************/
8634INT db_load(HNDLE hDB, HNDLE hKeyRoot, const char *filename, BOOL bRemote)
8635{
8636 struct stat stat_buf;
8637 INT hfile, size, n, i, status;
8638 char *buffer;
8639
8640 if (rpc_is_remote() && bRemote)
8641 return rpc_call(RPC_DB_LOAD, hDB, hKeyRoot, filename);
8642
8643 /* open file */
8644 hfile = open(filename, O_RDONLY | O_TEXT, 0644);
8645 if (hfile == -1) {
8646 cm_msg(MERROR, "db_load", "file \"%s\" not found", filename);
8647 return DB_FILE_ERROR;
8648 }
8649
8650 /* allocate buffer with file size */
8651 fstat(hfile, &stat_buf);
8652 size = stat_buf.st_size;
8653 buffer = (char *) malloc(size + 1);
8654
8655 if (buffer == NULL) {
8656 cm_msg(MERROR, "db_load", "cannot allocate ODB load buffer");
8657 close(hfile);
8658 return DB_NO_MEMORY;
8659 }
8660
8661 n = 0;
8662
8663 do {
8664 i = read(hfile, buffer + n, size - n);
8665 if (i <= 0)
8666 break;
8667 n += i;
8668 } while (TRUE);
8669
8670 buffer[n] = 0;
8671
8672 if (strncmp(buffer, "<?xml version=\"1.0\"", 19) == 0) {
8673 status = db_paste_xml(hDB, hKeyRoot, buffer);
8674 if (status != DB_SUCCESS)
8675 printf("Error in file \"%s\"\n", filename);
8676 } else if( buffer[0] == '{'){
8677 if(strrchr(buffer, '}')){
8678 status = db_paste_json(hDB, hKeyRoot, buffer);
8679 } else {
8681 }
8682 } else
8683 status = db_paste(hDB, hKeyRoot, buffer);
8684
8685 close(hfile);
8686 free(buffer);
8687
8688 return status;
8689}
8690
8691/********************************************************************/
8725INT db_copy(HNDLE hDB, HNDLE hKey, char *buffer, INT * buffer_size, const char *path)
8726{
8727 INT i, j, size, status;
8728 KEY key;
8729 HNDLE hSubkey;
8730 char *data;
8731
8732 //printf("db_copy: %d %d %p %d [%s]\n", hDB, hKey, buffer, *buffer_size, path);
8733
8734 std::string full_path = path;
8735
8736 if (full_path.length() > MAX_ODB_PATH) {
8737 cm_msg(MERROR, "db_copy", "ODB path too long at \"%s\"", path);
8738 return DB_NO_MEMORY;
8739 }
8740
8741 BOOL bWritten = FALSE;
8742
8743 /* first enumerate this level */
8744 for (i = 0;; i++) {
8746
8747 if (i == 0 && !hSubkey) {
8748 std::string line;
8749
8750 /* If key has no subkeys, just write this key */
8752 if (status != DB_SUCCESS)
8753 continue;
8754 size = key.total_size;
8755 data = (char *) malloc(size);
8756 if (data == NULL) {
8757 cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
8758 return DB_NO_MEMORY;
8759 }
8760
8761 if (key.type != TID_KEY) {
8763 if (status != DB_SUCCESS)
8764 continue;
8765 if (key.num_values == 1) {
8766 line = msprintf("%s = %s : ", key.name, rpc_tid_name(key.type));
8767
8768 if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
8769 /* multiline string */
8770 line += "[====#$@$#====]\n";
8771
8772 /* copy line to buffer */
8773 if ((INT) (line.length() + 1) > *buffer_size) {
8774 free(data);
8775 return DB_TRUNCATED;
8776 }
8777
8778 strcpy(buffer, line.c_str());
8779 buffer += line.length();
8780 *buffer_size -= line.length();
8781
8782 /* copy multiple lines to buffer */
8783 if (key.item_size > *buffer_size) {
8784 free(data);
8785 return DB_TRUNCATED;
8786 }
8787
8788 strcpy(buffer, data);
8789 buffer += strlen(data);
8790 *buffer_size -= strlen(data);
8791
8792 line = "\n====#$@$#====\n";
8793 } else {
8794 std::string str = db_sprintf(data, key.item_size, 0, key.type);
8795
8796 if (key.type == TID_STRING || key.type == TID_LINK)
8797 line += msprintf("[%d] ", key.item_size);
8798
8799 line += str + "\n";
8800 }
8801 } else {
8802 line = msprintf("%s = %s[%d] :\n", key.name, rpc_tid_name(key.type), key.num_values);
8803
8804 for (j = 0; j < key.num_values; j++) {
8805 if (key.type == TID_STRING || key.type == TID_LINK)
8806 line += msprintf("[%d] ", key.item_size);
8807 else
8808 line += msprintf("[%d] ", j);
8809
8810 std::string str = db_sprintf(data, key.item_size, j, key.type);
8811 line += str + "\n";
8812
8813 /* copy line to buffer */
8814 if ((INT) (line.length() + 1) > *buffer_size) {
8815 free(data);
8816 return DB_TRUNCATED;
8817 }
8818
8819 strcpy(buffer, line.c_str());
8820 buffer += line.length();
8821 *buffer_size -= line.length();
8822 line.clear();
8823 }
8824 }
8825 }
8826
8827 /* copy line to buffer */
8828 if ((INT) (line.length() + 1) > *buffer_size) {
8829 free(data);
8830 return DB_TRUNCATED;
8831 }
8832
8833 strcpy(buffer, line.c_str());
8834 buffer += line.length();
8835 *buffer_size -= line.length();
8836
8837 free(data);
8838 data = NULL;
8839 }
8840
8841 if (!hSubkey)
8842 break;
8843
8845 if (status != DB_SUCCESS)
8846 continue;
8847
8848 if (strcmp(key.name, "arr2") == 0)
8849 printf("\narr2\n");
8850 size = key.total_size;
8851 data = (char *) malloc(size);
8852 if (data == NULL) {
8853 cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
8854 return DB_NO_MEMORY;
8855 }
8856
8857 if (key.type == TID_KEY) {
8858 char str[MAX_ODB_PATH];
8859
8860 /* new line */
8861 if (bWritten) {
8862 if (*buffer_size < 2) {
8863 free(data);
8864 return DB_TRUNCATED;
8865 }
8866
8867 strcpy(buffer, "\n");
8868 buffer += 1;
8869 *buffer_size -= 1;
8870 }
8871
8872 strcpy(str, full_path.c_str());
8873 if (str[0] && str[strlen(str) - 1] != '/')
8874 strcat(str, "/");
8875 strcat(str, key.name);
8876
8877 /* recurse */
8878 status = db_copy(hDB, hSubkey, buffer, buffer_size, str);
8879 if (status != DB_SUCCESS) {
8880 free(data);
8881 return status;
8882 }
8883
8884 buffer += strlen(buffer);
8885 bWritten = FALSE;
8886 } else {
8887 std::string line;
8888
8890 if (status != DB_SUCCESS)
8891 continue;
8892
8893 if (!bWritten) {
8894 if (path[0] == 0)
8895 line = "[.]\n";
8896 else
8897 line = msprintf("[%s]\n", path);
8898 bWritten = TRUE;
8899 }
8900
8901 if (key.num_values == 1) {
8902 line += msprintf("%s = %s : ", key.name, rpc_tid_name(key.type));
8903
8904 if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
8905 /* multiline string */
8906 line += "[====#$@$#====]\n";
8907
8908 /* ensure string limiter */
8909 data[size - 1] = 0;
8910
8911 /* copy line to buffer */
8912 if ((INT) (line.length() + 1) > *buffer_size) {
8913 free(data);
8914 return DB_TRUNCATED;
8915 }
8916
8917 strcpy(buffer, line.c_str());
8918 buffer += line.length();
8919 *buffer_size -= line.length();
8920
8921 /* copy multiple lines to buffer */
8922 if (key.item_size > *buffer_size) {
8923 free(data);
8924 return DB_TRUNCATED;
8925 }
8926
8927 strcpy(buffer, data);
8928 buffer += strlen(data);
8929 *buffer_size -= strlen(data);
8930
8931 line = "\n====#$@$#====\n";
8932 } else {
8933 std::string str = db_sprintf(data, key.item_size, 0, key.type);
8934
8935 if (key.type == TID_STRING || key.type == TID_LINK)
8936 line += msprintf("[%d] ", key.item_size);
8937
8938 line += str + "\n";
8939 }
8940 } else {
8941 line += msprintf("%s = %s[%d] :\n", key.name, rpc_tid_name(key.type), key.num_values);
8942
8943 for (j = 0; j < key.num_values; j++) {
8944 if (key.type == TID_STRING || key.type == TID_LINK)
8945 line += msprintf("[%d] ", key.item_size);
8946 else
8947 line += msprintf("[%d] ", j);
8948
8949 std::string str = db_sprintf(data, key.item_size, j, key.type);
8950 line += str + "\n";
8951
8952 /* copy line to buffer */
8953 if ((INT) (line.length() + 1) > *buffer_size) {
8954 free(data);
8955 return DB_TRUNCATED;
8956 }
8957
8958 strcpy(buffer, line.c_str());
8959 buffer += line.length();
8960 *buffer_size -= line.length();
8961 line.clear();
8962 }
8963 }
8964
8965 /* copy line to buffer */
8966 if ((INT) (line.length() + 1) > *buffer_size) {
8967 free(data);
8968 return DB_TRUNCATED;
8969 }
8970
8971 strcpy(buffer, line.c_str());
8972 buffer += line.length();
8973 *buffer_size -= line.length();
8974 }
8975
8976 free(data);
8977 data = NULL;
8978 }
8979
8980 if (bWritten) {
8981 if (*buffer_size < 2)
8982 return DB_TRUNCATED;
8983
8984 strcpy(buffer, "\n");
8985 buffer += 1;
8986 *buffer_size -= 1;
8987 }
8988
8989 return DB_SUCCESS;
8990}
8991
8992/********************************************************************/
9000INT db_paste(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
9001{
9002 char title[MAX_STRING_LENGTH];
9003 char *data;
9004 const char *pold;
9005 INT data_size, index;
9006 INT tid, i, j, n_data, string_length, status, size;
9007 HNDLE hKey;
9008 KEY root_key;
9009
9010 title[0] = 0;
9011
9012 if (hKeyRoot == 0)
9013 db_find_key(hDB, hKeyRoot, "", &hKeyRoot);
9014
9015 db_get_key(hDB, hKeyRoot, &root_key);
9016
9017 /* initial data size */
9018 data_size = 1000;
9019 data = (char *) malloc(data_size);
9020 if (data == NULL) {
9021 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
9022 return DB_NO_MEMORY;
9023 }
9024
9025 do {
9026 char line[10*MAX_STRING_LENGTH];
9027
9028 if (*buffer == 0)
9029 break;
9030
9031 for (i = 0; *buffer != '\n' && *buffer && i < 10*MAX_STRING_LENGTH; i++)
9032 line[i] = *buffer++;
9033
9034 if (i == 10*MAX_STRING_LENGTH) {
9035 line[10*MAX_STRING_LENGTH-1] = 0;
9036 cm_msg(MERROR, "db_paste", "line too long: %s...", line);
9037 free(data);
9038 return DB_TRUNCATED;
9039 }
9040
9041 line[i] = 0;
9042 if (*buffer == '\n')
9043 buffer++;
9044
9045 /* check if it is a section title */
9046 if (line[0] == '[') {
9047 /* extract title and append '/' */
9048 mstrlcpy(title, line + 1, sizeof(title));
9049 if (strchr(title, ']'))
9050 *strchr(title, ']') = 0;
9051 if (title[0] && title[strlen(title) - 1] != '/')
9052 mstrlcat(title, "/", sizeof(title));
9053 } else {
9054 /* valid data line if it includes '=' and no ';' */
9055 if (strchr(line, '=') && line[0] != ';') {
9056 char key_name[MAX_ODB_PATH];
9057 char test_str[MAX_ODB_PATH];
9058 char data_str[10*MAX_STRING_LENGTH];
9059
9060 /* copy type info and data */
9061 char* pline = strrchr(line, '=') + 1;
9062 while (strstr(line, ": [") != NULL && strstr(line, ": [") < pline) {
9063 pline -= 2;
9064 while (*pline != '=' && pline > line)
9065 pline--;
9066 pline++;
9067 }
9068 while (*pline == ' ')
9069 pline++;
9070 mstrlcpy(data_str, pline, sizeof(data_str));
9071
9072 /* extract key name */
9073 *strrchr(line, '=') = 0;
9074 while (strstr(line, ": [") && strchr(line, '='))
9075 *strrchr(line, '=') = 0;
9076
9077 pline = &line[strlen(line) - 1];
9078 while (*pline == ' ')
9079 *pline-- = 0;
9080
9081 key_name[0] = 0;
9082 if (title[0] != '.')
9083 mstrlcpy(key_name, title, sizeof(key_name));
9084
9085 mstrlcat(key_name, line, sizeof(key_name));
9086
9087 /* evaluate type info */
9088 mstrlcpy(line, data_str, sizeof(line));
9089 if (strchr(line, ' '))
9090 *strchr(line, ' ') = 0;
9091
9092 n_data = 1;
9093 if (strchr(line, '[')) {
9094 n_data = atol(strchr(line, '[') + 1);
9095 *strchr(line, '[') = 0;
9096 }
9097
9098 for (tid = 0; tid < TID_LAST; tid++)
9099 if (strcmp(rpc_tid_name(tid), line) == 0)
9100 break;
9101 if (tid == TID_LAST) {
9102 for (tid = 0; tid < TID_LAST; tid++)
9103 if (strcmp(rpc_tid_name_old(tid), line) == 0)
9104 break;
9105 }
9106
9107 string_length = 0;
9108
9109 if (tid == TID_LAST)
9110 cm_msg(MERROR, "db_paste", "found unknown data type \"%s\" in ODB file", line);
9111 else {
9112 /* skip type info */
9113 char* pc = data_str;
9114 while (*pc != ' ' && *pc)
9115 pc++;
9116 while ((*pc == ' ' || *pc == ':') && *pc)
9117 pc++;
9118
9119 //mstrlcpy(data_str, pc, sizeof(data_str)); // MacOS 10.9 does not permit mstrlcpy() of overlapping strings
9120 assert(strlen(pc) < sizeof(data_str)); // "pc" points at a substring inside "data_str"
9121 memmove(data_str, pc, strlen(pc)+1);
9122
9123 if (n_data > 1) {
9124 data_str[0] = 0;
9125 if (!*buffer)
9126 break;
9127
9128 for (j = 0; *buffer != '\n' && *buffer; j++)
9129 data_str[j] = *buffer++;
9130 data_str[j] = 0;
9131 if (*buffer == '\n')
9132 buffer++;
9133 }
9134
9135 for (i = 0; i < n_data; i++) {
9136 /* strip trailing \n */
9137 char* pc = &data_str[strlen(data_str) - 1];
9138 while (*pc == '\n' || *pc == '\r')
9139 *pc-- = 0;
9140
9141 if (tid == TID_STRING || tid == TID_LINK) {
9142 if (!string_length) {
9143 if (data_str[1] == '=')
9144 string_length = -1;
9145 else
9146 string_length = atoi(data_str + 1);
9147 if (string_length > MAX_STRING_LENGTH) {
9148 string_length = MAX_STRING_LENGTH;
9149 cm_msg(MERROR, "db_paste", "found string exceeding MAX_STRING_LENGTH, odb path \"%s\"", key_name);
9150 }
9151 if (string_length == 0) {
9152 string_length = 32;
9153 cm_msg(MERROR, "db_paste", "found string length of zero, set to 32, odb path \"%s\"", key_name);
9154 }
9155 }
9156
9157 if (string_length == -1) {
9158 /* multi-line string */
9159 if (strstr(buffer, "\n====#$@$#====\n") != NULL) {
9160 string_length = (POINTER_T) strstr(buffer, "\n====#$@$#====\n") - (POINTER_T) buffer + 1;
9161
9162 if (string_length >= data_size) {
9163 data_size += string_length + 100;
9164 data = (char *) realloc(data, data_size);
9165 if (data == NULL) {
9166 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
9167 return DB_NO_MEMORY;
9168 }
9169 }
9170
9171 memset(data, 0, data_size);
9172 strncpy(data, buffer, string_length);
9173 data[string_length - 1] = 0;
9174 buffer = strstr(buffer, "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
9175 } else
9176 cm_msg(MERROR, "db_paste", "found multi-line string without termination sequence");
9177 } else {
9178 char* pc = data_str + 2;
9179 while (*pc && *pc != ' ')
9180 pc++;
9181
9182 // skip one space (needed for strings starting with spaces)
9183 if (*pc)
9184 pc++;
9185
9186 /* limit string size */
9187 *(pc + string_length - 1) = 0;
9188
9189 /* increase data buffer if necessary */
9190 if (string_length * (i + 1) >= data_size) {
9191 data_size += 1000;
9192 data = (char *) realloc(data, data_size);
9193 if (data == NULL) {
9194 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
9195 return DB_NO_MEMORY;
9196 }
9197 }
9198
9199 mstrlcpy(data + string_length * i, pc, string_length);
9200 }
9201 } else {
9202 char* pc = data_str;
9203
9204 if (n_data > 1 && data_str[0] == '[') {
9205 index = atoi(data_str+1);
9206 pc = strchr(data_str, ']') + 1;
9207 while (*pc && *pc == ' ')
9208 pc++;
9209 } else
9210 index = 0;
9211
9212 /* increase data buffer if necessary */
9213 if (rpc_tid_size(tid) * (index + 1) >= data_size) {
9214 data_size += 1000;
9215 data = (char *) realloc(data, data_size);
9216 if (data == NULL) {
9217 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
9218 return DB_NO_MEMORY;
9219 }
9220 }
9221
9222 db_sscanf(pc, data, &size, index, tid);
9223 }
9224
9225 if (i < n_data - 1) {
9226 data_str[0] = 0;
9227 if (!*buffer)
9228 break;
9229
9230 pold = buffer;
9231
9232 for (j = 0; *buffer != '\n' && *buffer; j++)
9233 data_str[j] = *buffer++;
9234 data_str[j] = 0;
9235 if (*buffer == '\n')
9236 buffer++;
9237
9238 /* test if valid data */
9239 if (tid != TID_STRING && tid != TID_LINK) {
9240 if (data_str[0] == 0 || (strchr(data_str, '=')
9241 && strchr(data_str, ':')))
9242 buffer = pold;
9243 }
9244 }
9245 }
9246
9247 /* skip system client entries */
9248 mstrlcpy(test_str, key_name, sizeof(test_str));
9249 test_str[15] = 0;
9250
9251 if (!equal_ustring(test_str, "/System/Clients")) {
9252 if (root_key.type != TID_KEY) {
9253 /* root key is destination key */
9254 hKey = hKeyRoot;
9255 } else {
9256 /* create key and set value */
9257 if (key_name[0] == '/') {
9258 status = db_find_link(hDB, 0, key_name, &hKey);
9259 if (status == DB_NO_KEY) {
9260 db_create_key(hDB, 0, key_name, tid);
9261 status = db_find_link(hDB, 0, key_name, &hKey);
9262 }
9263 } else {
9264 status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
9265 if (status == DB_NO_KEY) {
9266 db_create_key(hDB, hKeyRoot, key_name, tid);
9267 status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
9268 }
9269 }
9270 }
9271
9272 /* set key data if created successfully */
9273 if (hKey) {
9274 if (tid == TID_STRING || tid == TID_LINK)
9275 db_set_link_data(hDB, hKey, data, string_length * n_data, n_data, tid);
9276 else
9277 db_set_link_data(hDB, hKey, data, rpc_tid_size(tid) * n_data, n_data, tid);
9278 }
9279 }
9280 }
9281 }
9282 }
9283 } while (TRUE);
9284
9285 free(data);
9286 return DB_SUCCESS;
9287}
9288
9289/********************************************************************/
9290/*
9291 Only internally used by db_paste_xml
9292*/
9293static int db_paste_node(HNDLE hDB, HNDLE hKeyRoot, PMXML_NODE node)
9294{
9295 int status;
9296
9297 if (strcmp(mxml_get_name(node), "odb") == 0) {
9298 for (int i = 0; i < mxml_get_number_of_children(node); i++) {
9299 status = db_paste_node(hDB, hKeyRoot, mxml_subnode(node, i));
9300 if (status != DB_SUCCESS)
9301 return status;
9302 }
9303 } else if (strcmp(mxml_get_name(node), "dir") == 0) {
9304 const char* name = mxml_get_attribute(node, "name");
9305
9306 if (name == NULL) {
9307 cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no name in XML data", mxml_get_name(node));
9308 return DB_TYPE_MISMATCH;
9309 }
9310
9311 HNDLE hKey;
9312 status = db_find_link(hDB, hKeyRoot, name, &hKey);
9313
9314 if (status == DB_NO_KEY) {
9315 status = db_create_key(hDB, hKeyRoot, name, TID_KEY);
9316 if (status == DB_NO_ACCESS) {
9317 cm_msg(MINFO, "db_paste_node", "cannot load key \"%s\": write protected", name);
9318 return DB_SUCCESS; /* key or tree is locked, just skip it */
9319 }
9320
9321 if (status != DB_SUCCESS && status != DB_KEY_EXIST) {
9322 cm_msg(MERROR, "db_paste_node", "cannot create key \"%s\" in ODB, status = %d", name, status);
9323 return status;
9324 }
9325 status = db_find_link(hDB, hKeyRoot, name, &hKey);
9326 if (status != DB_SUCCESS) {
9327 cm_msg(MERROR, "db_paste_node", "cannot find key \"%s\" in ODB", name);
9328 return status;
9329 }
9330 }
9331
9332 std::string path = db_get_path(hDB, hKey);
9333 if (!equal_ustring(path.c_str(), "/System/Clients")) {
9334 for (int i = 0; i < mxml_get_number_of_children(node); i++) {
9335 status = db_paste_node(hDB, hKey, mxml_subnode(node, i));
9336 if (status != DB_SUCCESS)
9337 return status;
9338 }
9339 }
9340 } else if (strcmp(mxml_get_name(node), "key") == 0 || strcmp(mxml_get_name(node), "keyarray") == 0) {
9341
9342 const char* name = mxml_get_attribute(node, "name");
9343
9344 if (name == NULL) {
9345 cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no name in XML data", mxml_get_name(node));
9346 return DB_TYPE_MISMATCH;
9347 }
9348
9349 int num_values;
9350 if (strcmp(mxml_get_name(node), "keyarray") == 0)
9351 num_values = atoi(mxml_get_attribute(node, "num_values"));
9352 else
9353 num_values = 0;
9354
9355 const char* type = mxml_get_attribute(node, "type");
9356
9357 if (type == NULL) {
9358 cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no type in XML data", mxml_get_name(node));
9359 return DB_TYPE_MISMATCH;
9360 }
9361
9362 int tid = rpc_name_tid(type);
9363 if (tid == 0) {
9364 cm_msg(MERROR, "db_paste_node", "found unknown data type \"%s\" in XML data", type);
9365 return DB_TYPE_MISMATCH;
9366 }
9367
9368 HNDLE hKey;
9369 status = db_find_link(hDB, hKeyRoot, name, &hKey);
9370 if (status == DB_NO_KEY) {
9371 status = db_create_key(hDB, hKeyRoot, name, tid);
9372 if (status == DB_NO_ACCESS) {
9373 cm_msg(MINFO, "db_paste_node", "cannot load key \"%s\": write protected", name);
9374 return DB_SUCCESS; /* key or tree is locked, just skip it */
9375 }
9376
9377 if (status != DB_SUCCESS) {
9378 cm_msg(MERROR, "db_paste_node", "cannot create key \"%s\" in ODB, status = %d", name, status);
9379 return status;
9380 }
9381 status = db_find_link(hDB, hKeyRoot, name, &hKey);
9382 if (status != DB_SUCCESS) {
9383 cm_msg(MERROR, "db_paste_node", "cannot find key \"%s\" in ODB, status = %d", name, status);
9384 return status;
9385 }
9386 } else {
9387 KEY key;
9389 if (status != DB_SUCCESS) {
9390 cm_msg(MERROR, "db_paste_node", "cannot get key \"%s\" in ODB, status = %d", name, status);
9391 return status;
9392 }
9393
9394 if (num_values > 0 && num_values != key.num_values && key.item_size > 0) {
9395 status = db_set_num_values(hDB, hKey, num_values);
9396 if (status != DB_SUCCESS) {
9397 cm_msg(MERROR, "db_paste_node", "cannot resize key \"%s\" in ODB, status = %d", name, status);
9398 return status;
9399 }
9400 }
9401 }
9402
9403 int size = 0;
9404 char *buf = NULL;
9405
9406 if (tid == TID_STRING || tid == TID_LINK) {
9407 size = atoi(mxml_get_attribute(node, "size"));
9408 buf = (char *)malloc(size);
9409 assert(buf);
9410 buf[0] = 0;
9411 }
9412
9413 if (num_values) {
9414 /* evaluate array */
9415 for (int i = 0; i < mxml_get_number_of_children(node); i++) {
9416 PMXML_NODE child = mxml_subnode(node, i);
9417 int idx;
9418 if (mxml_get_attribute(child, "index"))
9419 idx = atoi(mxml_get_attribute(child, "index"));
9420 else
9421 idx = i;
9422 if (tid == TID_STRING || tid == TID_LINK) {
9423 if (mxml_get_value(child) == NULL) {
9424 status = db_set_data_index(hDB, hKey, "", size, i, tid);
9425 if (status == DB_NO_ACCESS) {
9426 cm_msg(MINFO, "db_paste_node", "cannot load string or link \"%s\": write protected", mxml_get_attribute(node, "name"));
9427 return DB_SUCCESS; /* key or tree is locked, just skip it */
9428 } else if (status != DB_SUCCESS) {
9429 cm_msg(MERROR, "db_paste_node", "cannot load string or link \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
9430 return status;
9431 }
9432 } else {
9433 mstrlcpy(buf, mxml_get_value(child), size);
9434 status = db_set_data_index(hDB, hKey, buf, size, idx, tid);
9435 if (status == DB_NO_ACCESS) {
9436 cm_msg(MINFO, "db_paste_node", "cannot load array element \"%s\": write protected", mxml_get_attribute(node, "name"));
9437 return DB_SUCCESS; /* key or tree is locked, just skip it */
9438 } else if (status != DB_SUCCESS) {
9439 cm_msg(MERROR, "db_paste_node", "cannot load array element \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
9440 return status;
9441 }
9442 }
9443 } else {
9444 char data[256];
9445 db_sscanf(mxml_get_value(child), data, &size, 0, tid);
9446 status = db_set_data_index(hDB, hKey, data, rpc_tid_size(tid), idx, tid);
9447 if (status == DB_NO_ACCESS) {
9448 cm_msg(MINFO, "db_paste_node", "cannot load array element \"%s\": write protected", mxml_get_attribute(node, "name"));
9449 return DB_SUCCESS; /* key or tree is locked, just skip it */
9450 } else if (status != DB_SUCCESS) {
9451 cm_msg(MERROR, "db_paste_node", "cannot load array element \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
9452 return status;
9453 }
9454 }
9455 }
9456
9457 } else { /* single value */
9458 if (tid == TID_STRING || tid == TID_LINK) {
9459 size = atoi(mxml_get_attribute(node, "size"));
9460 if (mxml_get_value(node) == NULL) {
9461 status = db_set_data(hDB, hKey, "", size, 1, tid);
9462 if (status == DB_NO_ACCESS) {
9463 cm_msg(MINFO, "db_paste_node", "cannot load string or link \"%s\": write protected", mxml_get_attribute(node, "name"));
9464 return DB_SUCCESS; /* key or tree is locked, just skip it */
9465 } else if (status != DB_SUCCESS) {
9466 cm_msg(MERROR, "db_paste_node", "cannot load string or link \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
9467 return status;
9468 }
9469 } else {
9470 mstrlcpy(buf, mxml_get_value(node), size);
9471 status = db_set_data(hDB, hKey, buf, size, 1, tid);
9472 if (status == DB_NO_ACCESS) {
9473 cm_msg(MINFO, "db_paste_node", "cannot load value \"%s\": write protected", mxml_get_attribute(node, "name"));
9474 return DB_SUCCESS; /* key or tree is locked, just skip it */
9475 } else if (status != DB_SUCCESS) {
9476 cm_msg(MERROR, "db_paste_node", "cannot load value \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
9477 return status;
9478 }
9479 }
9480 } else {
9481 char data[256];
9482 db_sscanf(mxml_get_value(node), data, &size, 0, tid);
9483 status = db_set_data(hDB, hKey, data, rpc_tid_size(tid), 1, tid);
9484 if (status == DB_NO_ACCESS) {
9485 cm_msg(MINFO, "db_paste_node", "cannot load value \"%s\": write protected", mxml_get_attribute(node, "name"));
9486 return DB_SUCCESS; /* key or tree is locked, just skip it */
9487 } else if (status != DB_SUCCESS) {
9488 cm_msg(MERROR, "db_paste_node", "cannot load value \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
9489 return status;
9490 }
9491 }
9492 }
9493
9494 if (buf) {
9495 free(buf);
9496 buf = NULL;
9497 }
9498 }
9499
9500 return DB_SUCCESS;
9501}
9502
9503/********************************************************************/
9511INT db_paste_xml(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
9512{
9513 char error[256];
9514 INT status;
9515 PMXML_NODE tree, node;
9516
9517 if (hKeyRoot == 0)
9518 db_find_key(hDB, hKeyRoot, "", &hKeyRoot);
9519
9520 /* parse XML buffer */
9521 tree = mxml_parse_buffer(buffer, error, sizeof(error), NULL);
9522 if (tree == NULL) {
9523 puts(error);
9524 return DB_TYPE_MISMATCH;
9525 }
9526
9527 node = mxml_find_node(tree, "odb");
9528 if (node == NULL) {
9529 puts("Cannot find element \"odb\" in XML data");
9530 return DB_TYPE_MISMATCH;
9531 }
9532
9533 status = db_paste_node(hDB, hKeyRoot, node);
9534
9535 mxml_free_tree(tree);
9536
9537 return status;
9538}
9539
9540/********************************************************************/
9550INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, int *buffer_size, bool header)
9551{
9552
9553 if (rpc_is_remote())
9554 return rpc_call(RPC_DB_COPY_XML, hDB, hKey, buffer, buffer_size, header);
9555
9556#ifdef LOCAL_ROUTINES
9557 {
9558 INT len;
9559 char *p;
9560 MXML_WRITER *writer;
9561
9562 /* open file */
9563 writer = mxml_open_buffer();
9564 if (writer == NULL) {
9565 cm_msg(MERROR, "db_copy_xml", "Cannot allocate buffer");
9566 return DB_NO_MEMORY;
9567 }
9568
9569 if (header) {
9570 std::string path = db_get_path(hDB, hKey);
9571
9572 /* write XML header */
9573 mxml_start_element(writer, "odb");
9574 mxml_write_attribute(writer, "root", path.c_str());
9575 mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
9576 mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", "http://midas.psi.ch/odb.xsd");
9577
9578 db_save_xml_key(hDB, hKey, 0, writer);
9579 mxml_end_element(writer); // "odb"
9580 } else {
9581 KEY key;
9582 int status = db_get_key(hDB, hKey, &key);
9583 if (status != DB_SUCCESS)
9584 return status;
9585
9586 db_save_xml_key(hDB, hKey, 1, writer);
9587 }
9588
9589 p = mxml_close_buffer(writer);
9590 len = strlen(p) + 1;
9591 if (len > *buffer_size) {
9592 free(p);
9593 *buffer_size = 0;
9594 return DB_TRUNCATED;
9595 }
9596
9597 mstrlcpy(buffer, p, len);
9598 free(p);
9599 *buffer_size = len;
9600 }
9601#endif /* LOCAL_ROUTINES */
9602
9603 return DB_SUCCESS;
9604}
9605
9607#ifndef DOXYGEN_SHOULD_SKIP_THIS
9608
9609/*------------------------------------------------------------------*/
9610void name2c(char *str)
9611/********************************************************************\
9612
9613 Routine: name2c
9614
9615 Purpose: Convert key name to C name. Internal use only.
9616
9617\********************************************************************/
9618{
9619 if (*str >= '0' && *str <= '9')
9620 *str = '_';
9621
9622 while (*str) {
9623 if (!(*str >= 'a' && *str <= 'z') && !(*str >= 'A' && *str <= 'Z') && !(*str >= '0' && *str <= '9'))
9624 *str = '_';
9625 *str = (char) tolower(*str);
9626 str++;
9627 }
9628}
9629
9630/*------------------------------------------------------------------*/
9631static void db_save_tree_struct(HNDLE hDB, HNDLE hKey, int hfile, INT level)
9632/********************************************************************\
9633
9634 Routine: db_save_tree_struct
9635
9636 Purpose: Save database tree as a C structure. Gets called by
9637 db_save_struct(). Internal use only.
9638
9639\********************************************************************/
9640{
9641 INT i, idx;
9642 KEY key;
9643 HNDLE hSubkey;
9644 int wr;
9645
9646 /* first enumerate this level */
9647 for (idx = 0;; idx++) {
9648 char name[MAX_ODB_PATH];
9649
9650 db_enum_link(hDB, hKey, idx, &hSubkey);
9651 if (!hSubkey)
9652 break;
9653
9654 /* first get the name of the link, than the type of the link target */
9656 mstrlcpy(name, key.name, sizeof(name));
9657 db_enum_key(hDB, hKey, idx, &hSubkey);
9658
9660
9661 if (key.type != TID_KEY) {
9662 char line[MAX_ODB_PATH];
9663 char str[MAX_ODB_PATH];
9664
9665 for (i = 0; i <= level; i++) {
9666 wr = write(hfile, " ", 2);
9667 assert(wr == 2);
9668 }
9669
9670 switch (key.type) {
9671 case TID_INT8:
9672 case TID_CHAR:
9673 strcpy(line, "char");
9674 break;
9675 case TID_INT16:
9676 strcpy(line, "short");
9677 break;
9678 case TID_FLOAT:
9679 strcpy(line, "float");
9680 break;
9681 case TID_DOUBLE:
9682 strcpy(line, "double");
9683 break;
9684 case TID_BITFIELD:
9685 strcpy(line, "unsigned char");
9686 break;
9687 case TID_STRING:
9688 strcpy(line, "char");
9689 break;
9690 case TID_LINK:
9691 strcpy(line, "char");
9692 break;
9693 default:
9694 strcpy(line, rpc_tid_name(key.type));
9695 break;
9696 }
9697
9698 mstrlcat(line, " ", sizeof(line));
9699 mstrlcpy(str, name, sizeof(str));
9700 name2c(str);
9701
9702 if (key.num_values > 1)
9703 sprintf(str + strlen(str), "[%d]", key.num_values);
9704 if (key.type == TID_STRING || key.type == TID_LINK)
9705 sprintf(str + strlen(str), "[%d]", key.item_size);
9706
9707 mstrlcpy(line + 10, str, sizeof(line) - 10);
9708 mstrlcat(line, ";\n", sizeof(line));
9709
9710 wr = write(hfile, line, strlen(line));
9711 assert(wr > 0);
9712 } else {
9713 char line[10+MAX_ODB_PATH];
9714 char str[MAX_ODB_PATH];
9715
9716 /* recurse subtree */
9717 for (i = 0; i <= level; i++) {
9718 wr = write(hfile, " ", 2);
9719 assert(wr == 2);
9720 }
9721
9722 sprintf(line, "struct {\n");
9723 wr = write(hfile, line, strlen(line));
9724 assert(wr > 0);
9725 db_save_tree_struct(hDB, hSubkey, hfile, level + 1);
9726
9727 for (i = 0; i <= level; i++) {
9728 wr = write(hfile, " ", 2);
9729 assert(wr == 2);
9730 }
9731
9732 strcpy(str, name);
9733 name2c(str);
9734
9735 sprintf(line, "} %s;\n", str);
9736 wr = write(hfile, line, strlen(line));
9737 assert(wr > 0);
9738 }
9739 }
9740}
9741
9743#endif /* DOXYGEN_SHOULD_SKIP_THIS */
9744
9745/********************************************************************/
9758INT db_save(HNDLE hDB, HNDLE hKey, const char *filename, BOOL bRemote)
9759{
9760 if (rpc_is_remote() && bRemote)
9761 return rpc_call(RPC_DB_SAVE, hDB, hKey, filename, bRemote);
9762
9763#ifdef LOCAL_ROUTINES
9764 {
9765 INT hfile, size, buffer_size, n, status;
9766 char *buffer;
9767
9768 /* open file */
9769 hfile = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0644);
9770 if (hfile == -1) {
9771 cm_msg(MERROR, "db_save", "Cannot open file \"%s\"", filename);
9772 return DB_FILE_ERROR;
9773 }
9774
9775 std::string path = db_get_path(hDB, hKey);
9776
9777 buffer_size = 10000;
9778 do {
9779 buffer = (char *) malloc(buffer_size);
9780 if (buffer == NULL) {
9781 cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
9782 break;
9783 }
9784
9785 size = buffer_size;
9786 status = db_copy(hDB, hKey, buffer, &size, path.c_str());
9787 if (status != DB_TRUNCATED) {
9788 n = write(hfile, buffer, buffer_size - size);
9789 free(buffer);
9790 buffer = NULL;
9791
9792 if (n != buffer_size - size) {
9793 cm_msg(MERROR, "db_save", "cannot save .ODB file");
9794 close(hfile);
9795 return DB_FILE_ERROR;
9796 }
9797 break;
9798 }
9799
9800 /* increase buffer size if truncated */
9801 free(buffer);
9802 buffer = NULL;
9803 buffer_size *= 2;
9804 } while (1);
9805
9806 close(hfile);
9807
9808 }
9809#endif /* LOCAL_ROUTINES */
9810
9811 return DB_SUCCESS;
9812}
9813
9814/*------------------------------------------------------------------*/
9815
9816void xml_encode(char *src, int size)
9817{
9818 int i;
9819 char *dst, *p;
9820
9821 dst = (char *) malloc(size);
9822 if (dst == NULL)
9823 return;
9824
9825 *dst = 0;
9826 for (i = 0; i < (int) strlen(src); i++) {
9827 switch (src[i]) {
9828 case '<':
9829 mstrlcat(dst, "&lt;", size);
9830 break;
9831 case '>':
9832 mstrlcat(dst, "&gt;", size);
9833 break;
9834 case '&':
9835 mstrlcat(dst, "&amp;", size);
9836 break;
9837 case '\"':
9838 mstrlcat(dst, "&quot;", size);
9839 break;
9840 case '\'':
9841 mstrlcat(dst, "&apos;", size);
9842 break;
9843 default:
9844 if ((int) strlen(dst) >= size) {
9845 free(dst);
9846 return;
9847 }
9848 p = dst + strlen(dst);
9849 *p = src[i];
9850 *(p + 1) = 0;
9851 }
9852 }
9853
9854 mstrlcpy(src, dst, size);
9855}
9856
9857/*------------------------------------------------------------------*/
9858
9859INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER * writer)
9860{
9861 INT i, idx, size, status;
9862 char *data;
9863 HNDLE hSubkey;
9864 KEY key;
9865
9866 if (level > MAX_ODB_PATH) {
9867 cm_msg(MERROR, "db_save_xml_key", "ODB path too long at \"%s\"", db_get_path(hDB, hKey).c_str());
9868 return DB_NO_MEMORY;
9869 }
9870
9872 if (status != DB_SUCCESS)
9873 return status;
9874
9875 if (key.type == TID_KEY) {
9876
9877 /* save opening tag for subtree */
9878
9879 if (level > 0) {
9880 mxml_start_element(writer, "dir");
9881 mxml_write_attribute(writer, "name", key.name);
9882 mxml_write_attribute(writer, "handle", std::to_string(hKey).c_str());
9883 }
9884
9885 for (idx = 0;; idx++) {
9886 db_enum_link(hDB, hKey, idx, &hSubkey);
9887
9888 if (!hSubkey)
9889 break;
9890
9891 /* save subtree */
9892 status = db_save_xml_key(hDB, hSubkey, level + 1, writer);
9893 if (status != DB_SUCCESS)
9894 return status;
9895 }
9896
9897 /* save closing tag for subtree */
9898 if (level > 0)
9899 mxml_end_element(writer);
9900
9901 } else {
9902 /* save key value */
9903
9904 if (key.num_values > 1)
9905 mxml_start_element(writer, "keyarray");
9906 else
9907 mxml_start_element(writer, "key");
9908 mxml_write_attribute(writer, "name", key.name);
9909 mxml_write_attribute(writer, "type", rpc_tid_name(key.type));
9910 mxml_write_attribute(writer, "handle", std::to_string(hKey).c_str());
9911
9912 if (key.type == TID_STRING || key.type == TID_LINK) {
9913 char str[256];
9914 sprintf(str, "%d", key.item_size);
9915 mxml_write_attribute(writer, "size", str);
9916 }
9917
9918 if (key.num_values > 1) {
9919 char str[256];
9920 sprintf(str, "%d", key.num_values);
9921 mxml_write_attribute(writer, "num_values", str);
9922 }
9923
9924 size = key.total_size;
9925 data = (char *) malloc(size+1); // an extra byte to zero-terminate strings
9926 if (data == NULL) {
9927 cm_msg(MERROR, "db_save_xml_key", "cannot allocate data buffer");
9928 return DB_NO_MEMORY;
9929 }
9930
9931 db_get_link_data(hDB, hKey, data, &size, key.type);
9932
9933 if (key.num_values == 1) {
9934 if (key.type == TID_STRING) {
9935 data[size] = 0; // make sure strings are NUL-terminated
9936 mxml_write_value(writer, data);
9937 } else {
9938 std::string str = db_sprintf(data, key.item_size, 0, key.type);
9939 if (key.type == TID_STRING && strlen(data) >= MAX_STRING_LENGTH) {
9940 std::string path = db_get_path(hDB, hKey);
9941 cm_msg(MERROR, "db_save_xml_key", "Long odb string probably truncated, odb path \"%s\", string length %d truncated to %d", path.c_str(), (int)strlen(data), (int)str.length());
9942 }
9943 mxml_write_value(writer, str.c_str());
9944 }
9945 mxml_end_element(writer);
9946
9947 } else { /* array of values */
9948
9949 for (i = 0; i < key.num_values; i++) {
9950
9951 mxml_start_element(writer, "value");
9952
9953 {
9954 char str[256];
9955 sprintf(str, "%d", i);
9956 mxml_write_attribute(writer, "index", str);
9957 }
9958
9959 if (key.type == TID_STRING) {
9960 char* p = data + i * key.item_size;
9961 p[key.item_size - 1] = 0; // make sure string is NUL-terminated
9962 //cm_msg(MINFO, "db_save_xml_key", "odb string array item_size %d, index %d length %d", key.item_size, i, (int)strlen(p));
9963 mxml_write_value(writer, p);
9964 } else {
9965 std::string str = db_sprintf(data, key.item_size, i, key.type);
9966 if ((key.type == TID_STRING) && (str.length() >= MAX_STRING_LENGTH-1)) {
9967 std::string path = db_get_path(hDB, hKey);
9968 cm_msg(MERROR, "db_save_xml_key", "Long odb string array probably truncated, odb path \"%s\"[%d]", path.c_str(), i);
9969 }
9970 mxml_write_value(writer, str.c_str());
9971 }
9972
9973 mxml_end_element(writer);
9974 }
9975
9976 mxml_end_element(writer); /* keyarray */
9977 }
9978
9979 free(data);
9980 data = NULL;
9981 }
9982
9983 return DB_SUCCESS;
9984}
9985
9986/********************************************************************/
9998INT db_save_xml(HNDLE hDB, HNDLE hKey, const char *filename)
9999{
10000#ifdef LOCAL_ROUTINES
10001 {
10002 INT status;
10003 MXML_WRITER *writer;
10004
10005 /* open file */
10006 writer = mxml_open_file(filename);
10007 if (writer == NULL) {
10008 cm_msg(MERROR, "db_save_xml", "Cannot open file \"%s\"", filename);
10009 return DB_FILE_ERROR;
10010 }
10011
10012 std::string path = db_get_path(hDB, hKey);
10013
10014 /* write XML header */
10015 mxml_start_element(writer, "odb");
10016 mxml_write_attribute(writer, "root", path.c_str());
10017 mxml_write_attribute(writer, "filename", filename);
10018 mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
10019
10020 std::string xsd_path;
10021
10022 if (getenv("MIDASSYS"))
10023 xsd_path = getenv("MIDASSYS");
10024 else
10025 xsd_path = ".";
10026
10027 xsd_path += DIR_SEPARATOR_STR;
10028 xsd_path += "odb.xsd";
10029 mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", xsd_path.c_str());
10030
10031 status = db_save_xml_key(hDB, hKey, 0, writer);
10032
10033 mxml_end_element(writer); // "odb"
10034 mxml_close_file(writer);
10035
10036 return status;
10037 }
10038#endif /* LOCAL_ROUTINES */
10039
10040 return DB_SUCCESS;
10041}
10042
10043/*------------------------------------------------------------------*/
10044
10045void json_write(char **buffer, int* buffer_size, int* buffer_end, int level, const char* s, int quoted)
10046{
10047 int len, remain, xlevel;
10048
10049 len = strlen(s);
10050 remain = *buffer_size - *buffer_end;
10051 assert(remain >= 0);
10052
10053 xlevel = 2*level;
10054
10055 while (10 + xlevel + 3*len > remain) {
10056 // reallocate the buffer
10057 int new_buffer_size = 2*(*buffer_size);
10058 if (new_buffer_size < 4*1024)
10059 new_buffer_size = 4*1024;
10060 //printf("reallocate: len %d, size %d, remain %d, allocate %d\n", len, *buffer_size, remain, new_buffer_size);
10061 assert(new_buffer_size > *buffer_size);
10062 *buffer = (char *)realloc(*buffer, new_buffer_size);
10063 assert(*buffer);
10064 *buffer_size = new_buffer_size;
10065 remain = *buffer_size - *buffer_end;
10066 assert(remain >= 0);
10067 }
10068
10069 if (xlevel) {
10070 int i;
10071 for (i=0; i<xlevel; i++)
10072 (*buffer)[(*buffer_end)++] = ' ';
10073 }
10074
10075 if (!quoted) {
10076 memcpy(*buffer + *buffer_end, s, len);
10077 *buffer_end += len;
10078 (*buffer)[*buffer_end] = 0; // NUL-terminate the buffer
10079 return;
10080 }
10081
10082 char *bufptr = *buffer;
10083 int bufend = *buffer_end;
10084
10085 bufptr[bufend++] = '"';
10086
10087 while (*s) {
10088 switch (*s) {
10089 case '\"':
10090 bufptr[bufend++] = '\\';
10091 bufptr[bufend++] = '\"';
10092 s++;
10093 break;
10094 case '\\':
10095 bufptr[bufend++] = '\\';
10096 bufptr[bufend++] = '\\';
10097 s++;
10098 break;
10099#if 0
10100 case '/':
10101 bufptr[bufend++] = '\\';
10102 bufptr[bufend++] = '/';
10103 s++;
10104 break;
10105#endif
10106 case '\b':
10107 bufptr[bufend++] = '\\';
10108 bufptr[bufend++] = 'b';
10109 s++;
10110 break;
10111 case '\f':
10112 bufptr[bufend++] = '\\';
10113 bufptr[bufend++] = 'f';
10114 s++;
10115 break;
10116 case '\n':
10117 bufptr[bufend++] = '\\';
10118 bufptr[bufend++] = 'n';
10119 s++;
10120 break;
10121 case '\r':
10122 bufptr[bufend++] = '\\';
10123 bufptr[bufend++] = 'r';
10124 s++;
10125 break;
10126 case '\t':
10127 bufptr[bufend++] = '\\';
10128 bufptr[bufend++] = 't';
10129 s++;
10130 break;
10131 default:
10132 bufptr[bufend++] = *s++;
10133 }
10134 }
10135
10136 bufptr[bufend++] = '"';
10137 bufptr[bufend] = 0; // NUL-terminate the buffer
10138
10139 *buffer_end = bufend;
10140
10141 remain = *buffer_size - *buffer_end;
10142 assert(remain > 0);
10143}
10144
10146{
10147 // sprintf "%.2e" can result in strings like "1,23e6" if
10148 // the user's locale has a comma for the decimal spearator.
10149 // However the JSON RFC requires a dot for the decimal.
10150 // This approach of "sprintf-then-replace" is much safer than
10151 // changing the user's locale, and much faster than the C++
10152 // approach of using a stringstream that we "imbue" with a C locale.
10153 char* comma = strchr(str, ',');
10154
10155 if (comma != nullptr) {
10156 *comma = '.';
10157 }
10158}
10159
10160static void json_write_data(char **buffer, int* buffer_size, int* buffer_end, int level, const KEY* key, const char* p)
10161{
10162 char str[256];
10163 switch (key->type) {
10164 case TID_UINT8:
10165 sprintf(str, "%u", *(unsigned char*)p);
10166 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10167 break;
10168 case TID_INT8:
10169 sprintf(str, "%d", *(char*)p);
10170 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10171 break;
10172 case TID_CHAR:
10173 sprintf(str, "%c", *(char*)p);
10174 json_write(buffer, buffer_size, buffer_end, 0, str, 1);
10175 break;
10176 case TID_UINT16:
10177 sprintf(str, "\"0x%04x\"", *(WORD*)p);
10178 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10179 break;
10180 case TID_INT16:
10181 sprintf(str, "%d", *(short*)p);
10182 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10183 break;
10184 case TID_UINT32:
10185 sprintf(str, "\"0x%08x\"", *(DWORD*)p);
10186 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10187 break;
10188 case TID_INT32:
10189 sprintf(str, "%d", *(int*)p);
10190 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10191 break;
10192 case TID_UINT64:
10193 // encode as hex string
10194 sprintf(str, "\"0x%016llx\"", *(UINT64*)p);
10195 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10196 break;
10197 case TID_INT64:
10198 // encode as decimal string
10199 sprintf(str, "\"%lld\"", *(INT64*)p);
10200 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10201 break;
10202 case TID_BOOL:
10203 if (*(int*)p)
10204 json_write(buffer, buffer_size, buffer_end, 0, "true", 0);
10205 else
10206 json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
10207 break;
10208 case TID_FLOAT: {
10209 float flt = (*(float*)p);
10210 if (isnan(flt))
10211 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
10212 else if (isinf(flt)) {
10213 if (flt > 0)
10214 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
10215 else
10216 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
10217 } else if (flt == 0)
10218 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10219 else if (flt == (int)flt) {
10220 sprintf(str, "%.0f", flt);
10222 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10223 } else {
10224 sprintf(str, "%.7e", flt);
10226 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10227 }
10228 break;
10229 }
10230 case TID_DOUBLE: {
10231 double dbl = (*(double*)p);
10232 if (isnan(dbl))
10233 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
10234 else if (isinf(dbl)) {
10235 if (dbl > 0)
10236 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
10237 else
10238 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
10239 } else if (dbl == 0)
10240 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10241 else if (dbl == (int)dbl) {
10242 sprintf(str, "%.0f", dbl);
10244 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10245 } else {
10246 sprintf(str, "%.16e", dbl);
10248 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10249 }
10250 break;
10251 }
10252 case TID_BITFIELD:
10253 json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
10254 break;
10255 case TID_STRING:
10256 // data is already NUL terminated // p[key.item_size-1] = 0; // make sure string is NUL terminated!
10257 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
10258 break;
10259 case TID_ARRAY:
10260 json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
10261 break;
10262 case TID_STRUCT:
10263 json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
10264 break;
10265 case TID_KEY:
10266 json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
10267 break;
10268 case TID_LINK:
10269 // data is already NUL terminated // p[key.item_size-1] = 0; // make sure string is NUL terminated!
10270 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
10271 break;
10272 default:
10273 json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
10274 }
10275}
10276
10277static void json_write_key(HNDLE hDB, HNDLE hKey, const KEY* key, const char* link_path, char **buffer, int* buffer_size, int* buffer_end)
10278{
10279 char str[256]; // not used to store anything long, only numeric values like: "item_size: 100"
10280
10281 json_write(buffer, buffer_size, buffer_end, 0, "{ ", 0);
10282
10283 sprintf(str, "\"type\" : %d", key->type);
10284 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10285
10286 if (link_path) {
10287 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10288 json_write(buffer, buffer_size, buffer_end, 0, "link", 1);
10289 json_write(buffer, buffer_size, buffer_end, 0, ": ", 0);
10290 json_write(buffer, buffer_size, buffer_end, 0, link_path, 1);
10291 }
10292
10293 if (key->num_values > 1) {
10294 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10295
10296 sprintf(str, "\"num_values\" : %d", key->num_values);
10297 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10298 }
10299
10300 if (key->type == TID_STRING) {
10301 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10302
10303 sprintf(str, "\"item_size\" : %d", key->item_size);
10304 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10305 }
10306
10307 if (key->notify_count > 0) {
10308 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10309
10310 sprintf(str, "\"notify_count\" : %d", key->notify_count);
10311 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10312 }
10313
10314 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10315
10316 sprintf(str, "\"access_mode\" : %d", key->access_mode);
10317 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10318
10319 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10320
10321 sprintf(str, "\"last_written\" : %d", key->last_written);
10322 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10323
10324 json_write(buffer, buffer_size, buffer_end, 0, " ", 0);
10325
10326 json_write(buffer, buffer_size, buffer_end, 0, "}", 0);
10327}
10328
10329static int db_save_json_key_obsolete(HNDLE hDB, HNDLE hKey, INT level, char **buffer, int* buffer_size, int* buffer_end, int save_keys, int follow_links, int recurse)
10330{
10331 INT i, size, status;
10332 char *data;
10333 KEY key;
10334 KEY link_key;
10335 char link_path[MAX_ODB_PATH];
10336 int omit_top_level_braces = 0;
10337
10338 //printf("db_save_json_key: key %d, level %d, save_keys %d, follow_links %d, recurse %d\n", hKey, level, save_keys, follow_links, recurse);
10339
10340 if (level < 0) {
10341 level = 0;
10342 omit_top_level_braces = 1;
10343 }
10344
10346
10347 if (status != DB_SUCCESS)
10348 return status;
10349
10350 link_key = key;
10351
10352 if (key.type == TID_LINK) {
10353 size = sizeof(link_path);
10354 status = db_get_data(hDB, hKey, link_path, &size, TID_LINK);
10355
10356 if (status != DB_SUCCESS)
10357 return status;
10358
10359 if (follow_links) {
10360 status = db_find_key(hDB, 0, link_path, &hKey);
10361
10362 if (status != DB_SUCCESS)
10363 return status;
10364
10366
10367 if (status != DB_SUCCESS)
10368 return status;
10369 }
10370 }
10371
10372 //printf("key [%s] link [%s], type %d, link %d\n", key.name, link_key.name, key.type, link_key.type);
10373
10374 if (key.type == TID_KEY && (recurse || level<=0)) {
10375 int idx = 0;
10376 int do_close_curly_bracket = 0;
10377
10378 if (level == 0 && !omit_top_level_braces) {
10379 json_write(buffer, buffer_size, buffer_end, 0, "{\n", 0);
10380 do_close_curly_bracket = 1;
10381 }
10382 else if (level > 0) {
10383 json_write(buffer, buffer_size, buffer_end, level, link_key.name, 1);
10384 json_write(buffer, buffer_size, buffer_end, 0, " : {\n", 0);
10385 do_close_curly_bracket = 1;
10386 }
10387
10388 if (level > 100) {
10389 std::string path = db_get_path(hDB, hKey);
10390
10391 json_write(buffer, buffer_size, buffer_end, 0, "/error", 1);
10392 json_write(buffer, buffer_size, buffer_end, 0, " : ", 0);
10393 json_write(buffer, buffer_size, buffer_end, 0, "max nesting level exceed", 1);
10394
10395 cm_msg(MERROR, "db_save_json_key", "max nesting level exceeded at \"%s\", check for symlink loops in this subtree", path.c_str());
10396
10397 } else {
10398 HNDLE hSubkey;
10399
10400 for (;; idx++) {
10401 db_enum_link(hDB, hKey, idx, &hSubkey);
10402
10403 if (!hSubkey)
10404 break;
10405
10406 if (idx != 0) {
10407 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10408 }
10409
10410 /* save subtree */
10411 status = db_save_json_key_obsolete(hDB, hSubkey, level + 1, buffer, buffer_size, buffer_end, save_keys, follow_links, recurse);
10412 if (status != DB_SUCCESS)
10413 return status;
10414 }
10415 }
10416
10417 if (do_close_curly_bracket) {
10418 if (idx > 0)
10419 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10420 json_write(buffer, buffer_size, buffer_end, level, "}", 0);
10421 }
10422
10423 } else {
10424
10425 if (save_keys && level == 0) {
10426 json_write(buffer, buffer_size, buffer_end, 0, "{\n", 0);
10427 }
10428
10429 /* save key value */
10430
10431 if (save_keys == 1) {
10432 char str[NAME_LENGTH+15];
10433 sprintf(str, "%s/key", link_key.name);
10434
10435 json_write(buffer, buffer_size, buffer_end, level, str, 1);
10436 json_write(buffer, buffer_size, buffer_end, 0, " : { ", 0);
10437
10438 sprintf(str, "\"type\" : %d", key.type);
10439 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10440
10441 if (link_key.type == TID_LINK && follow_links) {
10442 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10443 json_write(buffer, buffer_size, buffer_end, 0, "link", 1);
10444 json_write(buffer, buffer_size, buffer_end, 0, ": ", 0);
10445 json_write(buffer, buffer_size, buffer_end, 0, link_path, 1);
10446 }
10447
10448 if (key.num_values > 1) {
10449 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10450
10451 sprintf(str, "\"num_values\" : %d", key.num_values);
10452 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10453 }
10454
10455 if (key.type == TID_STRING || key.type == TID_LINK) {
10456 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10457
10458 sprintf(str, "\"item_size\" : %d", key.item_size);
10459 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10460 }
10461
10462 if (key.notify_count > 0) {
10463 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10464
10465 sprintf(str, "\"notify_count\" : %d", key.notify_count);
10466 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10467 }
10468
10469 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10470
10471 sprintf(str, "\"access_mode\" : %d", key.access_mode);
10472 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10473
10474 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10475
10476 sprintf(str, "\"last_written\" : %d", key.last_written);
10477 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10478
10479 json_write(buffer, buffer_size, buffer_end, 0, " ", 0);
10480
10481 json_write(buffer, buffer_size, buffer_end, 0, "}", 0);
10482
10483 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10484 }
10485
10486 if (save_keys == 2) {
10487 char str[NAME_LENGTH+15];
10488 sprintf(str, "%s/last_written", link_key.name);
10489
10490 json_write(buffer, buffer_size, buffer_end, level, str, 1);
10491
10492 sprintf(str, " : %d", key.last_written);
10493 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10494
10495 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10496 }
10497
10498 if (save_keys) {
10499 json_write(buffer, buffer_size, buffer_end, level, link_key.name, 1);
10500 json_write(buffer, buffer_size, buffer_end, 0, " : ", 0);
10501 }
10502
10503 if (key.num_values > 1) {
10504 json_write(buffer, buffer_size, buffer_end, 0, "[ ", 0);
10505 }
10506
10507 size = key.total_size;
10508 data = (char *) malloc(size);
10509 if (data == NULL) {
10510 cm_msg(MERROR, "db_save_json_key", "cannot allocate data buffer for %d bytes", size);
10511 return DB_NO_MEMORY;
10512 }
10513
10514 if (key.type != TID_KEY) {
10515 if (follow_links)
10516 status = db_get_data(hDB, hKey, data, &size, key.type);
10517 else
10519
10520 if (status != DB_SUCCESS)
10521 return status;
10522 }
10523
10524 for (i = 0; i < key.num_values; i++) {
10525 char str[256];
10526 char *p = data + key.item_size*i;
10527
10528 if (i != 0)
10529 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10530
10531 switch (key.type) {
10532 case TID_UINT8:
10533 sprintf(str, "%u", *(unsigned char*)p);
10534 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10535 break;
10536 case TID_INT8:
10537 sprintf(str, "%d", *(char*)p);
10538 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10539 break;
10540 case TID_CHAR:
10541 sprintf(str, "%c", *(char*)p);
10542 json_write(buffer, buffer_size, buffer_end, 0, str, 1);
10543 break;
10544 case TID_UINT16:
10545 sprintf(str, "\"0x%04x\"", *(WORD*)p);
10546 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10547 break;
10548 case TID_INT16:
10549 sprintf(str, "%d", *(short*)p);
10550 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10551 break;
10552 case TID_UINT32:
10553 sprintf(str, "\"0x%08x\"", *(DWORD*)p);
10554 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10555 break;
10556 case TID_INT32:
10557 sprintf(str, "%d", *(int*)p);
10558 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10559 break;
10560 case TID_UINT64:
10561 sprintf(str, "\"0x%08llx\"", *(UINT64*)p);
10562 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10563 break;
10564 case TID_INT64:
10565 sprintf(str, "%lld", *(INT64*)p);
10566 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10567 break;
10568 case TID_BOOL:
10569 if (*(int*)p)
10570 json_write(buffer, buffer_size, buffer_end, 0, "true", 0);
10571 else
10572 json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
10573 break;
10574 case TID_FLOAT: {
10575 float flt = (*(float*)p);
10576 if (isnan(flt))
10577 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
10578 else if (isinf(flt)) {
10579 if (flt > 0)
10580 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
10581 else
10582 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
10583 } else if (flt == 0)
10584 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10585 else if (flt == (int)flt) {
10586 sprintf(str, "%.0f", flt);
10587 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10588 } else {
10589 sprintf(str, "%.7e", flt);
10590 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10591 }
10592 break;
10593 }
10594 case TID_DOUBLE: {
10595 double dbl = (*(double*)p);
10596 if (isnan(dbl))
10597 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
10598 else if (isinf(dbl)) {
10599 if (dbl > 0)
10600 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
10601 else
10602 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
10603 } else if (dbl == 0)
10604 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10605 else if (dbl == (int)dbl) {
10606 sprintf(str, "%.0f", dbl);
10607 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10608 } else {
10609 sprintf(str, "%.16e", dbl);
10610 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10611 }
10612 break;
10613 }
10614 case TID_BITFIELD:
10615 json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
10616 break;
10617 case TID_STRING:
10618 p[key.item_size-1] = 0; // make sure string is NUL terminated!
10619 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
10620 break;
10621 case TID_ARRAY:
10622 json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
10623 break;
10624 case TID_STRUCT:
10625 json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
10626 break;
10627 case TID_KEY:
10628 json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
10629 break;
10630 case TID_LINK:
10631 p[key.item_size-1] = 0; // make sure string is NUL terminated!
10632 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
10633 break;
10634 default:
10635 json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
10636 }
10637
10638 }
10639
10640 if (key.num_values > 1) {
10641 json_write(buffer, buffer_size, buffer_end, 0, " ]", 0);
10642 } else {
10643 json_write(buffer, buffer_size, buffer_end, 0, "", 0);
10644 }
10645
10646 free(data);
10647 data = NULL;
10648
10649 if (save_keys && level == 0) {
10650 json_write(buffer, buffer_size, buffer_end, 0, "\n}", 0);
10651 }
10652 }
10653
10654 return DB_SUCCESS;
10655}
10656
10657/********************************************************************/
10668INT db_copy_json_array(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
10669{
10670 int size, asize;
10671 int status;
10672 char* data;
10673 int i;
10674 KEY key;
10675
10677 if (status != DB_SUCCESS)
10678 return status;
10679
10680 assert(key.type != TID_KEY);
10681
10682 if (key.num_values == 0) { // empty ODB entry cannot be handled by normal json_write_data()
10683
10684 switch (key.type) {
10685 case TID_UINT8:
10686 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10687 break;
10688 case TID_INT8:
10689 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10690 break;
10691 case TID_CHAR:
10692 json_write(buffer, buffer_size, buffer_end, 0, " ", 1);
10693 break;
10694 case TID_UINT16:
10695 json_write(buffer, buffer_size, buffer_end, 0, "0x0000", 1);
10696 break;
10697 case TID_INT16:
10698 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10699 break;
10700 case TID_UINT32:
10701 json_write(buffer, buffer_size, buffer_end, 0, "0x00000000", 1);
10702 break;
10703 case TID_INT32:
10704 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10705 break;
10706 case TID_UINT64:
10707 json_write(buffer, buffer_size, buffer_end, 0, "0x0000000000000000", 1);
10708 break;
10709 case TID_INT64:
10710 json_write(buffer, buffer_size, buffer_end, 0, "0", 1);
10711 break;
10712 case TID_BOOL:
10713 json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
10714 break;
10715 case TID_FLOAT: {
10716 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10717 break;
10718 }
10719 case TID_DOUBLE: {
10720 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10721 break;
10722 }
10723 case TID_BITFIELD:
10724 json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
10725 break;
10726 case TID_STRING:
10727 json_write(buffer, buffer_size, buffer_end, 0, "", 1);
10728 break;
10729 case TID_ARRAY:
10730 json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
10731 break;
10732 case TID_STRUCT:
10733 json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
10734 break;
10735 case TID_KEY:
10736 json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
10737 break;
10738 case TID_LINK:
10739 json_write(buffer, buffer_size, buffer_end, 0, "", 1);
10740 break;
10741 default:
10742 json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
10743 }
10744
10745 return DB_SUCCESS;
10746 }
10747
10748 if (key.num_values > 1) {
10749 json_write(buffer, buffer_size, buffer_end, 0, "[ ", 0);
10750 }
10751
10752 size = key.total_size;
10753
10754 asize = size;
10755 if (asize < 1024)
10756 asize = 1024;
10757
10758 data = (char *) malloc(asize);
10759 if (data == NULL) {
10760 cm_msg(MERROR, "db_save_json_key_data", "cannot allocate data buffer for %d bytes", asize);
10761 return DB_NO_MEMORY;
10762 }
10763
10764 data[0] = 0; // protect against TID_STRING that has key.total_size == 0.
10765
10766 status = db_get_data(hDB, hKey, data, &size, key.type);
10767 if (status != DB_SUCCESS) {
10768 free(data);
10769 return status;
10770 }
10771
10772 for (i = 0; i < key.num_values; i++) {
10773 char *p = data + key.item_size*i;
10774
10775 if (i != 0)
10776 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10777
10778 json_write_data(buffer, buffer_size, buffer_end, 0, &key, p);
10779 }
10780
10781 if (key.num_values > 1) {
10782 json_write(buffer, buffer_size, buffer_end, 0, " ]", 0);
10783 }
10784
10785 free(data);
10786 data = NULL;
10787
10788 return DB_SUCCESS;
10789}
10790
10791/********************************************************************/
10803INT db_copy_json_index(HNDLE hDB, HNDLE hKey, int index, char **buffer, int* buffer_size, int* buffer_end)
10804{
10805 int status;
10806 KEY key;
10807
10809
10810 if (status != DB_SUCCESS)
10811 return status;
10812
10813 int size = key.item_size;
10814 char* data = (char*)malloc(size + 1); // extra byte for string NUL termination
10815 assert(data != NULL);
10816
10818
10819 if (status != DB_SUCCESS) {
10820 free(data);
10821 return status;
10822 }
10823
10824 assert(size <= key.item_size);
10825 data[key.item_size] = 0; // make sure data is NUL terminated, in case of strings.
10826
10827 json_write_data(buffer, buffer_size, buffer_end, 0, &key, data);
10828
10829 free(data);
10830
10831 return DB_SUCCESS;
10832}
10833
10834static int json_write_bare_key(HNDLE hDB, HNDLE hLink, const KEY& link, char **buffer, int *buffer_size, int *buffer_end, int level, int flags, time_t timestamp, bool need_comma)
10835{
10836 int status;
10837 char link_buf[MAX_ODB_PATH]; // link_path points to link_buf
10838 char* link_path = NULL;
10839
10840 HNDLE hLinkTarget = hLink;
10841
10842 if (link.type == TID_LINK) {
10843 int size = sizeof(link_buf);
10844 status = db_get_link_data(hDB, hLink, link_buf, &size, TID_LINK);
10845 if (status != DB_SUCCESS)
10846 return status;
10847
10848 //printf("link size %d, len %d, data [%s]\n", size, (int)strlen(link_buf), link_buf);
10849
10850 if ((size > 0) && (strlen(link_buf) > 0)) {
10851 link_path = link_buf;
10852
10853 // resolve the link, unless it is a link to an array element
10854 if ((flags & JSFLAG_FOLLOW_LINKS) && strchr(link_path, '[') == NULL) {
10855 status = db_find_key(hDB, 0, link_path, &hLinkTarget);
10856 if (status != DB_SUCCESS) {
10857 // dangling link to nowhere
10858 hLinkTarget = hLink;
10859 }
10860 }
10861 }
10862 }
10863
10864 KEY link_target;
10865 status = db_get_key(hDB, hLinkTarget, &link_target);
10866 if (status != DB_SUCCESS)
10867 return status;
10868
10869 if (flags & JSFLAG_OMIT_OLD) {
10870 if (link_target.last_written) {
10871 if (link_target.last_written < timestamp) {
10872 // omit old data
10873 return DB_SUCCESS;
10874 }
10875 }
10876 }
10877
10878 if (need_comma) {
10879 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10880 } else {
10881 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10882 }
10883
10884 char link_name[MAX_ODB_PATH];
10885
10886 mstrlcpy(link_name, link.name, sizeof(link_name));
10887
10888 if (flags & JSFLAG_LOWERCASE) {
10889 for (int i=0; (i<(int)sizeof(link.name)) && link.name[i]; i++)
10890 link_name[i] = tolower(link.name[i]);
10891 }
10892
10893 if ((flags & JSFLAG_LOWERCASE) && !(flags & JSFLAG_OMIT_NAMES)) {
10894 char buf[MAX_ODB_PATH];
10895 mstrlcpy(buf, link_name, sizeof(buf));
10896 mstrlcat(buf, "/name", sizeof(buf));
10897 json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10898 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10899 json_write(buffer, buffer_size, buffer_end, 0, link.name, 1);
10900 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10901 }
10902
10903 if (link.type != TID_KEY && (flags & JSFLAG_SAVE_KEYS)) {
10904 char buf[MAX_ODB_PATH];
10905 mstrlcpy(buf, link_name, sizeof(buf));
10906 mstrlcat(buf, "/key", sizeof(buf));
10907 json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10908 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10909 json_write_key(hDB, hLink, &link_target, link_path, buffer, buffer_size, buffer_end);
10910 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10911 } else if ((link_target.type != TID_KEY) && !(flags & JSFLAG_OMIT_LAST_WRITTEN)) {
10912 char buf[MAX_ODB_PATH];
10913 mstrlcpy(buf, link_name, sizeof(buf));
10914 mstrlcat(buf, "/last_written", sizeof(buf));
10915 json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10916 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10917 sprintf(buf, "%d", link_target.last_written);
10918 json_write(buffer, buffer_size, buffer_end, 0, buf, 0);
10919 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10920 }
10921
10922 json_write(buffer, buffer_size, buffer_end, level, link_name, 1);
10923 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10924
10925 if (link_target.type == TID_KEY && !(flags & JSFLAG_RECURSE)) {
10926 json_write(buffer, buffer_size, buffer_end, 0, "{ }" , 0);
10927 } else {
10928 status = json_write_anything(hDB, hLinkTarget, buffer, buffer_size, buffer_end, level, 0, flags, timestamp);
10929 if (status != DB_SUCCESS)
10930 return status;
10931 }
10932
10933 return DB_SUCCESS;
10934}
10935
10936int json_write_bare_subdir(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int level, int flags, time_t timestamp)
10937{
10938 if (level > MAX_ODB_PATH) {
10939 cm_msg(MERROR, "json_write_bare_subdir", "Max ODB subdirectory nesting level exceeded %d at \"%s\"", level, db_get_path(hDB, hKey).c_str());
10940 return DB_TRUNCATED;
10941 }
10942
10943 for (int i=0; ; i++) {
10944 int status;
10945
10946 HNDLE hLink;
10947 status = db_enum_link(hDB, hKey, i, &hLink);
10948 if (status != DB_SUCCESS && !hLink)
10949 break;
10950
10951 KEY link;
10952 status = db_get_link(hDB, hLink, &link);
10953 if (status != DB_SUCCESS)
10954 return status;
10955
10956 bool need_comma = (i!=0);
10957
10958 status = json_write_bare_key(hDB, hLink, link, buffer, buffer_size, buffer_end, level, flags, timestamp, need_comma);
10959 if (status != DB_SUCCESS)
10960 return status;
10961 }
10962
10963 return DB_SUCCESS;
10964}
10965
10966int EXPRT json_write_anything(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int level, int must_be_subdir, int flags, time_t timestamp)
10967{
10968 int status;
10969 KEY key;
10970
10972 if (status != DB_SUCCESS)
10973 return status;
10974
10975 if (key.type == TID_KEY) {
10976
10977 json_write(buffer, buffer_size, buffer_end, 0, "{", 0);
10978
10979 status = json_write_bare_subdir(hDB, hKey, buffer, buffer_size, buffer_end, level+1, flags, timestamp);
10980 if (status != DB_SUCCESS)
10981 return status;
10982
10983 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10984 json_write(buffer, buffer_size, buffer_end, level, "}", 0);
10985
10986 } else if (must_be_subdir) {
10987 json_write(buffer, buffer_size, buffer_end, 0, "{", 0);
10988 status = json_write_bare_key(hDB, hKey, key, buffer, buffer_size, buffer_end, level+1, flags, timestamp, false);
10989 if (status != DB_SUCCESS)
10990 return status;
10991 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10992 json_write(buffer, buffer_size, buffer_end, level, "}", 0);
10993 } else {
10994 status = db_copy_json_array(hDB, hKey, buffer, buffer_size, buffer_end);
10995
10996 if (status != DB_SUCCESS)
10997 return status;
10998 }
10999
11000 return DB_SUCCESS;
11001}
11002
11003INT db_copy_json_ls(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
11004{
11005 int status;
11006 bool unlock = false;
11007
11008 if (!rpc_is_remote()) {
11010 if (status != DB_SUCCESS) {
11011 return status;
11012 }
11013 unlock = true;
11014 }
11015
11017
11018 if (unlock) {
11020 }
11021
11022 return status;
11023}
11024
11025INT db_copy_json_values(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end, int omit_names, int omit_last_written, time_t omit_old_timestamp, int preserve_case)
11026{
11027 int status;
11029 if (omit_names)
11030 flags |= JSFLAG_OMIT_NAMES;
11031 if (omit_last_written)
11032 flags |= JSFLAG_OMIT_LAST_WRITTEN;
11033 if (omit_old_timestamp)
11034 flags |= JSFLAG_OMIT_OLD;
11035 if (!preserve_case)
11036 flags |= JSFLAG_LOWERCASE;
11037
11038 bool unlock = false;
11039
11040 if (!rpc_is_remote()) {
11042 if (status != DB_SUCCESS) {
11043 return status;
11044 }
11045 unlock = true;
11046 }
11047
11048 status = json_write_anything(hDB, hKey, buffer, buffer_size, buffer_end, JS_LEVEL_0, 0, flags, omit_old_timestamp);
11049
11050 if (unlock) {
11052 }
11053
11054 return status;
11055}
11056
11057INT db_copy_json_save(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
11058{
11059 int status;
11060 bool unlock = false;
11061
11062 if (!rpc_is_remote()) {
11064 if (status != DB_SUCCESS) {
11065 return status;
11066 }
11067 unlock = true;
11068 }
11069
11071
11072 if (unlock) {
11074 }
11075
11076 return status;
11077}
11078
11079/********************************************************************/
11090INT db_copy_json_obsolete(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end, int save_keys, int follow_links, int recurse)
11091{
11092 db_save_json_key_obsolete(hDB, hKey, 0, buffer, buffer_size, buffer_end, save_keys, follow_links, recurse);
11093 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
11094 return DB_SUCCESS;
11095}
11096
11097/********************************************************************/
11109INT db_save_json(HNDLE hDB, HNDLE hKey, const char *filename, int flags)
11110{
11111 INT status;
11112
11113 /* open file */
11114 FILE *fp = fopen(filename, "w");
11115 if (fp == NULL) {
11116 cm_msg(MERROR, "db_save_json", "Cannot open file \"%s\", fopen() errno %d (%s)", filename, errno, strerror(errno));
11117 return DB_FILE_ERROR;
11118 }
11119
11120 bool unlock = false;
11121
11122 if (!rpc_is_remote()) {
11124 if (status != DB_SUCCESS) {
11125 fclose(fp);
11126 return status;
11127 }
11128 unlock = true;
11129 }
11130
11131 std::string path = db_get_path(hDB, hKey);
11132
11133 bool emptySubdir = false;
11134 HNDLE hSubKey;
11135 status = db_enum_link(hDB, hKey, 0, &hSubKey);
11137 emptySubdir = true;
11138
11139 char* buffer = NULL;
11140 int buffer_size = 0;
11141 int buffer_end = 0;
11142
11143 json_write(&buffer, &buffer_size, &buffer_end, 0, "{\n", 0);
11144
11145 json_write(&buffer, &buffer_size, &buffer_end, 1, "/MIDAS version", 1);
11146 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
11147 json_write(&buffer, &buffer_size, &buffer_end, 0, MIDAS_VERSION, 1);
11148 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
11149
11150 json_write(&buffer, &buffer_size, &buffer_end, 1, "/MIDAS git revision", 1);
11151 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
11152 json_write(&buffer, &buffer_size, &buffer_end, 0, GIT_REVISION, 1);
11153 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
11154
11155 json_write(&buffer, &buffer_size, &buffer_end, 1, "/filename", 1);
11156 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
11157 json_write(&buffer, &buffer_size, &buffer_end, 0, filename, 1);
11158 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
11159
11160 json_write(&buffer, &buffer_size, &buffer_end, 1, "/ODB path", 1);
11161 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
11162 json_write(&buffer, &buffer_size, &buffer_end, 0, path.c_str(), 1);
11163
11164 if (emptySubdir)
11165 json_write(&buffer, &buffer_size, &buffer_end, 0, "", 0);
11166 else
11167 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
11168
11169 //status = db_save_json_key_obsolete(hDB, hKey, -1, &buffer, &buffer_size, &buffer_end, 1, 0, 1);
11170 status = json_write_bare_subdir(hDB, hKey, &buffer, &buffer_size, &buffer_end, JS_LEVEL_1, flags, 0);
11171
11172 json_write(&buffer, &buffer_size, &buffer_end, 0, "\n}\n", 0);
11173
11174 if (unlock) {
11176 }
11177
11178 if (status == DB_SUCCESS) {
11179 if (buffer) {
11180 size_t wr = fwrite(buffer, 1, buffer_end, fp);
11181 if (wr != (size_t)buffer_end) {
11182 cm_msg(MERROR, "db_save_json", "Cannot write to file \"%s\", fwrite() errno %d (%s)", filename, errno, strerror(errno));
11183 free(buffer);
11184 fclose(fp);
11185 return DB_FILE_ERROR;
11186 }
11187 }
11188 }
11189
11190 if (buffer)
11191 free(buffer);
11192
11193 fclose(fp);
11194
11195 return DB_SUCCESS;
11196}
11197
11198/********************************************************************/
11209INT db_save_struct(HNDLE hDB, HNDLE hKey, const char *file_name, const char *struct_name, BOOL append)
11210{
11211 KEY key;
11212 char str[100], line[10+100];
11213 INT status, i, fh;
11214 int wr, size;
11215
11216 /* open file */
11217 fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
11218
11219 if (fh == -1) {
11220 cm_msg(MERROR, "db_save_struct", "Cannot open file\"%s\"", file_name);
11221 return DB_FILE_ERROR;
11222 }
11223
11225 if (status != DB_SUCCESS) {
11226 cm_msg(MERROR, "db_save_struct", "cannot find key");
11227 return DB_INVALID_HANDLE;
11228 }
11229
11230 sprintf(line, "typedef struct {\n");
11231
11232 size = strlen(line);
11233 wr = write(fh, line, size);
11234 if (wr != size) {
11235 cm_msg(MERROR, "db_save_struct", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
11236 close(fh);
11237 return DB_FILE_ERROR;
11238 }
11239
11240 db_save_tree_struct(hDB, hKey, fh, 0);
11241
11242 if (struct_name && struct_name[0])
11243 mstrlcpy(str, struct_name, sizeof(str));
11244 else
11245 mstrlcpy(str, key.name, sizeof(str));
11246
11247 name2c(str);
11248 for (i = 0; i < (int) strlen(str); i++)
11249 str[i] = (char) toupper(str[i]);
11250
11251 sprintf(line, "} %s;\n\n", str);
11252
11253 size = strlen(line);
11254 wr = write(fh, line, size);
11255 if (wr != size) {
11256 cm_msg(MERROR, "db_save_struct", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
11257 close(fh);
11258 return DB_FILE_ERROR;
11259 }
11260
11261 close(fh);
11262
11263 return DB_SUCCESS;
11264}
11265
11267#ifndef DOXYGEN_SHOULD_SKIP_THIS
11268
11269/*------------------------------------------------------------------*/
11270INT db_save_string(HNDLE hDB, HNDLE hKey, const char *file_name, const char *string_name, BOOL append)
11271/********************************************************************\
11272
11273 Routine: db_save_string
11274
11275 Purpose: Save a branch of a database as a string which can be used
11276 by db_create_record.
11277
11278 Input:
11279 HNDLE hDB Handle to the database
11280 HNDLE hKey Handle of key to start, 0 for root
11281 int fh File handle to write to
11282 char string_name Name of string. If struct_name == NULL,
11283 the name of the key is used.
11284
11285 Output:
11286 none
11287
11288 Function value:
11289 DB_SUCCESS Successful completion
11290 DB_INVALID_HANDLE Database handle is invalid
11291
11292\********************************************************************/
11293{
11294 KEY key;
11295 char str[256], line[50+256];
11296 INT status, i, size, fh, buffer_size;
11297 char *buffer = NULL, *pc;
11298 int wr;
11299
11300
11301 /* open file */
11302 fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
11303
11304 if (fh == -1) {
11305 cm_msg(MERROR, "db_save_string", "Cannot open file\"%s\"", file_name);
11306 return DB_FILE_ERROR;
11307 }
11308
11310 if (status != DB_SUCCESS) {
11311 cm_msg(MERROR, "db_save_string", "cannot find key");
11312 return DB_INVALID_HANDLE;
11313 }
11314
11315 if (string_name && string_name[0])
11316 strcpy(str, string_name);
11317 else
11318 strcpy(str, key.name);
11319
11320 name2c(str);
11321 for (i = 0; i < (int) strlen(str); i++)
11322 str[i] = (char) toupper(str[i]);
11323
11324 sprintf(line, "#define %s(_name) const char *_name[] = {\\\n", str);
11325 size = strlen(line);
11326 wr = write(fh, line, size);
11327 if (wr != size) {
11328 cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
11329 close(fh);
11330 if (buffer)
11331 free(buffer);
11332 return DB_FILE_ERROR;
11333 }
11334
11335 buffer_size = 10000;
11336 do {
11337 buffer = (char *) malloc(buffer_size);
11338 if (buffer == NULL) {
11339 cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
11340 break;
11341 }
11342
11343 size = buffer_size;
11344 status = db_copy(hDB, hKey, buffer, &size, "");
11345 if (status != DB_TRUNCATED)
11346 break;
11347
11348 /* increase buffer size if truncated */
11349 free(buffer);
11350 buffer = NULL;
11351 buffer_size *= 2;
11352 } while (1);
11353
11354
11355 pc = buffer;
11356
11357 do {
11358 i = 0;
11359 line[i++] = '"';
11360 while (*pc != '\n' && *pc != 0) {
11361 if (*pc == '\"' || *pc == '\'')
11362 line[i++] = '\\';
11363 line[i++] = *pc++;
11364 }
11365 strcpy(&line[i], "\",\\\n");
11366 if (i > 0) {
11367 size = strlen(line);
11368 wr = write(fh, line, size);
11369 if (wr != size) {
11370 cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
11371 close(fh);
11372 if (buffer)
11373 free(buffer);
11374 return DB_FILE_ERROR;
11375 }
11376 }
11377
11378 if (*pc == '\n')
11379 pc++;
11380
11381 } while (*pc);
11382
11383 sprintf(line, "NULL }\n\n");
11384 size = strlen(line);
11385 wr = write(fh, line, size);
11386 if (wr != size) {
11387 cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
11388 close(fh);
11389 if (buffer)
11390 free(buffer);
11391 return DB_FILE_ERROR;
11392 }
11393
11394 close(fh);
11395 free(buffer);
11396
11397 return DB_SUCCESS;
11398}
11399
11401#endif /* DOXYGEN_SHOULD_SKIP_THIS */
11402
11403/********************************************************************/
11426INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
11427{
11428 if (data_size == 0)
11429 sprintf(string, "<NULL>");
11430 else
11431 switch (type) {
11432 case TID_UINT8:
11433 sprintf(string, "%d", *(((BYTE *) data) + idx));
11434 break;
11435 case TID_INT8:
11436 sprintf(string, "%d", *(((char *) data) + idx));
11437 break;
11438 case TID_CHAR:
11439 sprintf(string, "%c", *(((char *) data) + idx));
11440 break;
11441 case TID_UINT16:
11442 sprintf(string, "%u", *(((WORD *) data) + idx));
11443 break;
11444 case TID_INT16:
11445 sprintf(string, "%d", *(((short *) data) + idx));
11446 break;
11447 case TID_UINT32:
11448 sprintf(string, "%u", *(((DWORD *) data) + idx));
11449 break;
11450 case TID_INT32:
11451 sprintf(string, "%d", *(((INT *) data) + idx));
11452 break;
11453 case TID_UINT64:
11454 sprintf(string, "%llu", *(((UINT64 *) data) + idx));
11455 break;
11456 case TID_INT64:
11457 sprintf(string, "%lld", *(((INT64 *) data) + idx));
11458 break;
11459 case TID_BOOL:
11460 sprintf(string, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11461 break;
11462 case TID_FLOAT:
11463 if (ss_isnan(*(((float *) data) + idx)))
11464 sprintf(string, "NAN");
11465 else
11466 sprintf(string, "%.7g", *(((float *) data) + idx));
11467 break;
11468 case TID_DOUBLE:
11469 if (ss_isnan(*(((double *) data) + idx)))
11470 sprintf(string, "NAN");
11471 else
11472 sprintf(string, "%.16lg", *(((double *) data) + idx));
11473 break;
11474 case TID_BITFIELD:
11475 sprintf(string, "%u", *(((DWORD *) data) + idx));
11476 break;
11477 case TID_STRING:
11478 case TID_LINK:
11479 mstrlcpy(string, ((char *) data) + data_size * idx, MAX_STRING_LENGTH);
11480 break;
11481 default:
11482 sprintf(string, "<unknown>");
11483 break;
11484 }
11485
11486 return DB_SUCCESS;
11487}
11488
11489/********************************************************************/
11502INT db_sprintff(char *string, const char *format, const void *data, INT data_size, INT idx, DWORD type)
11503{
11504 if (data_size == 0)
11505 sprintf(string, "<NULL>");
11506 else
11507 switch (type) {
11508 case TID_UINT8:
11509 sprintf(string, format, *(((BYTE *) data) + idx));
11510 break;
11511 case TID_INT8:
11512 sprintf(string, format, *(((char *) data) + idx));
11513 break;
11514 case TID_CHAR:
11515 sprintf(string, format, *(((char *) data) + idx));
11516 break;
11517 case TID_UINT16:
11518 sprintf(string, format, *(((WORD *) data) + idx));
11519 break;
11520 case TID_INT16:
11521 sprintf(string, format, *(((short *) data) + idx));
11522 break;
11523 case TID_UINT32:
11524 sprintf(string, format, *(((DWORD *) data) + idx));
11525 break;
11526 case TID_INT32:
11527 sprintf(string, format, *(((INT *) data) + idx));
11528 break;
11529 case TID_UINT64:
11530 sprintf(string, format, *(((UINT64 *) data) + idx));
11531 break;
11532 case TID_INT64:
11533 sprintf(string, format, *(((INT64 *) data) + idx));
11534 break;
11535 case TID_BOOL:
11536 sprintf(string, format, *(((BOOL *) data) + idx) ? 'y' : 'n');
11537 break;
11538 case TID_FLOAT:
11539 if (ss_isnan(*(((float *) data) + idx)))
11540 sprintf(string, "NAN");
11541 else
11542 sprintf(string, format, *(((float *) data) + idx));
11543 break;
11544 case TID_DOUBLE:
11545 if (ss_isnan(*(((double *) data) + idx)))
11546 sprintf(string, "NAN");
11547 else
11548 sprintf(string, format, *(((double *) data) + idx));
11549 break;
11550 case TID_BITFIELD:
11551 sprintf(string, format, *(((DWORD *) data) + idx));
11552 break;
11553 case TID_STRING:
11554 case TID_LINK:
11555 mstrlcpy(string, ((char *) data) + data_size * idx, MAX_STRING_LENGTH);
11556 break;
11557 default:
11558 sprintf(string, "<unknown>");
11559 break;
11560 }
11561
11562 return DB_SUCCESS;
11563}
11564
11565/*------------------------------------------------------------------*/
11566INT db_sprintfh(char *string, const void *data, INT data_size, INT idx, DWORD type)
11567/********************************************************************\
11568
11569 Routine: db_sprintfh
11570
11571 Purpose: Convert a database value to a string according to its type
11572 in hex format
11573
11574 Input:
11575 void *data Value data
11576 INT idx Index for array data
11577 INT data_size Size of single data element
11578 DWORD type Valye type, one of TID_xxx
11579
11580 Output:
11581 char *string ASCII string of data
11582
11583 Function value:
11584 DB_SUCCESS Successful completion
11585
11586\********************************************************************/
11587{
11588 if (data_size == 0)
11589 sprintf(string, "<NULL>");
11590 else
11591 switch (type) {
11592 case TID_UINT8:
11593 sprintf(string, "0x%X", *(((BYTE *) data) + idx));
11594 break;
11595 case TID_INT8:
11596 sprintf(string, "0x%X", *(((char *) data) + idx));
11597 break;
11598 case TID_CHAR:
11599 sprintf(string, "%c", *(((char *) data) + idx));
11600 break;
11601 case TID_UINT16:
11602 sprintf(string, "0x%X", *(((WORD *) data) + idx));
11603 break;
11604 case TID_INT16:
11605 sprintf(string, "0x%hX", *(((short *) data) + idx));
11606 break;
11607 case TID_UINT32:
11608 sprintf(string, "0x%X", *(((DWORD *) data) + idx));
11609 break;
11610 case TID_INT32:
11611 sprintf(string, "0x%X", *(((INT *) data) + idx));
11612 break;
11613 case TID_UINT64:
11614 sprintf(string, "0x%llX", *(((UINT64 *) data) + idx));
11615 break;
11616 case TID_INT64:
11617 sprintf(string, "0x%llX", *(((INT64 *) data) + idx));
11618 break;
11619 case TID_BOOL:
11620 sprintf(string, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11621 break;
11622 case TID_FLOAT:
11623 if (ss_isnan(*(((float *) data) + idx)))
11624 sprintf(string, "NAN");
11625 else
11626 sprintf(string, "%.7g", *(((float *) data) + idx));
11627 break;
11628 case TID_DOUBLE:
11629 if (ss_isnan(*(((double *) data) + idx)))
11630 sprintf(string, "NAN");
11631 else
11632 sprintf(string, "%.16lg", *(((double *) data) + idx));
11633 break;
11634 case TID_BITFIELD:
11635 sprintf(string, "0x%X", *(((DWORD *) data) + idx));
11636 break;
11637 case TID_STRING:
11638 case TID_LINK:
11639 sprintf(string, "%s", ((char *) data) + data_size * idx);
11640 break;
11641 default:
11642 sprintf(string, "<unknown>");
11643 break;
11644 }
11645
11646 return DB_SUCCESS;
11647}
11648
11649/********************************************************************/
11670std::string db_sprintf(const void *data, INT data_size, INT idx, DWORD type)
11671{
11672 if (data_size == 0) {
11673 return "<NULL>";
11674 } else {
11675 char buf[256];
11676 switch (type) {
11677 case TID_UINT8:
11678 sprintf(buf, "%d", *(((BYTE *) data) + idx));
11679 return buf;
11680 case TID_INT8:
11681 sprintf(buf, "%d", *(((char *) data) + idx));
11682 return buf;
11683 case TID_CHAR:
11684 sprintf(buf, "%c", *(((char *) data) + idx));
11685 return buf;
11686 case TID_UINT16:
11687 sprintf(buf, "%u", *(((WORD *) data) + idx));
11688 return buf;
11689 case TID_INT16:
11690 sprintf(buf, "%d", *(((short *) data) + idx));
11691 return buf;
11692 case TID_UINT32:
11693 sprintf(buf, "%u", *(((DWORD *) data) + idx));
11694 return buf;
11695 case TID_INT32:
11696 sprintf(buf, "%d", *(((INT *) data) + idx));
11697 return buf;
11698 case TID_UINT64:
11699 sprintf(buf, "%llu", *(((UINT64 *) data) + idx));
11700 return buf;
11701 case TID_INT64:
11702 sprintf(buf, "%lld", *(((INT64 *) data) + idx));
11703 return buf;
11704 case TID_BOOL:
11705 sprintf(buf, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11706 return buf;
11707 case TID_FLOAT:
11708 if (ss_isnan(*(((float *) data) + idx))) {
11709 return "NAN";
11710 } else {
11711 sprintf(buf, "%.7g", *(((float *) data) + idx));
11712 return buf;
11713 }
11714 case TID_DOUBLE:
11715 if (ss_isnan(*(((double *) data) + idx))) {
11716 return "NAN";
11717 } else {
11718 sprintf(buf, "%.16lg", *(((double *) data) + idx));
11719 return buf;
11720 }
11721 case TID_BITFIELD:
11722 sprintf(buf, "%u", *(((DWORD *) data) + idx));
11723 return buf;
11724 case TID_STRING:
11725 case TID_LINK:
11726 return (((char *) data) + data_size * idx);
11727 default:
11728 return "<unknown>";
11729 }
11730 }
11731}
11732
11733/********************************************************************/
11746std::string db_sprintff(const char *format, const void *data, INT data_size, INT idx, DWORD type)
11747{
11748 if (data_size == 0) {
11749 return "<NULL>";
11750 } else {
11751 char buf[256];
11752 switch (type) {
11753 case TID_UINT8:
11754 sprintf(buf, format, *(((BYTE *) data) + idx));
11755 return buf;
11756 case TID_INT8:
11757 sprintf(buf, format, *(((char *) data) + idx));
11758 return buf;
11759 case TID_CHAR:
11760 sprintf(buf, format, *(((char *) data) + idx));
11761 return buf;
11762 case TID_UINT16:
11763 sprintf(buf, format, *(((WORD *) data) + idx));
11764 return buf;
11765 case TID_INT16:
11766 sprintf(buf, format, *(((short *) data) + idx));
11767 return buf;
11768 case TID_UINT32:
11769 sprintf(buf, format, *(((DWORD *) data) + idx));
11770 return buf;
11771 case TID_INT32:
11772 sprintf(buf, format, *(((INT *) data) + idx));
11773 return buf;
11774 case TID_UINT64:
11775 sprintf(buf, format, *(((UINT64 *) data) + idx));
11776 return buf;
11777 case TID_INT64:
11778 sprintf(buf, format, *(((INT64 *) data) + idx));
11779 return buf;
11780 case TID_BOOL:
11781 sprintf(buf, format, *(((BOOL *) data) + idx) ? 'y' : 'n');
11782 return buf;
11783 case TID_FLOAT:
11784 if (ss_isnan(*(((float *) data) + idx))) {
11785 return "NAN";
11786 } else {
11787 sprintf(buf, format, *(((float *) data) + idx));
11788 return buf;
11789 }
11790 case TID_DOUBLE:
11791 if (ss_isnan(*(((double *) data) + idx))) {
11792 return "NAN";
11793 } else {
11794 sprintf(buf, format, *(((double *) data) + idx));
11795 return buf;
11796 }
11797 case TID_BITFIELD:
11798 sprintf(buf, format, *(((DWORD *) data) + idx));
11799 return buf;
11800 case TID_STRING:
11801 case TID_LINK:
11802 return (((char *) data) + data_size * idx);
11803 default:
11804 return "<unknown>";
11805 }
11806 }
11807}
11808
11810#ifndef DOXYGEN_SHOULD_SKIP_THIS
11811
11812/*------------------------------------------------------------------*/
11813std::string db_sprintfh(const void *data, INT data_size, INT idx, DWORD type)
11814/********************************************************************\
11815
11816 Routine: db_sprintfh
11817
11818 Purpose: Convert a database value to a string according to its type
11819 in hex format
11820
11821 Input:
11822 void *data Value data
11823 INT idx Index for array data
11824 INT data_size Size of single data element
11825 DWORD type Valye type, one of TID_xxx
11826
11827 Output:
11828 char *string ASCII string of data
11829
11830 Function value:
11831 DB_SUCCESS Successful completion
11832
11833\********************************************************************/
11834{
11835 if (data_size == 0) {
11836 return "<NULL>";
11837 } else {
11838 char buf[256];
11839 switch (type) {
11840 case TID_UINT8:
11841 sprintf(buf, "0x%X", *(((BYTE *) data) + idx));
11842 return buf;
11843 case TID_INT8:
11844 sprintf(buf, "0x%X", *(((char *) data) + idx));
11845 return buf;
11846 case TID_CHAR:
11847 sprintf(buf, "%c", *(((char *) data) + idx));
11848 return buf;
11849 case TID_UINT16:
11850 sprintf(buf, "0x%X", *(((WORD *) data) + idx));
11851 return buf;
11852 case TID_INT16:
11853 sprintf(buf, "0x%hX", *(((short *) data) + idx));
11854 return buf;
11855 case TID_UINT32:
11856 sprintf(buf, "0x%X", *(((DWORD *) data) + idx));
11857 return buf;
11858 case TID_INT32:
11859 sprintf(buf, "0x%X", *(((INT *) data) + idx));
11860 return buf;
11861 case TID_UINT64:
11862 sprintf(buf, "0x%llX", *(((UINT64 *) data) + idx));
11863 return buf;
11864 case TID_INT64:
11865 sprintf(buf, "0x%llX", *(((INT64 *) data) + idx));
11866 return buf;
11867 case TID_BOOL:
11868 sprintf(buf, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11869 return buf;
11870 case TID_FLOAT:
11871 if (ss_isnan(*(((float *) data) + idx))) {
11872 return "NAN";
11873 } else {
11874 sprintf(buf, "%.7g", *(((float *) data) + idx));
11875 return buf;
11876 }
11877 case TID_DOUBLE:
11878 if (ss_isnan(*(((double *) data) + idx))) {
11879 return "NAN";
11880 } else {
11881 sprintf(buf, "%.16lg", *(((double *) data) + idx));
11882 return buf;
11883 }
11884 case TID_BITFIELD:
11885 sprintf(buf, "0x%X", *(((DWORD *) data) + idx));
11886 return buf;
11887 case TID_STRING:
11888 case TID_LINK:
11889 return (((char *) data) + data_size * idx);
11890 default:
11891 return "<unknown>";
11892 }
11893 }
11894}
11895
11896/*------------------------------------------------------------------*/
11897INT db_sscanf(const char *data_str, void *data, INT * data_size, INT i, DWORD tid)
11898/********************************************************************\
11899
11900 Routine: db_sscanf
11901
11902 Purpose: Convert a string to a database value according to its type
11903
11904 Input:
11905 char *data_str ASCII string of data
11906 INT i Index for array data
11907 DWORD tid Value type, one of TID_xxx
11908
11909 Output:
11910 void *data Value data
11911 INT *data_size Size of single data element
11912
11913 Function value:
11914 DB_SUCCESS Successful completion
11915
11916\********************************************************************/
11917{
11918 DWORD value = 0;
11919 BOOL hex = FALSE;
11920
11921 if (data_str == NULL)
11922 return 0;
11923
11924 *data_size = rpc_tid_size(tid);
11925 if (strncmp(data_str, "0x", 2) == 0) {
11926 hex = TRUE;
11927 sscanf(data_str + 2, "%x", &value);
11928 }
11929
11930 switch (tid) {
11931 case TID_UINT8:
11932 case TID_INT8:
11933 if (hex)
11934 *((char *) data + i) = (char) value;
11935 else
11936 *((char *) data + i) = (char) atoi(data_str);
11937 break;
11938 case TID_CHAR:
11939 *((char *) data + i) = data_str[0];
11940 break;
11941 case TID_UINT16:
11942 if (hex)
11943 *((WORD *) data + i) = (WORD) value;
11944 else
11945 *((WORD *) data + i) = (WORD) atoi(data_str);
11946 break;
11947 case TID_INT16:
11948 if (hex)
11949 *((short int *) data + i) = (short int) value;
11950 else
11951 *((short int *) data + i) = (short int) atoi(data_str);
11952 break;
11953 case TID_UINT32:
11954 if (hex)
11955 value = strtoul(data_str, nullptr, 16);
11956 else
11957 value = strtoul(data_str, nullptr, 10);
11958
11959 *((DWORD *) data + i) = value;
11960 break;
11961 case TID_INT32:
11962 if (hex)
11963 value = strtol(data_str, nullptr, 16);
11964 else
11965 value = strtol(data_str, nullptr, 10);
11966
11967 *((INT *) data + i) = value;
11968 break;
11969 case TID_UINT64:
11970 if (hex)
11971 *((UINT64 *) data + i) = strtoull(data_str, nullptr, 16);
11972 else
11973 *((UINT64 *) data + i) = strtoull(data_str, nullptr, 10);
11974 break;
11975 case TID_INT64:
11976 if (hex)
11977 *((UINT64 *) data + i) = strtoll(data_str, nullptr, 16);
11978 else
11979 *((UINT64 *) data + i) = strtoll(data_str, nullptr, 10);
11980 break;
11981 case TID_BOOL:
11982 if (data_str[0] == 'y' || data_str[0] == 'Y' ||
11983 data_str[0] == 't' || data_str[0] == 'T' || atoi(data_str) > 0)
11984 *((BOOL *) data + i) = 1;
11985 else
11986 *((BOOL *) data + i) = 0;
11987 break;
11988 case TID_FLOAT:
11989 if (data_str[0] == 'n' || data_str[0] == 'N')
11990 *((float *) data + i) = (float) ss_nan();
11991 else
11992 *((float *) data + i) = (float) atof(data_str);
11993 break;
11994 case TID_DOUBLE:
11995 if (data_str[0] == 'n' || data_str[0] == 'N')
11996 *((double *) data + i) = ss_nan();
11997 else
11998 *((double *) data + i) = atof(data_str);
11999 break;
12000 case TID_BITFIELD:
12001 if (hex)
12002 value = strtoul(data_str, nullptr, 16);
12003 else
12004 value = strtoul(data_str, nullptr, 10);
12005
12006 *((DWORD *) data + i) = value;
12007 break;
12008 case TID_STRING:
12009 case TID_LINK:
12010 strcpy((char *) data, data_str);
12011 *data_size = strlen(data_str) + 1;
12012 break;
12013 }
12014
12015 return DB_SUCCESS;
12016}
12017
12018/*------------------------------------------------------------------*/
12019
12020#ifdef LOCAL_ROUTINES
12021
12022static void db_recurse_record_tree_locked(const DATABASE* pdb, const KEY* pkey, void **data, INT * total_size, INT base_align, INT * max_align, BOOL bSet, INT convert_flags, db_err_msg** msg)
12023/********************************************************************\
12024
12025 Routine: db_recurse_record_tree_locked
12026
12027 Purpose: Recurse a database tree and calculate its size or copy
12028 data. Internal use only.
12029
12030\********************************************************************/
12031{
12032 const KEY *pold;
12033 INT size, align, corr, total_size_tmp;
12034
12035 const DATABASE_HEADER* pheader = pdb->database_header;
12036
12037 const KEYLIST *pkeylist = (const KEYLIST *) ((const char *) pheader + pkey->data);
12038 if (!pkeylist->first_key)
12039 return;
12040
12041 // FIXME: validate pkeylist->first_key
12042 // FIXME: confusing reuse of pkey
12043 pkey = (const KEY *) ((char *) pheader + pkeylist->first_key);
12044
12045 /* first browse through this level */
12046 do {
12047 pold = NULL;
12048
12049 if (pkey->type == TID_LINK) {
12050 const KEY *plink = db_resolve_link_locked(pheader, pkey, NULL, msg);
12051
12052 if (!plink)
12053 return;
12054
12055 if (plink->type == TID_KEY) {
12056 db_recurse_record_tree_locked(pdb, plink, data, total_size, base_align, NULL, bSet, convert_flags, msg);
12057 } else {
12058 pold = pkey;
12059 pkey = plink;
12060 }
12061 }
12062
12063 if (pkey->type != TID_KEY) {
12064 /* correct for alignment */
12065 align = 1;
12066
12067 if (rpc_tid_size(pkey->type))
12068 align = rpc_tid_size(pkey->type) < base_align ? rpc_tid_size(pkey->type) : base_align;
12069
12070 if (max_align && align > *max_align)
12071 *max_align = align;
12072
12073 corr = VALIGN(*total_size, align) - *total_size;
12074 *total_size += corr;
12075 if (data)
12076 *data = (void *) ((char *) (*data) + corr);
12077
12078 /* calculate data size */
12079 size = pkey->item_size * pkey->num_values;
12080
12081 if (data) {
12082 if (bSet) {
12083 KEY* wpkey = (KEY*)pkey;
12084 /* copy data if there is write access */
12085 if (pkey->access_mode & MODE_WRITE) {
12086 memcpy((char *) pheader + pkey->data, *data, pkey->item_size * pkey->num_values);
12087
12088 /* convert data */
12089 if (convert_flags) {
12090 if (pkey->num_values > 1)
12091 rpc_convert_data((char *) pheader + pkey->data,
12092 pkey->type, RPC_FIXARRAY, pkey->item_size * pkey->num_values, convert_flags);
12093 else
12094 rpc_convert_single((char *) pheader + pkey->data, pkey->type, 0, convert_flags);
12095 }
12096
12097 /* update time */
12098 wpkey->last_written = ss_time();
12099
12100 /* notify clients which have key open */
12101 db_notify_clients_locked(pdb, db_pkey_to_hkey(pheader, pkey), -1, TRUE, msg);
12102 }
12103 } else {
12104 /* copy key data if there is read access */
12105 if (pkey->access_mode & MODE_READ) {
12106 memcpy(*data, (char *) pheader + pkey->data, pkey->item_size * pkey->num_values);
12107
12108 /* convert data */
12109 if (convert_flags) {
12110 if (pkey->num_values > 1)
12111 rpc_convert_data(*data, pkey->type,
12113 pkey->item_size * pkey->num_values, convert_flags);
12114 else
12115 rpc_convert_single(*data, pkey->type, RPC_OUTGOING, convert_flags);
12116 }
12117 }
12118 }
12119
12120 *data = (char *) (*data) + size;
12121 }
12122
12123 *total_size += size;
12124 } else {
12125 /* align new substructure according to the maximum
12126 align value in this structure */
12127 align = 1;
12128
12129 total_size_tmp = *total_size;
12130 db_recurse_record_tree_locked(pdb, pkey, NULL, &total_size_tmp, base_align, &align, bSet, convert_flags, msg);
12131
12132 if (max_align && align > *max_align)
12133 *max_align = align;
12134
12135 corr = VALIGN(*total_size, align) - *total_size;
12136 *total_size += corr;
12137 if (data)
12138 *data = (void *) ((char *) (*data) + corr);
12139
12140 /* now recurse subtree */
12141 db_recurse_record_tree_locked(pdb, pkey, data, total_size, base_align, NULL, bSet, convert_flags, msg);
12142
12143 corr = VALIGN(*total_size, align) - *total_size;
12144 *total_size += corr;
12145 if (data)
12146 *data = (void *) ((char *) (*data) + corr);
12147 }
12148
12149 if (pold) {
12150 pkey = pold;
12151 pold = NULL;
12152 }
12153
12154 if (!pkey->next_key)
12155 break;
12156
12157 // FIXME: validate pkey->next_key
12158 pkey = (KEY *) ((char *) pheader + pkey->next_key);
12159 } while (TRUE);
12160}
12161
12162static void db_recurse_record_tree_locked(const DATABASE* pdb, HNDLE hKey, void **data, INT * total_size, INT base_align, INT * max_align, BOOL bSet, INT convert_flags, db_err_msg** msg)
12163/********************************************************************\
12164
12165 Routine: db_recurse_record_tree_locked
12166
12167 Purpose: Recurse a database tree and calculate its size or copy
12168 data. Internal use only.
12169
12170\********************************************************************/
12171{
12172 /* get first subkey of hKey */
12173
12174 const KEY* pkey = db_get_pkey(pdb->database_header, hKey, NULL, "db_recurse_record_tree", msg);
12175
12176 if (!pkey) {
12177 return;
12178 }
12179
12180 db_recurse_record_tree_locked(pdb, pkey, data, total_size, base_align, max_align, bSet, convert_flags, msg);
12181}
12182
12183#endif /* LOCAL_ROUTINES */
12184
12186#endif /* DOXYGEN_SHOULD_SKIP_THIS */
12187
12188/********************************************************************/
12202{
12203 if (rpc_is_remote()) {
12204 align = ss_get_struct_align();
12205 return rpc_call(RPC_DB_GET_RECORD_SIZE, hDB, hKey, align, buf_size);
12206 }
12207#ifdef LOCAL_ROUTINES
12208 {
12209 KEY key;
12210 INT status, max_align;
12211
12212 if (!align)
12213 align = ss_get_struct_align();
12214
12215 /* check if key has subkeys */
12217 if (status != DB_SUCCESS)
12218 return status;
12219
12220 if (key.type != TID_KEY) {
12221 /* just a single key */
12222 *buf_size = key.item_size * key.num_values;
12223 return DB_SUCCESS;
12224 }
12225
12226 db_err_msg* msg = NULL;
12228
12229 DATABASE *pdb = &_database[hDB - 1];
12230
12231 /* determine record size */
12232 *buf_size = max_align = 0;
12233 db_recurse_record_tree_locked(pdb, hKey, NULL, buf_size, align, &max_align, 0, 0, &msg);
12234
12235 //int tmp = *buf_size;
12236 /* correct for byte padding */
12237 *buf_size = VALIGN(*buf_size, max_align);
12238
12239 //if (tmp != *buf_size) {
12240 // cm_msg(MERROR, "db_get_record_size", "ODB record \"%s\" has unexpected padding from %d to %d bytes", db_get_path(hDB, hKey).c_str(), tmp, *buf_size);
12241 //}
12242
12244 if (msg)
12245 db_flush_msg(&msg);
12246 }
12247#endif /* LOCAL_ROUTINES */
12248
12249 return DB_SUCCESS;
12250}
12251
12252/********************************************************************/
12297INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align)
12298{
12299 if (rpc_is_remote()) {
12300 align = ss_get_struct_align();
12301 return rpc_call(RPC_DB_GET_RECORD, hDB, hKey, data, buf_size, align);
12302 }
12303#ifdef LOCAL_ROUTINES
12304 {
12305 KEY key;
12306 INT convert_flags, status;
12307 INT total_size;
12308 void *pdata;
12309
12310 if (data && buf_size) {
12311 memset(data, 0x00, *buf_size);
12312 }
12313
12314 convert_flags = 0;
12315
12316 if (!align)
12317 align = ss_get_struct_align();
12318 else {
12319 /* only convert data if called remotely, as indicated by align != 0 */
12320 if (rpc_is_mserver()) {
12321 convert_flags = rpc_get_convert_flags();
12322 }
12323 }
12324
12325 /* check if key has subkeys */
12327 if (status != DB_SUCCESS)
12328 return status;
12329
12330 if (key.type != TID_KEY) {
12331 /* copy single key */
12332 if (key.item_size * key.num_values != *buf_size) {
12333 cm_msg(MERROR, "db_get_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d * %d = %d)",
12336 }
12337
12338 db_get_data(hDB, hKey, data, buf_size, key.type);
12339
12340 if (convert_flags) {
12341 if (key.num_values > 1)
12343 else
12344 rpc_convert_single(data, key.type, RPC_OUTGOING, convert_flags);
12345 }
12346
12347 return DB_SUCCESS;
12348 }
12349
12350 /* check record size */
12351 db_get_record_size(hDB, hKey, align, &total_size);
12352 if (total_size != *buf_size) {
12353 cm_msg(MERROR, "db_get_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d)", db_get_path(hDB, hKey).c_str(), *buf_size, total_size);
12355 }
12356
12357 /* get subkey data */
12358 pdata = data;
12359 total_size = 0;
12360
12361 db_err_msg* msg = NULL;
12363 DATABASE *pdb = &_database[hDB - 1];
12364 db_recurse_record_tree_locked(pdb, hKey, &pdata, &total_size, align, NULL, FALSE, convert_flags, &msg);
12366 if (msg)
12367 db_flush_msg(&msg);
12368
12369 //if (total_size != *buf_size) {
12370 // cm_msg(MERROR, "db_get_record", "struct size mismatch for \"%s\", expected size: %d, read from ODB: %d bytes", db_get_path(hDB, hKey).c_str(), *buf_size, total_size);
12371 // //return DB_STRUCT_SIZE_MISMATCH;
12372 //}
12373 }
12374#endif /* LOCAL_ROUTINES */
12375
12376 return DB_SUCCESS;
12377}
12378
12379/********************************************************************/
12395INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align, const char *rec_str)
12396{
12397 int size = *buf_size;
12398 int odb_size = 0;
12399 int status;
12400 //char path[MAX_ODB_PATH];
12401
12402 /* check record size first */
12403
12405 if (status != DB_SUCCESS)
12406 return status;
12407
12408 /* if size mismatch, call repair function */
12409
12410 if (odb_size != size) {
12411 std::string path = db_get_path(hDB, hKey);
12412 cm_msg(MINFO, "db_get_record1", "Fixing ODB \"%s\" struct size mismatch (expected %d, odb size %d)", path.c_str(), size, odb_size);
12413 status = db_create_record(hDB, hKey, "", rec_str);
12414 if (status != DB_SUCCESS)
12415 return status;
12416 }
12417
12418 /* run db_get_record(), if success, we are done */
12419
12420 status = db_get_record(hDB, hKey, data, buf_size, align);
12421 if (status == DB_SUCCESS)
12422 return status;
12423
12424 /* try repair with db_check_record() */
12425
12426 status = db_check_record(hDB, hKey, "", rec_str, TRUE);
12427 if (status != DB_SUCCESS)
12428 return status;
12429
12430 /* verify struct size again, because there can still be a mismatch if there
12431 * are extra odb entries at the end of the record as db_check_record()
12432 * seems to ignore all odb entries past the end of "rec_str". K.O.
12433 */
12434
12436 if (status != DB_SUCCESS)
12437 return status;
12438
12439 std::string path = db_get_path(hDB, hKey);
12440
12441 if (odb_size != size) {
12442 cm_msg(MERROR, "db_get_record1", "after db_check_record() still struct size mismatch (expected %d, odb size %d) of \"%s\", calling db_create_record()", size, odb_size, path.c_str());
12443 status = db_create_record(hDB, hKey, "", rec_str);
12444 if (status != DB_SUCCESS)
12445 return status;
12446 }
12447
12448 cm_msg(MERROR, "db_get_record1", "repaired struct size mismatch of \"%s\"", path.c_str());
12449
12450 *buf_size = size;
12451 status = db_get_record(hDB, hKey, data, buf_size, align);
12452
12453 return status;
12454}
12455
12456static int db_parse_record(const char* rec_str, const char** out_rec_str, char* title, int title_size, char* key_name, int key_name_size, int* tid, int* n_data, int* string_length)
12457{
12458 title[0] = 0;
12459 key_name[0] = 0;
12460 *tid = 0;
12461 *n_data = 0;
12462 *string_length = 0;
12463 *out_rec_str = NULL;
12464
12465 //
12466 // expected format of rec_str:
12467 //
12468 // title: "[.]",
12469 // numeric value: "example_int = INT : 3",
12470 // string value: "example_string = STRING : [20] /Runinfo/Run number",
12471 // array: "aaa = INT[10] : ...",
12472 // string array: "sarr = STRING[10] : [32] ",
12473 //
12474
12475 //printf("parse_rec_str: [%s]\n", rec_str);
12476
12477 while (*rec_str == '\n')
12478 rec_str++;
12479
12480 /* check if it is a section title */
12481 if (rec_str[0] == '[') {
12482 rec_str++;
12483
12484 title[0] = 0;
12485
12486 /* extract title and append '/' */
12487 mstrlcpy(title, rec_str, title_size);
12488 char* p = strchr(title, ']');
12489 if (p)
12490 *p = 0;
12491
12492 int len = strlen(title);
12493 if (len > 0) {
12494 if (title[len - 1] != '/')
12495 mstrlcat(title, "/", title_size);
12496 }
12497
12498 // skip to the next end-of-line
12499 const char* pend = strchr(rec_str, '\n');
12500 if (pend)
12501 rec_str = pend;
12502 else
12503 rec_str = rec_str+strlen(rec_str);
12504
12505 while (*rec_str == '\n')
12506 rec_str++;
12507
12508 *out_rec_str = rec_str;
12509 return DB_SUCCESS;
12510 }
12511
12512 if (rec_str[0] == ';') {
12513 // skip to the next end-of-line
12514 const char* pend = strchr(rec_str, '\n');
12515 if (pend)
12516 rec_str = pend;
12517 else
12518 rec_str = rec_str+strlen(rec_str);
12519
12520 while (*rec_str == '\n')
12521 rec_str++;
12522
12523 *out_rec_str = rec_str;
12524 return DB_SUCCESS;
12525 }
12526
12527 const char* peq = strchr(rec_str, '=');
12528 if (!peq) {
12529 cm_msg(MERROR, "db_parse_record", "do not see \'=\'");
12530 return DB_INVALID_PARAM;
12531 }
12532
12533 int key_name_len = peq - rec_str;
12534
12535 // remove trailing equals sign and trailing spaces
12536 while (key_name_len > 1) {
12537 if (rec_str[key_name_len-1] == '=') {
12538 key_name_len--;
12539 continue;
12540 }
12541 if (rec_str[key_name_len-1] == ' ') {
12542 key_name_len--;
12543 continue;
12544 }
12545 break;
12546 }
12547
12548 memcpy(key_name, rec_str, key_name_len);
12549 key_name[key_name_len] = 0;
12550
12551 rec_str = peq + 1; // consume the "=" sign
12552
12553 while (*rec_str == ' ') // consume spaces
12554 rec_str++;
12555
12556 // extract type id
12557 char stid[256];
12558 int i;
12559 for (i=0; i<(int)sizeof(stid)-1; i++) {
12560 char s = *rec_str;
12561 if (s == 0) break;
12562 if (s == ' ') break;
12563 if (s == '\n') break;
12564 if (s == '[') break;
12565 stid[i] = s;
12566 rec_str++;
12567 }
12568 stid[i] = 0;
12569
12570 DWORD xtid = 0;
12571 for (xtid = 0; xtid < TID_LAST; xtid++) {
12572 if (strcmp(rpc_tid_name(xtid), stid) == 0) {
12573 *tid = xtid;
12574 break;
12575 }
12576 }
12577
12578 //printf("tid [%s], tid %d\n", stid, *tid);
12579
12580 if (xtid == TID_LAST) {
12581 cm_msg(MERROR, "db_parse_record", "do not see \':\'");
12582 return DB_INVALID_PARAM;
12583 }
12584
12585 while (*rec_str == ' ') // consume spaces
12586 rec_str++;
12587
12588 *n_data = 1;
12589
12590 if (*rec_str == '[') {
12591 // decode array size
12592 rec_str++; // cosume the '['
12593 *n_data = atoi(rec_str);
12594 const char *pbr = strchr(rec_str, ']');
12595 if (!pbr) {
12596 cm_msg(MERROR, "db_parse_record", "do not see closing bracket \']\'");
12597 return DB_INVALID_PARAM;
12598 }
12599 rec_str = pbr + 1; // skip the closing bracket
12600 }
12601
12602 while (*rec_str == ' ') // consume spaces
12603 rec_str++;
12604
12605 const char* pcol = strchr(rec_str, ':');
12606 if (!pcol) {
12607 cm_msg(MERROR, "db_parse_record", "do not see \':\'");
12608 return DB_INVALID_PARAM;
12609 }
12610
12611 rec_str = pcol + 1; // skip the ":"
12612
12613 while (*rec_str == ' ') // consume spaces
12614 rec_str++;
12615
12616 *string_length = 0;
12617 if (xtid == TID_LINK || xtid == TID_STRING) {
12618 // extract string length
12619 const char* pbr = strchr(rec_str, '[');
12620 if (pbr) {
12621 *string_length = atoi(pbr+1);
12622 }
12623 }
12624
12625 // skip to the next end-of-line
12626 const char* pend = strchr(rec_str, '\n');
12627 if (pend)
12628 rec_str = pend;
12629 else
12630 rec_str = rec_str+strlen(rec_str);
12631
12632 while (*rec_str == '\n')
12633 rec_str++;
12634
12635 *out_rec_str = rec_str;
12636 return DB_SUCCESS;
12637}
12638
12639static int db_get_record2_read_element(HNDLE hDB, HNDLE hKey, const char* key_name, int tid, int n_data, int string_length, char* buf_start, char** buf_ptr, int* buf_remain, BOOL correct)
12640{
12641 assert(tid > 0);
12642 assert(n_data > 0);
12643 int tsize = rpc_tid_size(tid);
12644 int offset = *buf_ptr - buf_start;
12645 int align = 0;
12646 if (tsize && (offset%tsize != 0)) {
12647 while (offset%tsize != 0) {
12648 align++;
12649 *(*buf_ptr) = 0xFF; // pad bytes for correct data alignement
12650 (*buf_ptr)++;
12651 (*buf_remain)--;
12652 offset++;
12653 }
12654 }
12655 printf("read element [%s] tid %d, n_data %d, string_length %d, tid_size %d, align %d, offset %d, buf_remain %d\n", key_name, tid, n_data, string_length, tsize, align, offset, *buf_remain);
12656 if (tsize > 0) {
12657 int xsize = tsize*n_data;
12658 if (xsize > *buf_remain) {
12659 cm_msg(MERROR, "db_get_record2", "buffer overrun at key \"%s\", size %d, buffer remaining %d", key_name, xsize, *buf_remain);
12660 return DB_INVALID_PARAM;
12661 }
12662 int ysize = xsize;
12663 int status = db_get_value(hDB, hKey, key_name, *buf_ptr, &ysize, tid, FALSE);
12664 //printf("status %d, xsize %d\n", status, xsize);
12665 if (status != DB_SUCCESS) {
12666 cm_msg(MERROR, "db_get_record2", "cannot read \"%s\", db_get_value() status %d", key_name, status);
12667 memset(*buf_ptr, 0, xsize);
12668 *buf_ptr += xsize;
12669 *buf_remain -= xsize;
12670 return status;
12671 }
12672 *buf_ptr += xsize;
12673 *buf_remain -= xsize;
12674 } else if (tid == TID_STRING) {
12675 int xstatus = 0;
12676 int i;
12677 for (i=0; i<n_data; i++) {
12678 int xsize = string_length;
12679 if (xsize > *buf_remain) {
12680 cm_msg(MERROR, "db_get_record2", "string buffer overrun at key \"%s\" index %d, size %d, buffer remaining %d", key_name, i, xsize, *buf_remain);
12681 return DB_INVALID_PARAM;
12682 }
12683 std::string xkey_name = msprintf("%s[%d]", key_name, i);
12684 int status = db_get_value(hDB, hKey, xkey_name.c_str(), *buf_ptr, &xsize, tid, FALSE);
12685 //printf("status %d, string length %d, xsize %d, actual len %d\n", status, string_length, xsize, (int)strlen(*buf_ptr));
12686 if (status == DB_TRUNCATED) {
12687 // make sure string is NUL terminated
12688 (*buf_ptr)[string_length-1] = 0;
12689 cm_msg(MERROR, "db_get_record2", "string key \"%s\" index %d, string value was truncated", key_name, i);
12690 } else if (status != DB_SUCCESS) {
12691 cm_msg(MERROR, "db_get_record2", "cannot read string \"%s\"[%d], db_get_value() status %d", key_name, i, status);
12692 memset(*buf_ptr, 0, string_length);
12693 xstatus = status;
12694 }
12695 *buf_ptr += string_length;
12696 *buf_remain -= string_length;
12697 }
12698 if (xstatus != 0) {
12699 return xstatus;
12700 }
12701 } else {
12702 cm_msg(MERROR, "db_get_record2", "cannot read key \"%s\" of unsupported type %d", key_name, tid);
12703 return DB_INVALID_PARAM;
12704 }
12705 return DB_SUCCESS;
12706}
12707
12708/********************************************************************/
12758INT db_get_record2(HNDLE hDB, HNDLE hKey, void *data, INT * xbuf_size, INT align, const char *rec_str, BOOL correct)
12759{
12760 int status = DB_SUCCESS;
12761
12762 printf("db_get_record2!\n");
12763
12764 assert(data != NULL);
12765 assert(xbuf_size != NULL);
12766 assert(*xbuf_size > 0);
12767 assert(correct == 0);
12768
12769 int truncated = 0;
12770#if 1
12771 char* r1 = NULL;
12772 int rs = *xbuf_size;
12773 if (1) {
12774 r1 = (char*)malloc(rs);
12775 assert(r1 != NULL);
12776 memset(data, 0xFF, *xbuf_size);
12777 memset(r1, 0xFF, rs);
12778 //status = db_get_record1(hDB, hKey, r1, &rs, 0, rec_str);
12779 status = db_get_record(hDB, hKey, r1, &rs, 0);
12780 printf("db_get_record status %d\n", status);
12781 }
12782#endif
12783
12784 char* buf_start = (char*)data;
12785 int buf_size = *xbuf_size;
12786
12787 char* buf_ptr = buf_start;
12788 int buf_remain = buf_size;
12789
12790 while (rec_str && *rec_str != 0) {
12791 char title[256];
12792 char key_name[MAX_ODB_PATH];
12793 int tid = 0;
12794 int n_data = 0;
12795 int string_length = 0;
12796 const char* rec_str_next = NULL;
12797
12798 status = db_parse_record(rec_str, &rec_str_next, title, sizeof(title), key_name, sizeof(key_name), &tid, &n_data, &string_length);
12799
12800 //printf("parse [%s], status %d, title [%s], key_name [%s], tid %d, n_data %d, string_length %d, next [%s]\n", rec_str, status, title, key_name, tid, n_data, string_length, rec_str_next);
12801
12802 rec_str = rec_str_next;
12803
12804 if (status != DB_SUCCESS) {
12805 return status;
12806 }
12807
12808 if (key_name[0] == 0) {
12809 // skip title strings, comments, etc
12810 continue;
12811 }
12812
12813 status = db_get_record2_read_element(hDB, hKey, key_name, tid, n_data, string_length, buf_start, &buf_ptr, &buf_remain, correct);
12814 if (status == DB_INVALID_PARAM) {
12815 cm_msg(MERROR, "db_get_record2", "error: cannot continue reading odb record because of previous fatal error, status %d", status);
12816 return DB_INVALID_PARAM;
12817 } if (status != DB_SUCCESS) {
12818 truncated = 1;
12819 }
12820
12821 rec_str = rec_str_next;
12822 }
12823
12824 if (r1) {
12825 int ok = -1;
12826 int i;
12827 for (i=0; i<rs; i++) {
12828 if (r1[i] != buf_start[i]) {
12829 ok = i;
12830 break;
12831 }
12832 }
12833 if (ok>=0 || buf_remain>0) {
12834 printf("db_get_record2: miscompare at %d out of %d, buf_remain %d\n", ok, rs, buf_remain);
12835 } else {
12836 printf("db_get_record2: check ok\n");
12837 }
12838 }
12839
12840 if (buf_remain > 0) {
12841 // FIXME: we finished processing the data definition string, but unused space remains in the buffer
12842 return DB_TRUNCATED;
12843 }
12844
12845 if (truncated)
12846 return DB_TRUNCATED;
12847 else
12848 return DB_SUCCESS;
12849}
12850
12851/********************************************************************/
12881INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
12882{
12883 if (rpc_is_remote()) {
12884 align = ss_get_struct_align();
12885 return rpc_call(RPC_DB_SET_RECORD, hDB, hKey, data, buf_size, align);
12886 }
12887#ifdef LOCAL_ROUTINES
12888 {
12889 KEY key;
12890 INT convert_flags;
12891 INT total_size;
12892 void *pdata;
12893
12894 convert_flags = 0;
12895
12896 if (!align)
12897 align = ss_get_struct_align();
12898 else {
12899 /* only convert data if called remotely, as indicated by align != 0 */
12900 if (rpc_is_mserver()) {
12901 convert_flags = rpc_get_convert_flags();
12902 }
12903 }
12904
12905 /* check if key has subkeys */
12906 db_get_key(hDB, hKey, &key);
12907 if (key.type != TID_KEY) {
12908 /* copy single key */
12909 if (key.item_size * key.num_values != buf_size) {
12910 cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
12912 }
12913
12914 if (convert_flags) {
12915 if (key.num_values > 1)
12917 else
12918 rpc_convert_single(data, key.type, 0, convert_flags);
12919 }
12920
12922 return DB_SUCCESS;
12923 }
12924
12925 /* check record size */
12926 db_get_record_size(hDB, hKey, align, &total_size);
12927 if (total_size != buf_size) {
12928 cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
12930 }
12931
12932 /* set subkey data */
12933 pdata = data;
12934 total_size = 0;
12935
12937 db_err_msg* msg = NULL;
12938 DATABASE *pdb = &_database[hDB - 1];
12939 db_allow_write_locked(pdb, "db_set_record");
12940 db_recurse_record_tree_locked(pdb, hKey, &pdata, &total_size, align, NULL, TRUE, convert_flags, &msg);
12942 if (msg)
12943 db_flush_msg(&msg);
12944 }
12945#endif /* LOCAL_ROUTINES */
12946
12947 return DB_SUCCESS;
12948}
12949
12951#ifndef DOXYGEN_SHOULD_SKIP_THIS
12952
12953/*------------------------------------------------------------------*/
12955/********************************************************************\
12956
12957 Routine: db_add_open_record
12958
12959 Purpose: Server part of db_open_record. Internal use only.
12960
12961\********************************************************************/
12962{
12963 if (rpc_is_remote())
12964 return rpc_call(RPC_DB_ADD_OPEN_RECORD, hDB, hKey, access_mode);
12965
12966#ifdef LOCAL_ROUTINES
12967 {
12968 INT i;
12969 int status;
12970
12971 if (hDB > _database_entries || hDB <= 0) {
12972 cm_msg(MERROR, "db_add_open_record", "invalid database handle");
12973 return DB_INVALID_HANDLE;
12974 }
12975
12976 db_err_msg* msg = NULL;
12977
12978 /* lock database */
12980
12981 DATABASE *pdb = &_database[hDB - 1];
12982 DATABASE_HEADER *pheader = pdb->database_header;
12984
12985 /* check if key is already open */
12986 for (i = 0; i < pclient->max_index; i++) {
12987 if (pclient->open_record[i].handle == hKey)
12988 break;
12989 }
12990
12991 if (i < pclient->max_index) {
12993 return DB_SUCCESS;
12994 }
12995
12996 /* not found, search free entry */
12997 for (i = 0; i < pclient->max_index; i++) {
12998 if (pclient->open_record[i].handle == 0)
12999 break;
13000 }
13001
13002 /* check if maximum number reached */
13003 if (i == MAX_OPEN_RECORDS) {
13005 return DB_NO_MEMORY;
13006 }
13007
13008 db_allow_write_locked(pdb, "db_add_open_record");
13009
13010 KEY *pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_add_open_record", &msg);
13011
13012 if (!pkey) {
13014 if (msg)
13015 db_flush_msg(&msg);
13016 return status;
13017 }
13018
13019 if (i == pclient->max_index)
13020 pclient->max_index++;
13021
13022 pclient->open_record[i].handle = hKey;
13023 pclient->open_record[i].access_mode = access_mode;
13024
13025 /* increment notify_count */
13026 pkey->notify_count++;
13027
13028 pclient->num_open_records++;
13029
13030 /* set exclusive bit if requested */
13031 if (access_mode & MODE_WRITE)
13033
13035 }
13036#endif /* LOCAL_ROUTINES */
13037
13038 return DB_SUCCESS;
13039}
13040
13041#ifdef LOCAL_ROUTINES
13042
13044{
13045 int status = DB_SUCCESS;
13046
13048
13049 /* search key */
13050 int idx;
13051 for (idx = 0; idx < pclient->max_index; idx++)
13052 if (pclient->open_record[idx].handle == hKey)
13053 break;
13054
13055 if (idx == pclient->max_index) {
13056 return DB_INVALID_HANDLE;
13057 }
13058
13059 KEY* pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_remove_open_record_wlocked", NULL);
13060
13061 if (!pkey)
13062 return status;
13063
13064 /* decrement notify_count */
13065
13066 if (pkey->notify_count > 0)
13067 pkey->notify_count--;
13068
13069 pclient->num_open_records--;
13070
13071 /* remove exclusive flag */
13072 if (pclient->open_record[idx].access_mode & MODE_WRITE)
13073 db_set_mode_wlocked(pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2, NULL);
13074
13075 memset(&pclient->open_record[idx], 0, sizeof(OPEN_RECORD));
13076
13077 /* calculate new max_index entry */
13078 int i;
13079 for (i = pclient->max_index - 1; i >= 0; i--)
13080 if (pclient->open_record[i].handle != 0)
13081 break;
13082 pclient->max_index = i + 1;
13083
13084 return DB_SUCCESS;
13085}
13086#endif // LOCAL_ROUTINES
13087
13088
13090/********************************************************************\
13091
13092 Routine: db_remove_open_record
13093
13094 Purpose: Gets called by db_close_record. Internal use only.
13095
13096\********************************************************************/
13097{
13098 if (rpc_is_remote())
13100
13101 int status = DB_SUCCESS;
13102
13103#ifdef LOCAL_ROUTINES
13104 {
13105 if (hDB > _database_entries || hDB <= 0) {
13106 cm_msg(MERROR, "db_remove_open_record", "invalid database handle %d", hDB);
13107 return DB_INVALID_HANDLE;
13108 }
13109
13111
13112 DATABASE *pdb = &_database[hDB - 1];
13113 DATABASE_HEADER *pheader = pdb->database_header;
13114
13115 db_allow_write_locked(pdb, "db_remove_open_record");
13116
13117 status = db_remove_open_record_wlocked(pdb, pheader, hKey);
13118
13120 }
13121#endif /* LOCAL_ROUTINES */
13122
13123 return status;
13124}
13125
13126/*------------------------------------------------------------------*/
13127
13128#ifdef LOCAL_ROUTINES
13129
13130static INT db_notify_clients_locked(const DATABASE* pdb, HNDLE hKeyMod, int index, BOOL bWalk, db_err_msg** msg)
13131/********************************************************************\
13132
13133 Routine: db_notify_clients
13134
13135 Purpose: Gets called by db_set_xxx functions. Internal use only.
13136
13137\********************************************************************/
13138{
13139 HNDLE hKey;
13140 KEYLIST *pkeylist;
13141 INT i, j;
13142 int status;
13143
13144 const DATABASE_HEADER* pheader = pdb->database_header;
13145
13146 hKey = hKeyMod;
13147
13148 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_notify_clients", msg);
13149
13150 if (!pkey) {
13151 return status;
13152 }
13153
13154 do {
13155
13156 /* check which client has record open */
13157 if (pkey->notify_count)
13158 for (i = 0; i < pheader->max_client_index; i++) {
13159 const DATABASE_CLIENT* pclient = &pheader->client[i];
13160 for (j = 0; j < pclient->max_index; j++)
13161 if (pclient->open_record[j].handle == hKey) {
13162 /* send notification to remote process */
13163 std::string str = msprintf("O %d %d %d %d", pdb->hDB, hKey, hKeyMod, index);
13164 ss_resume(pclient->port, str.c_str());
13165 }
13166 }
13167
13168 if (pkey->parent_keylist == 0 || !bWalk)
13169 return DB_SUCCESS;
13170
13171 // FIXME: validate pkey->parent_keylist
13172 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
13173 pkey = db_get_pkey(pheader, pkeylist->parent, &status, "db_notify_clients", msg);
13174 if (!pkey) {
13175 return status;
13176 }
13177 hKey = db_pkey_to_hkey(pheader, pkey);
13178 } while (TRUE);
13179
13180}
13181
13182#endif /* LOCAL_ROUTINES */
13183/*------------------------------------------------------------------*/
13184
13185
13187/********************************************************************\
13188
13189 Routine: db_notify_clients
13190
13191 Purpose: Gets called by db_set_xxx functions. Internal use only.
13192 \********************************************************************/
13193{
13194 if (rpc_is_remote()) {
13195 cm_msg(MERROR, "db_notify_clients", "db_notify_clients() does not work in remotely connected MIDAS clients");
13196 return DB_INVALID_HANDLE;
13197 }
13198
13199#ifdef LOCAL_ROUTINES
13200 {
13201 db_err_msg* msg = NULL;
13203 if (status != DB_SUCCESS)
13204 return status;
13205 DATABASE* pdb = &_database[hDB - 1];
13206 db_notify_clients_locked(pdb, hKeyMod, index, bWalk, &msg);
13208 if (msg)
13209 db_flush_msg(&msg);
13210 }
13211#endif
13212 return DB_SUCCESS;
13213}
13214
13216/********************************************************************\
13217
13218 Routine: db_notify_clients_array
13219
13220 Purpose: This function is typically called after a set of calls
13221 to db_set_data1 which omits hot-link notification to
13222 programs. After several ODB values are modified in a set,
13223 this function has to be called to trigger the hot-links
13224 of the whole set.
13225
13226 \********************************************************************/
13227{
13228 if (rpc_is_remote())
13229 return rpc_call(RPC_DB_NOTIFY_CLIENTS_ARRAY, hDB, hKeys, size);
13230
13231#ifdef LOCAL_ROUTINES
13232 {
13234 if (status != DB_SUCCESS)
13235 return status;
13236 db_err_msg* msg = NULL;
13237 DATABASE* pdb = &_database[hDB - 1];
13238 int count = size/sizeof(INT);
13239 for (int i=0 ; i<count; i++) {
13240 db_notify_clients_locked(pdb, hKeys[i], -1, TRUE, &msg);
13241 }
13243 if (msg)
13244 db_flush_msg(&msg);
13245 }
13246#endif
13247 return DB_SUCCESS;
13248}
13249
13250/*------------------------------------------------------------------*/
13251static void merge_records(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
13252{
13253 char full_name[MAX_ODB_PATH];
13254 INT status, size;
13255 HNDLE hKeyInit;
13256 KEY initkey, key;
13257
13258 /* avoid compiler warnings */
13259 status = level;
13260
13261 /* compose name of init key */
13262 std::string s = db_get_path(hDB, hKey);
13263 mstrlcpy(full_name, s.c_str(), sizeof(full_name));
13264 *strchr(full_name, 'O') = 'I';
13265
13266 /* if key in init record found, copy original data to init data */
13267 status = db_find_key(hDB, 0, full_name, &hKeyInit);
13268 if (status == DB_SUCCESS) {
13269 status = db_get_key(hDB, hKeyInit, &initkey);
13270 if (status != DB_SUCCESS) {
13271 cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', db_get_key() status %d", full_name, status);
13272 return;
13273 }
13275 if (status != DB_SUCCESS) {
13276 cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', second db_get_key() status %d", full_name, status);
13277 return;
13278 }
13279
13280 if (initkey.type != TID_KEY && initkey.type == key.type) {
13281 char* allocbuffer = NULL;
13282 char stackbuffer[10000];
13283 char* buffer = stackbuffer;
13284 size = sizeof(stackbuffer);
13285 while (1) {
13286 /* copy data from original key to new key */
13287 status = db_get_data(hDB, hKey, buffer, &size, initkey.type);
13288 if (status == DB_SUCCESS) {
13289 status = db_set_data(hDB, hKeyInit, buffer, initkey.total_size, initkey.num_values, initkey.type);
13290 if (status != DB_SUCCESS) {
13291 cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', db_set_data() status %d", full_name, status);
13292 return;
13293 }
13294 break;
13295 }
13296 if (status == DB_TRUNCATED) {
13297 size *= 2;
13298 allocbuffer = (char *)realloc(allocbuffer, size);
13299 assert(allocbuffer != NULL);
13300 buffer = allocbuffer;
13301 continue;
13302 }
13303 cm_msg(MERROR, "merge_records", "aborting on unexpected failure of db_get_data(%s), status %d", full_name, status);
13304 abort();
13305 }
13306 if (allocbuffer)
13307 free(allocbuffer);
13308 }
13309 } else if (status == DB_NO_KEY) {
13310 /* do nothing */
13311 } else if (status == DB_INVALID_LINK) {
13312 status = db_find_link(hDB, 0, full_name, &hKeyInit);
13313 if (status == DB_SUCCESS) {
13314 size = sizeof(full_name);
13315 status = db_get_data(hDB, hKeyInit, full_name, &size, TID_LINK);
13316 }
13317 cm_msg(MERROR, "merge_records", "Invalid link \"%s\"", full_name);
13318 } else {
13319 cm_msg(MERROR, "merge_records", "aborting on unexpected failure of db_find_key(%s), status %d", full_name, status);
13320 abort();
13321 }
13322}
13323
13324static int _global_open_count; // FIXME: this is not thread-safe
13325
13326static void check_open_keys(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
13327{
13328 if (pkey->notify_count)
13330}
13331
13333#endif /* DOXYGEN_SHOULD_SKIP_THIS */
13334
13335/********************************************************************/
13392INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
13393{
13394 char key_name[256], *buffer;
13395 INT status, size, buffer_size;
13396 HNDLE hKeyTmp, hKeyTmpO, hKeyOrig, hSubkey;
13397
13398 if (rpc_is_remote())
13399 return rpc_call(RPC_DB_CREATE_RECORD, hDB, hKey, orig_key_name, init_str);
13400
13401 /* make this function atomic */
13403
13404 /* strip trailing '/' */
13405 mstrlcpy(key_name, orig_key_name, sizeof(key_name));
13406 if (strlen(key_name) > 1 && key_name[strlen(key_name) - 1] == '/')
13407 key_name[strlen(key_name) - 1] = 0;
13408
13409 /* merge temporay record and original record */
13410 status = db_find_key(hDB, hKey, key_name, &hKeyOrig);
13411 if (status == DB_SUCCESS) {
13412 assert(hKeyOrig != 0);
13413#ifdef CHECK_OPEN_RECORD
13414 /* check if key or subkey is opened */
13415 _global_open_count = 0; // FIXME: this is not thread safe
13416 db_scan_tree_link(hDB, hKeyOrig, 0, check_open_keys, NULL);
13417 if (_global_open_count) {
13419 return DB_OPEN_RECORD;
13420 }
13421#endif
13422 /* create temporary records */
13423 std::string str = "/System/Tmp/" + ss_tid_to_string(ss_gettid()) + "I";
13424 //printf("db_create_record str [%s]\n", str);
13425 db_delete(hDB, 0, str.c_str());
13426 db_create_key(hDB, 0, str.c_str(), TID_KEY);
13427 status = db_find_key(hDB, 0, str.c_str(), &hKeyTmp);
13428 if (status != DB_SUCCESS) {
13430 return status;
13431 }
13432
13433 str = "/System/Tmp/" + ss_tid_to_string(ss_gettid()) + "O";
13434 //printf("db_create_record str [%s]\n", str);
13435 db_delete(hDB, 0, str.c_str());
13436 db_create_key(hDB, 0, str.c_str(), TID_KEY);
13437 status = db_find_key(hDB, 0, str.c_str(), &hKeyTmpO);
13438 if (status != DB_SUCCESS) {
13440 return status;
13441 }
13442
13443 status = db_paste(hDB, hKeyTmp, init_str);
13444 if (status != DB_SUCCESS) {
13446 return status;
13447 }
13448
13449 buffer_size = 10000;
13450 buffer = (char *) malloc(buffer_size);
13451 assert(buffer != NULL);
13452
13453 do {
13454 size = buffer_size;
13455 status = db_copy(hDB, hKeyOrig, buffer, &size, "");
13456 if (status == DB_TRUNCATED) {
13457 buffer_size += 10000;
13458 buffer = (char *) realloc(buffer, buffer_size);
13459 assert(buffer != NULL);
13460 continue;
13461 }
13462 if (status != DB_SUCCESS) {
13463 free(buffer);
13465 return status;
13466 }
13467
13468 } while (status != DB_SUCCESS);
13469
13470 status = db_paste(hDB, hKeyTmpO, buffer);
13471 if (status != DB_SUCCESS) {
13472 free(buffer);
13474 return status;
13475 }
13476
13477 /* merge temporay record and original record */
13478 db_scan_tree_link(hDB, hKeyTmpO, 0, merge_records, NULL);
13479
13480 /* delete original record */
13481 while (1) {
13482 db_enum_link(hDB, hKeyOrig, 0, &hSubkey);
13483 if (!hSubkey)
13484 break;
13485
13487 if (status != DB_SUCCESS) {
13488 free(buffer);
13490 return status;
13491 }
13492 }
13493
13494 /* copy merged record to original record */
13495 do {
13496 size = buffer_size;
13497 status = db_copy(hDB, hKeyTmp, buffer, &size, "");
13498 if (status == DB_TRUNCATED) {
13499 buffer_size += 10000;
13500 buffer = (char *) realloc(buffer, buffer_size);
13501 continue;
13502 }
13503 if (status != DB_SUCCESS) {
13504 free(buffer);
13506 return status;
13507 }
13508
13509 } while (status != DB_SUCCESS);
13510
13511 status = db_paste(hDB, hKeyOrig, buffer);
13512 if (status != DB_SUCCESS) {
13513 free(buffer);
13515 return status;
13516 }
13517
13518 /* delete temporary records */
13519 db_delete_key(hDB, hKeyTmp);
13520 db_delete_key(hDB, hKeyTmpO);
13521
13522 free(buffer);
13523 buffer = NULL;
13524 } else if (status == DB_NO_KEY) {
13525 /* create fresh record */
13526 db_create_key(hDB, hKey, key_name, TID_KEY);
13527 status = db_find_key(hDB, hKey, key_name, &hKeyTmp);
13528 if (status != DB_SUCCESS) {
13530 return status;
13531 }
13532
13533 status = db_paste(hDB, hKeyTmp, init_str);
13534 if (status != DB_SUCCESS) {
13536 return status;
13537 }
13538 } else {
13539 cm_msg(MERROR, "db_create_record", "aborting on unexpected failure of db_find_key(%s), status %d", key_name, status);
13540 abort();
13541 }
13542
13544
13545 return DB_SUCCESS;
13546}
13547
13548/********************************************************************/
13564INT db_check_record(HNDLE hDB, HNDLE hKey, const char *keyname, const char *rec_str, BOOL correct)
13565{
13566 char line[MAX_STRING_LENGTH];
13567 char title[MAX_STRING_LENGTH];
13568 char key_name[MAX_STRING_LENGTH];
13569 char info_str[MAX_STRING_LENGTH + 50];
13570 char *pc;
13571 const char *pold, *rec_str_orig;
13572 DWORD tid;
13573 INT i, j, n_data, string_length, status;
13574 HNDLE hKeyRoot, hKeyTest;
13575 KEY key;
13576 int bad_string_length;
13577
13578 if (rpc_is_remote())
13579 return rpc_call(RPC_DB_CHECK_RECORD, hDB, hKey, keyname, rec_str, correct);
13580
13581 /* check if record exists */
13582 status = db_find_key(hDB, hKey, keyname, &hKeyRoot);
13583
13584 /* create record if not */
13585 if (status == DB_NO_KEY) {
13586 if (correct)
13587 return db_create_record(hDB, hKey, keyname, rec_str);
13588 return DB_NO_KEY;
13589 }
13590
13591 assert(hKeyRoot);
13592
13593 title[0] = 0;
13594 rec_str_orig = rec_str;
13595
13596 db_get_key(hDB, hKeyRoot, &key);
13597 if (key.type == TID_KEY)
13598 /* get next subkey which is not of type TID_KEY */
13599 db_get_next_link(hDB, hKeyRoot, &hKeyTest);
13600 else
13601 /* test root key itself */
13602 hKeyTest = hKeyRoot;
13603
13604 if (hKeyTest == 0 && *rec_str != 0) {
13605 if (correct)
13606 return db_create_record(hDB, hKey, keyname, rec_str_orig);
13607
13608 return DB_STRUCT_MISMATCH;
13609 }
13610
13611 do {
13612 if (*rec_str == 0)
13613 break;
13614
13615 for (i = 0; *rec_str != '\n' && *rec_str && i < MAX_STRING_LENGTH; i++)
13616 line[i] = *rec_str++;
13617
13618 if (i == MAX_STRING_LENGTH) {
13619 cm_msg(MERROR, "db_check_record", "line too long");
13620 return DB_TRUNCATED;
13621 }
13622
13623 line[i] = 0;
13624 if (*rec_str == '\n')
13625 rec_str++;
13626
13627 /* check if it is a section title */
13628 if (line[0] == '[') {
13629 /* extract title and append '/' */
13630 strcpy(title, line + 1);
13631 if (strchr(title, ']'))
13632 *strchr(title, ']') = 0;
13633 if (title[0] && title[strlen(title) - 1] != '/')
13634 strcat(title, "/");
13635 } else {
13636 /* valid data line if it includes '=' and no ';' */
13637 if (strchr(line, '=') && line[0] != ';') {
13638 /* copy type info and data */
13639 pc = strchr(line, '=') + 1;
13640 while (*pc == ' ')
13641 pc++;
13642 strcpy(info_str, pc);
13643
13644 /* extract key name */
13645 *strchr(line, '=') = 0;
13646
13647 pc = &line[strlen(line) - 1];
13648 while (*pc == ' ')
13649 *pc-- = 0;
13650
13651 mstrlcpy(key_name, line, sizeof(key_name));
13652
13653 /* evaluate type info */
13654 strcpy(line, info_str);
13655 if (strchr(line, ' '))
13656 *strchr(line, ' ') = 0;
13657
13658 n_data = 1;
13659 if (strchr(line, '[')) {
13660 n_data = atol(strchr(line, '[') + 1);
13661 *strchr(line, '[') = 0;
13662 }
13663
13664 for (tid = 0; tid < TID_LAST; tid++)
13665 if (strcmp(rpc_tid_name(tid), line) == 0)
13666 break;
13667 if (tid == TID_LAST) {
13668 for (tid = 0; tid < TID_LAST; tid++)
13669 if (strcmp(rpc_tid_name_old(tid), line) == 0)
13670 break;
13671 }
13672
13673 string_length = 0;
13674
13675 if (tid == TID_LAST)
13676 cm_msg(MERROR, "db_check_record", "found unknown data type \"%s\" in ODB file", line);
13677 else {
13678 /* skip type info */
13679 pc = info_str;
13680 while (*pc != ' ' && *pc)
13681 pc++;
13682 while ((*pc == ' ' || *pc == ':') && *pc)
13683 pc++;
13684
13685 if (n_data > 1) {
13686 info_str[0] = 0;
13687 if (!*rec_str)
13688 break;
13689
13690 for (j = 0; *rec_str != '\n' && *rec_str; j++)
13691 info_str[j] = *rec_str++;
13692 info_str[j] = 0;
13693 if (*rec_str == '\n')
13694 rec_str++;
13695 }
13696
13697 for (i = 0; i < n_data; i++) {
13698 /* strip trailing \n */
13699 pc = &info_str[strlen(info_str) - 1];
13700 while (*pc == '\n' || *pc == '\r')
13701 *pc-- = 0;
13702
13703 if (tid == TID_STRING || tid == TID_LINK) {
13704 if (!string_length) {
13705 if (info_str[1] == '=')
13706 string_length = -1;
13707 else {
13708 pc = strchr(info_str, '[');
13709 if (pc != NULL)
13710 string_length = atoi(pc + 1);
13711 else
13712 string_length = -1;
13713 }
13714 if (string_length > MAX_STRING_LENGTH) {
13715 string_length = MAX_STRING_LENGTH;
13716 cm_msg(MERROR, "db_check_record", "found string exceeding MAX_STRING_LENGTH");
13717 }
13718 }
13719
13720 if (string_length == -1) {
13721 /* multi-line string */
13722 if (strstr(rec_str, "\n====#$@$#====\n") != NULL) {
13723 string_length = (POINTER_T) strstr(rec_str, "\n====#$@$#====\n") - (POINTER_T) rec_str + 1;
13724
13725 rec_str = strstr(rec_str, "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
13726 } else
13727 cm_msg(MERROR, "db_check_record", "found multi-line string without termination sequence");
13728 } else {
13729 if (strchr(info_str, ']'))
13730 pc = strchr(info_str, ']') + 1;
13731 else
13732 pc = info_str + 2;
13733 while (*pc && *pc != ' ')
13734 pc++;
13735 while (*pc && *pc == ' ')
13736 pc++;
13737
13738 /* limit string size */
13739 *(pc + string_length - 1) = 0;
13740 }
13741 } else {
13742 pc = info_str;
13743
13744 if (n_data > 1 && info_str[0] == '[') {
13745 pc = strchr(info_str, ']') + 1;
13746 while (*pc && *pc == ' ')
13747 pc++;
13748 }
13749 }
13750
13751 if (i < n_data - 1) {
13752 info_str[0] = 0;
13753 if (!*rec_str)
13754 break;
13755
13756 pold = rec_str;
13757
13758 for (j = 0; *rec_str != '\n' && *rec_str; j++)
13759 info_str[j] = *rec_str++;
13760 info_str[j] = 0;
13761 if (*rec_str == '\n')
13762 rec_str++;
13763
13764 /* test if valid data */
13765 if (tid != TID_STRING && tid != TID_LINK) {
13766 if (info_str[0] == 0 || (strchr(info_str, '=')
13767 && strchr(info_str, ':')))
13768 rec_str = pold;
13769 }
13770 }
13771 }
13772
13773 /* if rec_str contains key, but not ODB, return mismatch */
13774 if (!hKeyTest) {
13775 if (correct)
13776 return db_create_record(hDB, hKey, keyname, rec_str_orig);
13777
13778 return DB_STRUCT_MISMATCH;
13779 }
13780
13781 status = db_get_key(hDB, hKeyTest, &key);
13782 assert(status == DB_SUCCESS);
13783
13784 bad_string_length = 0;
13785
13786 if (key.type == TID_STRING) {
13787 //printf("key name [%s], tid %d/%d, num_values %d/%d, string length %d/%d\n", key_name, key.type, tid, key.num_values, n_data, string_length, key.item_size);
13788 if (string_length > 0 && string_length != key.item_size) {
13789 bad_string_length = 1;
13790 }
13791 }
13792
13793 /* check rec_str vs. ODB key */
13794 if (!equal_ustring(key.name, key_name) || key.type != tid || key.num_values != n_data || bad_string_length) {
13795 //printf("miscompare key name [%s], tid %d/%d, num_values %d/%d, string length %d/%d\n", key_name, key.type, tid, key.num_values, n_data, key.item_size, string_length);
13796 if (correct)
13797 return db_create_record(hDB, hKey, keyname, rec_str_orig);
13798
13799 return DB_STRUCT_MISMATCH;
13800 }
13801
13802 /* get next key in ODB */
13803 db_get_next_link(hDB, hKeyTest, &hKeyTest);
13804 }
13805 }
13806 }
13807 } while (TRUE);
13808
13809 return DB_SUCCESS;
13810}
13811
13812/********************************************************************/
13883INT db_open_record(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size,
13884 WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info)
13885{
13886 INT idx, status, size;
13887 KEY key;
13888 void *data;
13889
13890 /* allocate new space for the local record list */
13891 if (_record_list_entries == 0) {
13892 _record_list = (RECORD_LIST *) malloc(sizeof(RECORD_LIST));
13893 assert(_record_list != NULL);
13894 memset(_record_list, 0, sizeof(RECORD_LIST));
13895 if (_record_list == NULL) {
13896 cm_msg(MERROR, "db_open_record", "not enough memory");
13897 return DB_NO_MEMORY;
13898 }
13899
13901 idx = 0;
13902 } else {
13903 /* check for a deleted entry */
13904 for (idx = 0; idx < _record_list_entries; idx++)
13905 if (!_record_list[idx].handle)
13906 break;
13907
13908 /* if not found, create new one */
13909 if (idx == _record_list_entries) {
13911 if (_record_list == NULL) {
13912 cm_msg(MERROR, "db_open_record", "not enough memory");
13913 return DB_NO_MEMORY;
13914 }
13915
13916 memset(&_record_list[_record_list_entries], 0, sizeof(RECORD_LIST));
13917
13919 }
13920 }
13921
13922 db_get_key(hDB, hKey, &key);
13923
13924 /* check record size */
13925 status = db_get_record_size(hDB, hKey, 0, &size);
13926 if (status != DB_SUCCESS) {
13928 cm_msg(MERROR, "db_open_record", "cannot get record size, db_get_record_size() status %d", status);
13929 return DB_NO_MEMORY;
13930 }
13931
13932 if (size != rec_size && ptr != NULL) {
13934 std::string path = db_get_path(hDB, hKey);
13935 cm_msg(MERROR, "db_open_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d)", path.c_str(), rec_size, size);
13937 }
13938
13939 /* check for read access */
13940 if (((key.access_mode & MODE_EXCLUSIVE) && (access_mode & MODE_WRITE))
13941 || (!(key.access_mode & MODE_WRITE) && (access_mode & MODE_WRITE))
13942 || (!(key.access_mode & MODE_READ) && (access_mode & MODE_READ))) {
13944 return DB_NO_ACCESS;
13945 }
13946
13947 if (access_mode & MODE_ALLOC) {
13948 data = malloc(size);
13949
13950 if (data == NULL) {
13952 cm_msg(MERROR, "db_open_record", "not enough memory, malloc(%d) returned NULL", size);
13953 return DB_NO_MEMORY;
13954 }
13955
13956 memset(data, 0, size);
13957
13958 *((void **) ptr) = data;
13959 } else {
13960 data = ptr;
13961 }
13962
13963 /* copy record to local memory */
13964 if (access_mode & MODE_READ && data != NULL) {
13965 status = db_get_record(hDB, hKey, data, &size, 0);
13966 if (status != DB_SUCCESS) {
13968 cm_msg(MERROR, "db_open_record", "cannot get record, db_get_record() status %d", status);
13969 return DB_NO_MEMORY;
13970 }
13971 }
13972
13973 /* copy local record to ODB */
13974 if (access_mode & MODE_WRITE) {
13975 /* only write to ODB if not in MODE_ALLOC */
13976 if ((access_mode & MODE_ALLOC) == 0) {
13977 status = db_set_record(hDB, hKey, data, size, 0);
13978 if (status != DB_SUCCESS) {
13980 cm_msg(MERROR, "db_open_record", "cannot set record, db_set_record() status %d", status);
13981 return DB_NO_MEMORY;
13982 }
13983 }
13984
13985 /* init a local copy of the record */
13986 _record_list[idx].copy = malloc(size);
13987 if (_record_list[idx].copy == NULL) {
13988 cm_msg(MERROR, "db_open_record", "not enough memory");
13989 return DB_NO_MEMORY;
13990 }
13991
13992 memcpy(_record_list[idx].copy, data, size);
13993 }
13994
13995 /* initialize record list */
13996 _record_list[idx].handle = hKey;
13997 _record_list[idx].hDB = hDB;
13998 _record_list[idx].access_mode = access_mode;
13999 _record_list[idx].data = data;
14000 _record_list[idx].buf_size = size;
14001 _record_list[idx].dispatcher = dispatcher;
14002 _record_list[idx].info = info;
14003
14004 /* add record entry in database structure */
14005 return db_add_open_record(hDB, hKey, (WORD) (access_mode & ~MODE_ALLOC));
14006}
14007
14008/********************************************************************/
14034INT db_open_record1(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size,
14035 WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info,
14036 const char *rec_str)
14037{
14038 if (rec_str) {
14039 int status;
14040 if (rec_size) {
14041 char* pbuf;
14042 int size = rec_size;
14043 pbuf = (char*)malloc(size);
14044 assert(pbuf != NULL);
14045 status = db_get_record1(hDB, hKey, pbuf, &size, 0, rec_str);
14046 free(pbuf);
14047 if (status != DB_SUCCESS)
14048 return status;
14049 }
14050
14051 status = db_check_record(hDB, hKey, "", rec_str, TRUE);
14052 if (status != DB_SUCCESS)
14053 return status;
14054 }
14055
14056 return db_open_record(hDB, hKey, ptr, rec_size, access_mode, dispatcher, info);
14057}
14058
14059/********************************************************************/
14067{
14068#ifdef LOCAL_ROUTINES
14069 {
14070 INT i;
14071
14072 for (i = 0; i < _record_list_entries; i++)
14073 if (_record_list[i].handle == hKey && _record_list[i].hDB == hDB)
14074 break;
14075
14076 if (i == _record_list_entries)
14077 return DB_INVALID_HANDLE;
14078
14079 /* remove record entry from database structure */
14081
14082 /* free local memory */
14083 if (_record_list[i].access_mode & MODE_ALLOC) {
14084 free(_record_list[i].data);
14085 _record_list[i].data = NULL;
14086 }
14087
14088 if (_record_list[i].access_mode & MODE_WRITE) {
14089 free(_record_list[i].copy);
14090 _record_list[i].copy = NULL;
14091 }
14092
14093 memset(&_record_list[i], 0, sizeof(RECORD_LIST));
14094 }
14095#endif /* LOCAL_ROUTINES */
14096
14097 return DB_SUCCESS;
14098}
14099
14100/********************************************************************/
14108{
14109 INT i;
14110
14111 for (i = 0; i < _record_list_entries; i++) {
14112 if (_record_list[i].handle) {
14113 if (_record_list[i].access_mode & MODE_WRITE) {
14114 free(_record_list[i].copy);
14115 _record_list[i].copy = NULL;
14116 }
14117
14118 if (_record_list[i].access_mode & MODE_ALLOC) {
14119 free(_record_list[i].data);
14120 _record_list[i].data = NULL;
14121 }
14122
14123 memset(&_record_list[i], 0, sizeof(RECORD_LIST));
14124 }
14125 }
14126
14127 if (_record_list_entries > 0) {
14129 free(_record_list);
14130 _record_list = NULL;
14131 }
14132
14133 return DB_SUCCESS;
14134}
14135
14136/********************************************************************/
14146{
14147 INT i, size, status;
14148
14150
14151 /* check all record entries for matching key */
14152 for (i = 0; i < _record_list_entries; i++)
14153 if (_record_list[i].handle == hKeyRoot) {
14155
14156 /* get updated data if record not opened in write mode */
14157 if ((_record_list[i].access_mode & MODE_WRITE) == 0) {
14158 size = _record_list[i].buf_size;
14159 if (_record_list[i].data != NULL) {
14160 status = db_get_record(hDB, hKeyRoot, _record_list[i].data, &size, 0); // db_open_record() update
14161 //printf("db_open_record update status %d, size %d %d\n", status, _record_list[i].buf_size, size);
14162 }
14163
14164 /* call dispatcher if requested */
14165 if (_record_list[i].dispatcher)
14167 }
14168 }
14169
14170 /* check all watch entries for matching key */
14171 for (i = 0; i < _watch_list_entries; i++)
14172 if (_watch_list[i].handle == hKeyRoot) {
14174
14175 /* call dispatcher if requested */
14176 if (_watch_list[i].dispatcher)
14178 }
14179
14180 return status;
14181}
14182
14183/********************************************************************/
14192INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
14193{
14194 char buffer[32];
14195
14196 int convert_flags = rpc_get_convert_flags();
14197
14198 NET_COMMAND* nc = (NET_COMMAND *) buffer;
14199
14201 nc->header.param_size = 4 * sizeof(INT);
14202 *((INT *) nc->param) = hDB;
14203 *((INT *) nc->param + 1) = hKeyRoot;
14204 *((INT *) nc->param + 2) = hKey;
14205 *((INT *) nc->param + 3) = index;
14206
14207 if (convert_flags) {
14210 rpc_convert_single(&nc->param[0], TID_UINT32, RPC_OUTGOING, convert_flags);
14211 rpc_convert_single(&nc->param[4], TID_UINT32, RPC_OUTGOING, convert_flags);
14212 rpc_convert_single(&nc->param[8], TID_UINT32, RPC_OUTGOING, convert_flags);
14213 rpc_convert_single(&nc->param[12], TID_UINT32, RPC_OUTGOING, convert_flags);
14214 }
14215
14216 /* send the update notification to the client */
14217 send_tcp(client_socket, buffer, sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT), 0);
14218
14219 return DB_SUCCESS;
14220}
14221
14222/********************************************************************/
14371{
14372 INT i;
14373
14374 for (i = 0; i < _record_list_entries; i++)
14375 if (_record_list[i].access_mode & MODE_WRITE) {
14376 if (memcmp(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size) != 0) {
14377 if (rpc_is_remote()) {
14378 int align = ss_get_struct_align();
14380 } else {
14382 }
14383 memcpy(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size);
14384 }
14385 }
14386
14387 return DB_SUCCESS;
14388}
14389
14390/*------------------------------------------------------------------*/
14391
14392/********************************************************************/
14406INT db_watch(HNDLE hDB, HNDLE hKey, void (*dispatcher) (INT, INT, INT, void*), void* info)
14407{
14408 INT idx, status;
14409 KEY key;
14410
14411 /* check for valid key */
14412 assert(hKey);
14413
14414 /* allocate new space for the local record list */
14415 if (_watch_list_entries == 0) {
14416 _watch_list = (WATCH_LIST *) malloc(sizeof(WATCH_LIST));
14417 assert(_watch_list != NULL);
14418 memset(_watch_list, 0, sizeof(WATCH_LIST));
14419 if (_watch_list == NULL) {
14420 cm_msg(MERROR, "db_watch", "not enough memory");
14421 return DB_NO_MEMORY;
14422 }
14423
14425 idx = 0;
14426 } else {
14427 /* check for a deleted entry */
14428 for (idx = 0; idx < _watch_list_entries; idx++)
14429 if (!_watch_list[idx].handle)
14430 break;
14431
14432 /* if not found, create new one */
14433 if (idx == _watch_list_entries) {
14434 _watch_list = (WATCH_LIST *) realloc(_watch_list, sizeof(WATCH_LIST) * (_watch_list_entries + 1));
14435 if (_watch_list == NULL) {
14436 cm_msg(MERROR, "db_watch", "not enough memory");
14437 return DB_NO_MEMORY;
14438 }
14439
14440 memset(&_watch_list[_watch_list_entries], 0, sizeof(WATCH_LIST));
14441
14443 }
14444 }
14445
14446 /* check key */
14448 if (status != DB_SUCCESS) {
14450 std::string path = db_get_path(hDB, hKey);
14451 cm_msg(MERROR, "db_watch", "cannot get key \"%s\"", path.c_str());
14452 return DB_NO_MEMORY;
14453 }
14454
14455 /* check for read access */
14456 if (!(key.access_mode & MODE_READ)) {
14458 std::string path = db_get_path(hDB, hKey);
14459 cm_msg(MERROR, "db_watch", "cannot get key \"%s\"", path.c_str());
14460 return DB_NO_ACCESS;
14461 }
14462
14463 /* initialize record list */
14464 _watch_list[idx].handle = hKey;
14465 _watch_list[idx].hDB = hDB;
14466 _watch_list[idx].dispatcher = dispatcher;
14467 _watch_list[idx].info = info;
14468
14469 /* add record entry in database structure */
14471}
14472
14473
14474/********************************************************************/
14482{
14483#ifdef LOCAL_ROUTINES
14484 {
14485 INT i;
14486
14487 for (i = 0; i < _watch_list_entries; i++)
14488 if (_watch_list[i].handle == hKey && _watch_list[i].hDB == hDB)
14489 break;
14490
14491 if (i == _watch_list_entries)
14492 return DB_INVALID_HANDLE;
14493
14494 /* remove record entry from database structure */
14496
14497 memset(&_watch_list[i], 0, sizeof(WATCH_LIST));
14498 }
14499#endif /* LOCAL_ROUTINES */
14500
14501 return DB_SUCCESS;
14502}
14503
14504/********************************************************************/
14512{
14513 for (int i = _watch_list_entries-1; i >= 0 ; i--) {
14514 if ((_watch_list[i].hDB == 0) && (_watch_list[i].handle == 0)) {
14515 // empty or deleted watch list entry
14516 continue;
14517 }
14519 }
14520
14521 return DB_SUCCESS;
14522}
14523
14524/*------------------------------------------------------------------*/
14525
14526/* C++ wrapper for db_get_value */
14527
14528INT EXPRT db_get_value_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int index, std::string* s, BOOL create, int create_string_length)
14529{
14530 int status;
14531 int hkey;
14532
14533 //printf("db_get_value_string: key_name [%s], index %d, string [%s], create %d, create_string_length %d\n", key_name, index, s->c_str(), create, create_string_length);
14534
14535 if (index > 0 && create) {
14536 cm_msg(MERROR, "db_get_value_string", "cannot resize odb string arrays, please use db_resize_string() instead");
14537 return DB_OUT_OF_RANGE;
14538 }
14539
14540 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
14541 if (status == DB_SUCCESS) {
14542 KEY key;
14543 status = db_get_key(hdb, hkey, &key);
14544 if (status != DB_SUCCESS)
14545 return status;
14546 if (index < 0 || index >= key.num_values)
14547 return DB_OUT_OF_RANGE;
14548 int size = key.item_size;
14549 if (size == 0) {
14550 if (s)
14551 *s = "";
14552 //printf("db_get_value_string: return empty string, item_size %d\n", key.item_size);
14553 return DB_SUCCESS;
14554 }
14555 char* buf = (char*)malloc(size);
14556 assert(buf != NULL);
14557 status = db_get_data_index(hdb, hkey, buf, &size, index, TID_STRING);
14558 if (status != DB_SUCCESS) {
14559 free(buf);
14560 return status;
14561 }
14562 if (s)
14563 *s = buf;
14564 free(buf);
14565 //printf("db_get_value_string: return [%s], len %d, item_size %d, size %d\n", s->c_str(), s->length(), key.item_size, size);
14566 return DB_SUCCESS;
14567 } else if (!create) {
14568 // does not exist and not asked to create it
14569 return status;
14570 } else {
14571 //printf("db_get_value_string: creating [%s]\n", key_name);
14572 status = db_create_key(hdb, hKeyRoot, key_name, TID_STRING);
14573 if (status != DB_SUCCESS)
14574 return status;
14575 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
14576 if (status != DB_SUCCESS)
14577 return status;
14578 assert(s != NULL);
14579 if (create_string_length == 0) {
14580 int size = s->length() + 1; // 1 byte for final \0
14581 status = db_set_data_index(hdb, hkey, s->c_str(), size, index, TID_STRING);
14582 } else {
14583 char* buf = (char*)malloc(create_string_length);
14584 assert(buf);
14585 mstrlcpy(buf, s->c_str(), create_string_length);
14586 status = db_set_data_index(hdb, hkey, buf, create_string_length, index, TID_STRING);
14587 free(buf);
14588 }
14589 if (status != DB_SUCCESS)
14590 return status;
14591 //printf("db_get_value_string: created with value [%s]\n", s->c_str());
14592 return DB_SUCCESS;
14593 }
14594 // NOT REACHED
14595}
14596
14597/* C++ wrapper for db_set_value */
14598
14599INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string* s)
14600{
14601 assert(s != NULL);
14602 int size = s->length() + 1; // 1 byte for final \0
14603 //printf("db_set_value_string: key_name [%s], string [%s], size %d\n", key_name, s->c_str(), size);
14604 return db_set_value(hDB, hKeyRoot, key_name, s->c_str(), size, 1, TID_STRING);
14605}
14606
14607/********************************************************************/
14619INT EXPRT db_resize_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int num_values, int max_string_length)
14620{
14621 int status;
14622 int hkey;
14623
14624 //printf("db_resize_string: key_name [%s], num_values %d, max_string_length %d\n", key_name, num_values, max_string_length);
14625
14626 int old_num_values = 0;
14627 int old_item_size = 0;
14628 int old_size = 0;
14629 char* old_data = NULL;
14630
14631 if (key_name) {
14632 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
14633 } else {
14634 hkey = hKeyRoot;
14636 }
14637 if (status == DB_SUCCESS) {
14638 KEY key;
14639 status = db_get_key(hdb, hkey, &key);
14640 if (status != DB_SUCCESS)
14641 return status;
14642 old_num_values = key.num_values;
14643 old_item_size = key.item_size;
14644 old_size = old_num_values * old_item_size;
14645 if (old_size > 0) {
14646 old_data = (char*)malloc(old_size);
14647 assert(old_data != NULL);
14648 int size = old_size;
14649 status = db_get_data(hdb, hkey, old_data, &size, TID_STRING);
14650 if (status != DB_SUCCESS) {
14651 free(old_data);
14652 return status;
14653 }
14654 assert(size == old_size);
14655 }
14656 } else {
14657 status = db_create_key(hdb, hKeyRoot, key_name, TID_STRING);
14658 if (status != DB_SUCCESS)
14659 return status;
14660 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
14661 if (status != DB_SUCCESS)
14662 return status;
14663 }
14664
14665 //printf("old_num_values %d, old_item_size %d, old_size %d\n", old_num_values, old_item_size, old_size);
14666
14667 int item_size = max_string_length;
14668
14669 if (item_size < 1)
14670 item_size = old_item_size;
14671
14672 if (num_values < 1)
14673 num_values = old_num_values;
14674
14675 int new_size = num_values * item_size;
14676 char* new_data = (char*)malloc(new_size);
14677 assert(new_data);
14678
14679 memset(new_data, 0, new_size);
14680
14681 if (old_data) {
14682 int num = old_num_values;
14683 if (num > num_values)
14684 num = num_values;
14685
14686 //printf("new num_values %d, item_size %d, new_size %d, old_size %d, to copy %d values\n", num_values, item_size, new_size, old_size, num);
14687
14688 for (int i=0; i<num; i++) {
14689 const char* old_ptr = old_data + i*old_item_size;
14690 char* new_ptr = new_data + i*item_size;
14691 mstrlcpy(new_ptr, old_ptr, item_size);
14692 }
14693 }
14694
14695 status = db_set_data(hdb, hkey, new_data, new_size, num_values, TID_STRING);
14696
14697 if (old_data)
14698 free(old_data);
14699 if (new_data)
14700 free(new_data);
14701
14702 return status;
14703}
14704
14705MJsonNode* db_scl(HNDLE hDB)
14706{
14707#ifdef LOCAL_ROUTINES
14708 MJsonNode* scl = MJsonNode::MakeObject();
14709 MJsonNode* clients = MJsonNode::MakeArray();
14710
14711 scl->AddToObject("now", MJsonNode::MakeNumber(ss_time_sec()));
14712 scl->AddToObject("clients", clients);
14713
14714 /* lock database */
14716
14717 DATABASE *pdb = &_database[hDB - 1];
14718 DATABASE_HEADER *pheader = pdb->database_header;
14719
14720 DWORD now = ss_millitime();
14721
14722 /* list clients */
14723 for (int i = 0; i < pheader->max_client_index; i++) {
14724 DATABASE_CLIENT *pclient = &pheader->client[i];
14725 if (pclient->pid) {
14726 MJsonNode* c = MJsonNode::MakeObject();
14727 c->AddToObject("slot", MJsonNode::MakeNumber(i));
14728 c->AddToObject("pid", MJsonNode::MakeNumber(pclient->pid));
14729 c->AddToObject("name", MJsonNode::MakeString(pclient->name));
14730 std::string path = msprintf("/System/Clients/%d/Host", pclient->pid);
14731 const KEY* pkey = db_find_pkey_locked(pheader, NULL, path.c_str(), true, NULL, NULL);
14732 if (pkey) {
14733 int host_size = pkey->total_size;
14734 char* host = (char*)malloc(host_size);
14735 assert(host != NULL);
14736 db_get_data_locked(pheader, pkey, 0, host, &host_size, TID_STRING, NULL);
14737 c->AddToObject("host", MJsonNode::MakeString(host));
14738 free(host);
14739 }
14740 c->AddToObject("watchdog_timeout_millisec", MJsonNode::MakeNumber(pclient->watchdog_timeout));
14741 // "now" and "last_activity" is millisecond time wrapped around at 32 bits, must do unsigned 32-bit math here, not in javascript
14742 c->AddToObject("last_activity_millisec", MJsonNode::MakeNumber(now - pclient->last_activity));
14743 clients->AddToArray(c);
14744 }
14745 }
14746
14748
14749 return scl;
14750#else
14751 return MJsonNode::MakeNull();
14752#endif
14753}
14754
14755MJsonNode* db_sor(HNDLE hDB, const char* root_path)
14756{
14757 //printf("db_sor(%s)\n", root_path);
14758 assert(root_path != NULL);
14759#ifdef LOCAL_ROUTINES
14760 size_t root_path_len = strlen(root_path);
14761 MJsonNode* sor = MJsonNode::MakeArray();
14762
14763 /* lock database */
14765
14766 DATABASE *pdb = &_database[hDB - 1];
14767 DATABASE_HEADER *pheader = pdb->database_header;
14768
14769 /* list clients */
14770 for (int i = 0; i < pheader->max_client_index; i++) {
14771 DATABASE_CLIENT *pclient = &pheader->client[i];
14772 if (pclient->pid) {
14773 for (int j = 0; j < pclient->max_index; j++) {
14774 std::string path = db_get_path_locked(pheader, pclient->open_record[j].handle);
14775 if (path.length() < root_path_len)
14776 continue;
14777 if (strncmp(root_path, path.c_str(), root_path_len) != 0)
14778 continue;
14779 MJsonNode* c = MJsonNode::MakeObject();
14780 c->AddToObject("name", MJsonNode::MakeString(pclient->name));
14781 //c->AddToObject("handle", MJsonNode::MakeNumber(pclient->open_record[j].handle));
14782 c->AddToObject("access_mode", MJsonNode::MakeNumber(pclient->open_record[j].access_mode));
14783 c->AddToObject("flags", MJsonNode::MakeNumber(pclient->open_record[j].flags));
14784 c->AddToObject("path", MJsonNode::MakeString(path.c_str()));
14785 sor->AddToArray(c);
14786 }
14787 }
14788 }
14789
14791
14792 return sor;
14793#else
14794 return MJsonNode::MakeNull();
14795#endif
14796}
14797
/* end of odbfunctionc */
14799
14800/* emacs
14801 * Local Variables:
14802 * tab-width: 8
14803 * c-basic-offset: 3
14804 * indent-tabs-mode: nil
14805 * End:
14806 */
#define FALSE
Definition cfortran.h:309
#define EXPRT
Definition esone.h:28
INT cm_get_watchdog_params(BOOL *call_watchdog, DWORD *timeout)
Definition midas.cxx:3341
#define CM_NO_CLIENT
Definition midas.h:584
#define DB_STRUCT_MISMATCH
Definition midas.h:655
#define DB_OUT_OF_RANGE
Definition midas.h:652
#define DB_NO_SEMAPHORE
Definition midas.h:638
#define DB_FULL
Definition midas.h:641
#define DB_KEY_EXIST
Definition midas.h:642
#define DB_INVALID_HANDLE
Definition midas.h:636
#define DB_INVALID_PARAM
Definition midas.h:640
#define DB_FILE_ERROR
Definition midas.h:648
#define DB_NO_ACCESS
Definition midas.h:649
#define DB_SUCCESS
Definition midas.h:632
#define DB_NO_MEMORY
Definition midas.h:634
#define DB_INVALID_NAME
Definition midas.h:635
#define DB_NO_KEY
Definition midas.h:643
#define DB_STRUCT_SIZE_MISMATCH
Definition midas.h:650
#define DB_CORRUPTED
Definition midas.h:654
#define DB_TYPE_MISMATCH
Definition midas.h:646
#define DB_CREATED
Definition midas.h:633
#define DB_INVALID_LINK
Definition midas.h:653
#define DB_OPEN_RECORD
Definition midas.h:651
#define DB_NO_MORE_SUBKEYS
Definition midas.h:647
#define DB_VERSION_MISMATCH
Definition midas.h:657
#define DB_TRUNCATED
Definition midas.h:645
#define DB_NO_SLOT
Definition midas.h:637
#define SS_SUCCESS
Definition midas.h:664
#define SS_FILE_ERROR
Definition midas.h:670
#define SS_NO_MEMORY
Definition midas.h:666
#define SS_TIMEOUT
Definition midas.h:675
#define SS_CREATED
Definition midas.h:665
unsigned short int WORD
Definition mcstd.h:49
unsigned char BYTE
Definition mcstd.h:48
unsigned int DWORD
Definition mcstd.h:51
#define SUCCESS
Definition mcstd.h:54
#define TID_DOUBLE
Definition midas.h:343
#define TID_KEY
Definition midas.h:349
#define TID_BOOL
Definition midas.h:340
#define MODE_EXCLUSIVE
Definition midas.h:373
#define TID_UINT64
Definition midas.h:352
#define TID_INT64
Definition midas.h:351
#define MINFO
Definition midas.h:560
#define MODE_ALLOC
Definition midas.h:374
#define MODE_DELETE
Definition midas.h:372
#define MODE_WATCH
Definition midas.h:375
#define RPC_NO_REPLY
Definition midas.h:396
#define TID_STRUCT
Definition midas.h:348
#define TID_INT32
Definition midas.h:339
#define TID_UINT8
Definition midas.h:328
#define TID_LINK
Definition midas.h:350
#define TID_BITFIELD
Definition midas.h:345
#define TID_STRING
Definition midas.h:346
#define MODE_WRITE
Definition midas.h:371
#define MERROR
Definition midas.h:559
#define MODE_READ
Definition midas.h:370
#define TID_INT8
Definition midas.h:330
#define TID_ARRAY
Definition midas.h:347
#define TID_CHAR
Definition midas.h:331
#define TID_UINT32
Definition midas.h:337
#define TID_UINT16
Definition midas.h:333
#define TID_INT16
Definition midas.h:335
#define TID_FLOAT
Definition midas.h:341
#define TID_LAST
Definition midas.h:354
#define VALIGN(adr, align)
Definition midas.h:526
#define MIN(a, b)
Definition midas.h:515
#define ALIGN8(x)
Definition midas.h:522
#define MAX_STRING_LENGTH
Definition msystem.h:113
#define O_TEXT
Definition msystem.h:227
#define MSG_ODB
Definition msystem.h:303
INT ss_get_struct_align()
Definition system.cxx:1321
INT ss_suspend_get_odb_port(INT *port)
Definition system.cxx:4406
bool ss_is_valid_utf8(const char *string)
Definition system.cxx:8153
INT ss_mutex_release(MUTEX_T *mutex)
Definition system.cxx:3236
int ss_isnan(double x)
Definition system.cxx:8046
INT ss_shm_flush(const char *name, const void *adr, size_t size, HNDLE handle, bool wait_for_thread)
Definition system.cxx:1178
DWORD ss_millitime()
Definition system.cxx:3472
INT ss_semaphore_create(const char *name, HNDLE *semaphore_handle)
Definition system.cxx:2532
INT ss_mutex_delete(MUTEX_T *mutex)
Definition system.cxx:3290
INT ss_getpid(void)
Definition system.cxx:1379
INT ss_mutex_create(MUTEX_T **mutex, BOOL recursive)
Definition system.cxx:3020
INT ss_shm_open(const char *name, INT size, void **adr, size_t *shm_size, HNDLE *handle, BOOL get_size)
Definition system.cxx:326
INT ss_semaphore_release(HNDLE semaphore_handle)
Definition system.cxx:2860
void ss_stack_history_entry(char *tag)
Definition system.cxx:8107
std::string ss_tid_to_string(midas_thread_t thread_id)
Definition system.cxx:1643
DWORD ss_time()
Definition system.cxx:3541
INT ss_resume(INT port, const char *message)
Definition system.cxx:4923
midas_thread_t ss_gettid(void)
Definition system.cxx:1591
INT ss_semaphore_delete(HNDLE semaphore_handle, INT destroy_flag)
Definition system.cxx:2948
double ss_time_sec()
Definition system.cxx:3546
double ss_nan()
Definition system.cxx:8025
INT ss_semaphore_wait_for(HNDLE semaphore_handle, DWORD timeout_millisec)
Definition system.cxx:2718
INT ss_shm_unprotect(HNDLE handle, void **adr, size_t shm_size, BOOL read, BOOL write, const char *caller_name)
Definition system.cxx:1062
INT ss_shm_close(const char *name, void *adr, size_t shm_size, HNDLE handle, INT destroy_flag)
Definition system.cxx:757
INT send_tcp(int sock, char *buffer, DWORD buffer_size, INT flags)
Definition system.cxx:5364
BOOL ss_pid_exists(int pid)
Definition system.cxx:1442
INT ss_shm_protect(HNDLE handle, void *adr, size_t shm_size)
Definition system.cxx:1005
INT ss_mutex_wait_for(MUTEX_T *mutex, INT timeout)
Definition system.cxx:3116
INT cm_msg_flush_buffer()
Definition midas.cxx:881
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:931
BOOL equal_ustring(const char *str1, const char *str2)
Definition odb.cxx:3780
static void free_key(DATABASE_HEADER *pheader, void *address, INT size)
Definition odb.cxx:356
INT db_sprintfh(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:11566
INT db_flush_database(HNDLE hDB)
Definition odb.cxx:2801
bool operator<(const keylist_struct &a, const keylist_struct &b)
Definition odb.cxx:711
static void json_write_key(HNDLE hDB, HNDLE hKey, const KEY *key, const char *link_path, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:10277
INT db_remove_open_record(HNDLE hDB, HNDLE hKey, BOOL lock)
Definition odb.cxx:13089
static void check_open_keys(HNDLE hDB, HNDLE hKey, KEY *pkey, INT level, void *info)
Definition odb.cxx:13326
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:7412
INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
Definition odb.cxx:4428
INT db_send_changed_records()
Definition odb.cxx:14370
INT db_find_link(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4788
INT db_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition odb.cxx:3638
static void * malloc_key(DATABASE_HEADER *pheader, INT size, const char *caller)
Definition odb.cxx:228
static INT db_set_data_index_wlocked(DATABASE_HEADER *pheader, KEY *pkey, int idx, const void *data, INT data_size, DWORD type, const char *caller, db_err_msg **msg)
Definition odb.cxx:7576
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5680
INT db_reorder_key(HNDLE hDB, HNDLE hKey, INT idx)
Definition odb.cxx:6880
static const KEY * db_get_parent(const DATABASE_HEADER *pheader, const KEY *pkey, int *pstatus, const char *caller, db_err_msg **msg)
Definition odb.cxx:1184
static int db_scan_tree_locked(const DATABASE_HEADER *pheader, const KEY *pkey, int level, int(*callback)(const DATABASE_HEADER *, const KEY *, int, void *, db_err_msg **), void *info, db_err_msg **msg)
INT db_save_json(HNDLE hDB, HNDLE hKey, const char *filename, int flags)
Definition odb.cxx:11109
static INT _record_list_entries
Definition odb.cxx:61
INT db_open_record(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size, WORD access_mode, void(*dispatcher)(INT, INT, void *), void *info)
Definition odb.cxx:13883
INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE *hDB, const char *client_name)
Definition odb.cxx:2315
static int db_paste_node(HNDLE hDB, HNDLE hKeyRoot, PMXML_NODE node)
Definition odb.cxx:9293
INT db_get_open_records(HNDLE hDB, HNDLE hKey, char *str, INT buf_size, BOOL fix)
Definition odb.cxx:5450
INT db_paste_xml(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
Definition odb.cxx:9511
static INT db_check_set_data_locked(DATABASE_HEADER *pheader, const KEY *pkey, const void *data, INT data_size, INT num_values, DWORD type, const char *caller, db_err_msg **msg)
Definition odb.cxx:7620
void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition odb.cxx:3406
static const KEY * db_resolve_link_locked(const DATABASE_HEADER *, const KEY *, int *pstatus, db_err_msg **)
Definition odb.cxx:1246
static bool db_validate_hkey(const DATABASE_HEADER *pheader, HNDLE hKey)
Definition odb.cxx:1073
std::string strcomb1(const char **list)
Definition odb.cxx:684
static void db_print_hkey(const DATABASE_HEADER *pheader, HNDLE hkey, int recurse=0, const char *path=NULL, HNDLE parenthkeylist=0)
Definition odb.cxx:1289
static void * malloc_data(DATABASE_HEADER *pheader, INT size)
Definition odb.cxx:439
static int db_save_json_key_obsolete(HNDLE hDB, HNDLE hKey, INT level, char **buffer, int *buffer_size, int *buffer_end, int save_keys, int follow_links, int recurse)
Definition odb.cxx:10329
INT db_save_xml(HNDLE hDB, HNDLE hKey, const char *filename)
Definition odb.cxx:9998
static int db_delete_key_locked(DATABASE *pdb, const KEY *pkey, int level, db_err_msg **msg)
Definition odb.cxx:4277
INT db_set_link_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7944
static INT print_key_info_locked(const DATABASE_HEADER *pheader, const KEY *pkey, INT level, void *info, db_err_msg **msg)
Definition odb.cxx:793
static const KEY * db_find_pkey_locked(const DATABASE_HEADER *pheader, const KEY *pkey, const char *key_name, bool follow_links, int *pstatus, db_err_msg **msg)
Definition odb.cxx:4537
INT db_get_path(HNDLE hDB, HNDLE hKey, char *path, INT buf_size)
Definition odb.cxx:5270
static bool check_free_list_repair(db_err_msg **msg)
Definition odb.cxx:1867
int json_write_bare_subdir(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int level, int flags, time_t timestamp)
Definition odb.cxx:10936
INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align, const char *rec_str)
Definition odb.cxx:12395
static int db_create_key_wlocked(DATABASE_HEADER *pheader, KEY *parentKey, const char *key_name, DWORD type, KEY **pnewkey, db_err_msg **msg)
Definition odb.cxx:3944
INT db_copy(HNDLE hDB, HNDLE hKey, char *buffer, INT *buffer_size, const char *path)
Definition odb.cxx:8725
INT db_set_link_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:8268
BOOL strmatch(char *pattern, char *str)
Definition odb.cxx:3814
INT EXPRT db_enum(HNDLE hDB, HNDLE hKeyRoot, std::vector< HNDLE > *sub_handles, std::vector< KEY > *sub_keys, bool follow_links)
Definition odb.cxx:6263
std::string db_show_mem(HNDLE hDB, BOOL verbose)
Definition odb.cxx:812
INT db_get_record_size(HNDLE hDB, HNDLE hKey, INT align, INT *buf_size)
Definition odb.cxx:12201
static INT db_check_set_data_index_locked(DATABASE_HEADER *pheader, const KEY *pkey, int idx, const void *data, INT data_size, DWORD type, const char *caller, db_err_msg **msg)
Definition odb.cxx:7667
static INT db_get_data_locked(DATABASE_HEADER *pheader, const KEY *pkey, int idx, void *data, INT *buf_size, DWORD type, db_err_msg **msg)
Definition odb.cxx:5772
INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:7058
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3887
INT db_check_record(HNDLE hDB, HNDLE hKey, const char *keyname, const char *rec_str, BOOL correct)
Definition odb.cxx:13564
static int validate_free_data(DATABASE_HEADER *pheader, int free_data)
Definition odb.cxx:415
static bool db_validate_and_repair_key_wlocked(DATABASE_HEADER *pheader, int recurse, const char *path, HNDLE parenthkeylist, HNDLE hkey, KEY *pkey, db_err_msg **msg, keylist_vector *keylist)
Definition odb.cxx:1300
INT db_copy_json_save(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:11057
INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, int *buffer_size, bool header)
Definition odb.cxx:9550
MJsonNode * db_scl(HNDLE hDB)
Definition odb.cxx:14705
static RECORD_LIST * _record_list
Definition odb.cxx:60
INT db_save_struct(HNDLE hDB, HNDLE hKey, const char *file_name, const char *struct_name, BOOL append)
Definition odb.cxx:11209
INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align)
Definition odb.cxx:12297
static void * realloc_data(DATABASE_HEADER *pheader, void *address, INT old_size, INT new_size, const char *caller)
Definition odb.cxx:613
void xml_encode(char *src, int size)
Definition odb.cxx:9816
INT db_unwatch(HNDLE hDB, HNDLE hKey)
Definition odb.cxx:14481
INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
Definition odb.cxx:8535
INT db_get_next_link(HNDLE hDB, HNDLE hKey, HNDLE *subkey_handle)
Definition odb.cxx:6081
static bool db_validate_data_area_wlocked(DATABASE_HEADER *pheader, const keylist_vector &keylist, const keylist_vector &datafreelist, db_err_msg **msg)
Definition odb.cxx:2032
static const KEYLIST * db_get_pkeylist(const DATABASE_HEADER *pheader, const KEY *pkey, const char *caller, db_err_msg **msg, bool kludge_repair=false)
Definition odb.cxx:1138
INT db_scan_tree_link(HNDLE hDB, HNDLE hKey, INT level, void(*callback)(HNDLE, HNDLE, KEY *, INT, void *), void *info)
Definition odb.cxx:5122
INT db_save(HNDLE hDB, HNDLE hKey, const char *filename, BOOL bRemote)
Definition odb.cxx:9758
static bool db_validate_and_repair_db_wlocked(DATABASE_HEADER *pheader, db_err_msg **msg)
Definition odb.cxx:2208
INT db_allow_write_locked(DATABASE *p, const char *caller_name)
Definition odb.cxx:3114
INT db_scan_tree(HNDLE hDB, HNDLE hKey, INT level, INT(*callback)(HNDLE, HNDLE, KEY *, INT, void *), void *info)
Definition odb.cxx:5039
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6538
static bool db_get_key_free_list_locked(const DATABASE_HEADER *pheader, keylist_vector *keyfreelist, db_err_msg **msg)
Definition odb.cxx:717
static INT db_get_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, KEY *key, db_err_msg **msg)
Definition odb.cxx:6461
static int _global_open_count
Definition odb.cxx:13324
INT db_get_link(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6591
static INT _database_entries
Definition odb.cxx:57
static bool db_validate_data_offset(const DATABASE_HEADER *pheader, int offset)
Definition odb.cxx:1061
static int db_remove_open_record_wlocked(DATABASE *pdb, DATABASE_HEADER *pheader, HNDLE hKey)
Definition odb.cxx:13043
static int json_write_bare_key(HNDLE hDB, HNDLE hLink, const KEY &link, char **buffer, int *buffer_size, int *buffer_end, int level, int flags, time_t timestamp, bool need_comma)
Definition odb.cxx:10834
static int db_get_record2_read_element(HNDLE hDB, HNDLE hKey, const char *key_name, int tid, int n_data, int string_length, char *buf_start, char **buf_ptr, int *buf_remain, BOOL correct)
Definition odb.cxx:12639
INT db_load(HNDLE hDB, HNDLE hKeyRoot, const char *filename, BOOL bRemote)
Definition odb.cxx:8634
INT EXPRT db_get_value_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int index, std::string *s, BOOL create, int create_string_length)
Definition odb.cxx:14528
INT db_sprintff(char *string, const char *format, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:11502
static void db_delete_client_wlocked(DATABASE_HEADER *pheader, int jclient, db_err_msg **msg)
Definition odb.cxx:3304
INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition odb.cxx:3593
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:8163
static int db_notify_clients_locked(const DATABASE *pdb, HNDLE hKeyMod, int index, BOOL bWalk, db_err_msg **msg)
Definition odb.cxx:13130
static HNDLE db_pkey_to_hkey(const DATABASE_HEADER *pheader, const KEY *pkey)
Definition odb.cxx:1179
static bool db_validate_key_offset(const DATABASE_HEADER *pheader, int offset)
Definition odb.cxx:1049
static int free_data(DATABASE_HEADER *pheader, void *address, INT size, const char *caller)
Definition odb.cxx:532
static void db_save_tree_struct(HNDLE hDB, HNDLE hKey, int hfile, INT level)
Definition odb.cxx:9631
static void db_print_pkey(const DATABASE_HEADER *pheader, const KEY *pkey, int recurse=0, const char *path=NULL, HNDLE parenthkeylist=0)
Definition odb.cxx:1265
INT db_save_string(HNDLE hDB, HNDLE hKey, const char *file_name, const char *string_name, BOOL append)
Definition odb.cxx:11270
static int db_update_open_record_wlocked(const DATABASE_HEADER *xpheader, const KEY *xpkey, int level, void *voidp, db_err_msg **msg)
Definition odb.cxx:1726
INT db_copy_json_index(HNDLE hDB, HNDLE hKey, int index, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:10803
static int db_check_open_record(const DATABASE_HEADER *pheader, const KEY *pkey, db_err_msg **msg)
Definition odb.cxx:4253
INT db_close_all_records()
Definition odb.cxx:14107
static int db_delete_client_info_wlocked(DATABASE *pdb, int pid, db_err_msg **msg)
Definition odb.cxx:3339
static const KEY * db_enum_next_locked(const DATABASE_HEADER *pheader, const KEY *pdir, const KEY *pkey, db_err_msg **msg)
Definition odb.cxx:1238
INT db_paste(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
Definition odb.cxx:9000
INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER *writer)
Definition odb.cxx:9859
static std::string db_get_path_pkey(const DATABASE_HEADER *pheader, const KEY *pkey)
Definition odb.cxx:5177
static void static void db_print_msg(const db_err_msg *msg)
Definition odb.cxx:136
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:14406
static std::string db_get_path_locked(const DATABASE_HEADER *pheader, HNDLE hKey)
Definition odb.cxx:5242
INT db_get_key_info(HNDLE hDB, HNDLE hKey, char *name, INT name_size, INT *type, INT *num_values, INT *item_size)
Definition odb.cxx:6710
INT db_close_all_databases(void)
Definition odb.cxx:2893
INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7734
INT db_enum_link(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5990
static const KEY * db_get_pkey(const DATABASE_HEADER *pheader, HNDLE hKey, int *pstatus, const char *caller, db_err_msg **msg)
Definition odb.cxx:1086
INT db_copy_json_ls(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:11003
void name2c(char *str)
Definition odb.cxx:9610
char * strcomb(const char **list)
Definition odb.cxx:655
static INT db_set_data_wlocked(DATABASE_HEADER *pheader, KEY *pkey, const void *data, INT data_size, INT num_values, DWORD type, const char *caller, db_err_msg **msg)
Definition odb.cxx:7533
int EXPRT json_write_anything(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int level, int must_be_subdir, int flags, time_t timestamp)
Definition odb.cxx:10966
static WATCH_LIST * _watch_list
Definition odb.cxx:63
static int db_fix_open_records(HNDLE hDB, HNDLE hKey, KEY *key, INT level, void *xresult)
Definition odb.cxx:5408
INT db_delete(HNDLE hDB, HNDLE hKeyRoot, const char *odb_path)
Definition odb.cxx:4494
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:11426
INT db_update_last_activity(DWORD millitime)
Definition odb.cxx:3271
INT db_copy_json_obsolete(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int save_keys, int follow_links, int recurse)
Definition odb.cxx:11090
INT db_get_free_mem(HNDLE hDB, size_t *pkey_size, size_t *pdata_size, size_t *pdata_biggest)
Definition odb.cxx:925
void strarrayindex(char *odbpath, int *index1, int *index2)
Definition odb.cxx:3852
INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7832
DATABASE_CLIENT * db_get_my_client_locked(DATABASE *pdb)
Definition odb.cxx:1571
static int db_set_mode_wlocked(DATABASE_HEADER *, KEY *, WORD mode, int recurse, db_err_msg **)
Definition odb.cxx:8487
INT db_get_data1(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type, INT *num_values)
Definition odb.cxx:7275
INT db_copy_json_values(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int omit_names, int omit_last_written, time_t omit_old_timestamp, int preserve_case)
Definition odb.cxx:11025
INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type)
Definition odb.cxx:5523
static int db_find_open_records(HNDLE hDB, HNDLE hKey, KEY *key, INT level, void *xresult)
Definition odb.cxx:5366
static void json_write_data(char **buffer, int *buffer_size, int *buffer_end, int level, const KEY *key, const char *p)
Definition odb.cxx:10160
static INT _watch_list_entries
Definition odb.cxx:64
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4751
static void db_recurse_record_tree_locked(const DATABASE *pdb, const KEY *pkey, void **data, INT *total_size, INT base_align, INT *max_align, BOOL bSet, INT convert_flags, db_err_msg **msg)
Definition odb.cxx:12022
static const KEY * db_enum_first_locked(const DATABASE_HEADER *pheader, const KEY *pkey, db_err_msg **msg)
Definition odb.cxx:1210
INT db_update_record_local(INT hDB, INT hKeyRoot, INT hKey, int index)
Definition odb.cxx:14145
INT db_copy_json_array(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:10668
MJsonNode * db_sor(HNDLE hDB, const char *root_path)
Definition odb.cxx:14755
INT db_get_link_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:7175
void db_set_watchdog_params(DWORD timeout)
Definition odb.cxx:3555
static void db_validate_sizes()
Definition odb.cxx:1617
INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
Definition odb.cxx:14192
INT db_rename_key(HNDLE hDB, HNDLE hKey, const char *name)
Definition odb.cxx:6780
BOOL ends_with_ustring(const char *str, const char *suffix)
Definition odb.cxx:3801
void db_cleanup2(const char *client_name, int ignore_timeout, DWORD actual_time, const char *who)
Definition odb.cxx:3476
void json_write(char **buffer, int *buffer_size, int *buffer_end, int level, const char *s, int quoted)
Definition odb.cxx:10045
INT EXPRT db_paste_json(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
static bool is_utf8(const char *string)
Definition odb.cxx:978
INT db_get_key_time(HNDLE hDB, HNDLE hKey, DWORD *delta)
Definition odb.cxx:6651
int db_delete_client_info(HNDLE hDB, int pid)
Definition odb.cxx:3371
static bool db_validate_key_area_wlocked(DATABASE_HEADER *pheader, const keylist_vector &keylist, const keylist_vector &keyfreelist, db_err_msg **msg)
Definition odb.cxx:1884
static void db_flush_msg(db_err_msg **msg)
Definition odb.cxx:195
static INT db_find_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, const char *key_name, bool follow_links, HNDLE *subhKey, db_err_msg **msg)
Definition odb.cxx:4703
static DATABASE * db_lock_database(HNDLE hDB, int *pstatus, const char *caller, bool check_attached=true)
Definition odb.cxx:3003
INT db_find_keys(HNDLE hDB, HNDLE hKeyRoot, const char *odbpath, std::vector< HNDLE > &hKeyVector)
Definition odb.cxx:4847
static int db_validate_name(const char *name, int maybe_path, const char *caller_name, db_err_msg **msg)
Definition odb.cxx:989
INT db_set_client_name(HNDLE hDB, const char *client_name)
Definition odb.cxx:2935
static int db_set_value_wlocked(DATABASE *pdb, KEY *pkey_root, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type, db_err_msg **msg)
Definition odb.cxx:5563
INT db_set_value_index(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data, INT data_size, INT idx, DWORD type, BOOL trunc)
Definition odb.cxx:5630
INT db_notify_clients_array(HNDLE hDB, HNDLE hKeys[], INT size)
Definition odb.cxx:13215
INT db_open_record1(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size, WORD access_mode, void(*dispatcher)(INT, INT, void *), void *info, const char *rec_str)
Definition odb.cxx:14034
INT db_merge_data(HNDLE hDB, HNDLE hKeyRoot, const char *name, void *data, INT data_size, INT num_values, INT type)
Definition odb.cxx:8436
INT db_notify_clients(HNDLE hDB, HNDLE hKeyMod, int index, BOOL bWalk)
Definition odb.cxx:13186
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12881
INT db_set_data_index1(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type, BOOL bNotify)
Definition odb.cxx:8339
static int db_parse_record(const char *rec_str, const char **out_rec_str, char *title, int title_size, char *key_name, int key_name_size, int *tid, int *n_data, int *string_length)
Definition odb.cxx:12456
INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5852
INT EXPRT db_resize_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int num_values, int max_string_length)
Definition odb.cxx:14619
static void db_unlock_database(DATABASE *pdb, const char *caller)
Definition odb.cxx:3165
INT db_close_record(HNDLE hDB, HNDLE hKey)
Definition odb.cxx:14066
static int db_validate_open_records_wlocked(DATABASE_HEADER *pheader, db_err_msg **msg)
Definition odb.cxx:1803
INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
Definition odb.cxx:13392
INT db_sscanf(const char *data_str, void *data, INT *data_size, INT i, DWORD tid)
Definition odb.cxx:11897
static db_err_msg * _last_error_message
Definition odb.cxx:134
INT db_unwatch_all()
Definition odb.cxx:14511
static void db_msg(db_err_msg **msg, INT message_type, const char *filename, INT line, const char *routine, const char *format,...) MATTRPRINTF(6
Definition odb.cxx:144
INT db_add_open_record(HNDLE hDB, HNDLE hKey, WORD access_mode)
Definition odb.cxx:12954
INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string *s)
Definition odb.cxx:14599
INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
Definition odb.cxx:3243
static int db_enum_locked(const DATABASE_HEADER *pheader, const KEY *pkey, std::vector< HNDLE > *sub_handles, std::vector< KEY > *sub_keys, bool follow_links, db_err_msg **msg)
Definition odb.cxx:6202
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:8018
INT db_get_record2(HNDLE hDB, HNDLE hKey, void *data, INT *xbuf_size, INT align, const char *rec_str, BOOL correct)
Definition odb.cxx:12758
INT db_create_link(HNDLE hDB, HNDLE hKey, const char *link_name, const char *destination)
Definition odb.cxx:4183
static void merge_records(HNDLE hDB, HNDLE hKey, KEY *pkey, INT level, void *info)
Definition odb.cxx:13251
INT db_close_database(HNDLE hDB)
Definition odb.cxx:2685
INT db_protect_database(HNDLE hDB)
Definition odb.cxx:3746
struct db_err_msg_struct db_err_msg
Definition odb.cxx:72
const char * extract_key(const char *key_list, char *key_name, int key_name_length)
Definition odb.cxx:3766
std::vector< keylist_struct > keylist_vector
Definition odb.cxx:715
static DATABASE * _database
Definition odb.cxx:56
static bool db_get_data_free_list_locked(const DATABASE_HEADER *pheader, keylist_vector *datafreelist, db_err_msg **msg)
Definition odb.cxx:755
static void json_ensure_decimal_dot(char *str)
Definition odb.cxx:10145
#define RPC_DB_ADD_OPEN_RECORD
Definition mrpc.h:73
#define RPC_DB_SET_DATA_INDEX
Definition mrpc.h:68
void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
Definition midas.cxx:11846
#define RPC_DB_CLOSE_DATABASE
Definition mrpc.h:54
#define RPC_DB_CREATE_RECORD
Definition mrpc.h:81
#define RPC_DB_LOAD
Definition mrpc.h:76
#define RPC_DB_CREATE_LINK
Definition mrpc.h:57
#define RPC_DB_NOTIFY_CLIENTS_ARRAY
Definition mrpc.h:97
#define RPC_DB_ENUM_LINK
Definition mrpc.h:79
#define RPC_DB_DELETE_KEY
Definition mrpc.h:63
#define RPC_DB_SET_NUM_VALUES
Definition mrpc.h:89
#define RPC_DB_COPY_XML
Definition mrpc.h:99
#define RPC_DB_ENUM_KEY
Definition mrpc.h:64
bool rpc_is_remote(void)
Definition midas.cxx:12901
const char * rpc_tid_name_old(INT id)
Definition midas.cxx:11911
#define RPC_DB_SET_VALUE
Definition mrpc.h:58
#define RPC_DB_SET_CLIENT_NAME
Definition mrpc.h:77
#define RPC_DB_GET_DATA1
Definition mrpc.h:88
#define RPC_DB_REMOVE_OPEN_RECORD
Definition mrpc.h:74
#define RPC_DB_CLOSE_ALL_DATABASES
Definition mrpc.h:55
#define RPC_DB_SET_LINK_DATA_INDEX
Definition mrpc.h:95
#define RPC_DB_RENAME_KEY
Definition mrpc.h:78
#define RPC_DB_GET_NEXT_LINK
Definition mrpc.h:91
#define RPC_DB_GET_DATA
Definition mrpc.h:66
#define RPC_DB_SET_MODE
Definition mrpc.h:69
INT rpc_call(DWORD routine_id,...)
Definition midas.cxx:14124
const char * rpc_tid_name(INT id)
Definition midas.cxx:11904
#define RPC_DB_FIND_LINK
Definition mrpc.h:61
#define RPC_DB_GET_LINK
Definition mrpc.h:92
#define RPC_DB_GET_KEY
Definition mrpc.h:65
#define RPC_DB_SET_DATA
Definition mrpc.h:67
#define RPC_DB_SET_LINK_DATA
Definition mrpc.h:94
#define RPC_DB_GET_LINK_DATA
Definition mrpc.h:93
#define RPC_DB_SET_DATA_INDEX1
Definition mrpc.h:86
#define RPC_DB_GET_PARENT
Definition mrpc.h:98
#define RPC_DB_REORDER_KEY
Definition mrpc.h:80
#define RPC_DB_OPEN_DATABASE
Definition mrpc.h:53
#define RPC_DB_CHECK_RECORD
Definition mrpc.h:90
#define RPC_DB_GET_DATA_INDEX
Definition mrpc.h:82
int rpc_name_tid(const char *name)
Definition midas.cxx:11918
#define RPC_DB_GET_VALUE
Definition mrpc.h:59
#define RPC_DB_GET_KEY_INFO
Definition mrpc.h:87
bool rpc_is_mserver(void)
Definition midas.cxx:12958
#define RPC_DB_FLUSH_DATABASE
Definition mrpc.h:85
#define RPC_DB_SAVE
Definition mrpc.h:75
#define RPC_DB_GET_PATH
Definition mrpc.h:62
#define RPC_DB_GET_KEY_TIME
Definition mrpc.h:83
#define RPC_DB_SET_RECORD
Definition mrpc.h:72
#define RPC_DB_GET_RECORD_SIZE
Definition mrpc.h:70
#define RPC_DB_FIND_KEY
Definition mrpc.h:60
#define RPC_DB_GET_OPEN_RECORDS
Definition mrpc.h:84
INT rpc_get_convert_flags(void)
Definition midas.cxx:13170
INT rpc_tid_size(INT id)
Definition midas.cxx:11897
void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
Definition midas.cxx:11821
#define RPC_DB_SET_DATA1
Definition mrpc.h:96
#define RPC_DB_CREATE_KEY
Definition mrpc.h:56
#define RPC_DB_GET_RECORD
Definition mrpc.h:71
void ** info
Definition fesimdaq.cxx:41
HNDLE hKey
DWORD n[4]
Definition mana.cxx:247
INT index
Definition mana.cxx:271
void * data
Definition mana.cxx:268
INT odb_size
Definition analyzer.cxx:46
BOOL debug
debug printouts
Definition mana.cxx:254
INT type
Definition mana.cxx:269
HNDLE hDB
main ODB handle
Definition mana.cxx:207
BOOL verbose
Definition mana.cxx:255
BOOL create
Definition mchart.cxx:39
double count
Definition mdump.cxx:33
KEY key
Definition mdump.cxx:34
INT i
Definition mdump.cxx:32
std::vector< FMT_ID > eq
Definition mdump.cxx:55
HNDLE hSubkey
Definition mdump.cxx:35
DWORD actual_time
Definition mfe.cxx:37
static int offset
Definition mgd.cxx:1500
INT _semaphore_alarm
Definition midas.cxx:1478
INT _semaphore_elog
Definition midas.cxx:1479
INT _semaphore_history
Definition midas.cxx:1480
std::string msprintf(const char *format,...)
Definition midas.cxx:419
#define JSFLAG_RECURSE
Definition midas.h:1712
#define DIR_SEPARATOR
Definition midas.h:193
unsigned short UINT16
Definition midas.h:138
#define MAX_CLIENTS
Definition midas.h:274
INT HNDLE
Definition midas.h:132
INT midas_thread_t
Definition midas.h:179
DWORD BOOL
Definition midas.h:105
#define DIR_SEPARATOR_STR
Definition midas.h:194
char INT8
Definition midas.h:137
#define JS_LEVEL_0
Definition midas.h:1707
int INT
Definition midas.h:129
#define JSFLAG_FOLLOW_LINKS
Definition midas.h:1711
#define JSFLAG_LOWERCASE
Definition midas.h:1713
#define MATTRPRINTF(a, b)
Definition midas.h:221
#define JSFLAG_OMIT_LAST_WRITTEN
Definition midas.h:1715
#define DATABASE_VERSION
Definition midas.h:34
unsigned long long UINT64
Definition midas.h:142
short INT16
Definition midas.h:139
#define RPC_OUTGOING
Definition midas.h:1568
int INT32
Definition midas.h:141
long long INT64
Definition midas.h:143
#define MAX_ODB_PATH
Definition midas.h:277
#define MIDAS_VERSION
Definition midas.h:37
#define JSFLAG_OMIT_NAMES
Definition midas.h:1714
#define WATCHDOG_INTERVAL
Definition midas.h:288
#define JS_LEVEL_1
Definition midas.h:1708
#define MAX_OPEN_RECORDS
Definition midas.h:276
#define TRUE
Definition midas.h:182
#define JS_MUST_BE_SUBDIR
Definition midas.h:1709
unsigned char UINT8
Definition midas.h:136
#define POINTER_T
Definition midas.h:166
#define JSFLAG_SAVE_KEYS
Definition midas.h:1710
unsigned int UINT32
Definition midas.h:140
#define JSFLAG_OMIT_OLD
Definition midas.h:1716
#define RPC_FIXARRAY
Definition midas.h:1566
#define NAME_LENGTH
Definition midas.h:272
#define message(type, str)
HNDLE hdb
Definition midas_macro.h:21
#define read(n, a, f)
#define write(n, a, f, d)
#define name(x)
Definition midas_macro.h:24
static FILE * fp
struct callback_addr callback
Definition mserver.cxx:22
#define S(x)
BOOL match(char *pat, char *str)
Definition odbedit.cxx:189
INT j
Definition odbhist.cxx:40
double value[100]
Definition odbhist.cxx:42
INT k
Definition odbhist.cxx:40
char str[256]
Definition odbhist.cxx:33
char file_name[256]
Definition odbhist.cxx:41
DWORD status
Definition odbhist.cxx:39
Definition midas.h:1214
DWORD last_activity
Definition msystem.h:424
char name[NAME_LENGTH]
Definition msystem.h:418
OPEN_RECORD open_record[MAX_OPEN_RECORDS]
Definition msystem.h:428
DWORD watchdog_timeout
Definition msystem.h:425
INT num_open_records
Definition msystem.h:423
char name[NAME_LENGTH]
Definition msystem.h:433
DATABASE_CLIENT client[MAX_CLIENTS]
Definition msystem.h:443
INT first_free_data
Definition msystem.h:441
INT max_client_index
Definition msystem.h:436
BOOL protect
Definition msystem.h:461
void * database_data
Definition msystem.h:455
INT timeout
Definition msystem.h:465
HNDLE shm_size
Definition msystem.h:459
MUTEX_T * mutex
Definition msystem.h:464
BOOL protect_read
Definition msystem.h:462
INT lock_cnt
Definition msystem.h:457
INT client_index
Definition msystem.h:453
HNDLE semaphore
Definition msystem.h:456
HNDLE shm_handle
Definition msystem.h:460
HNDLE hDB
Definition msystem.h:450
DATABASE_HEADER * database_header
Definition msystem.h:454
BOOL attached
Definition msystem.h:452
BOOL protect_write
Definition msystem.h:463
BOOL inside_lock_unlock
Definition msystem.h:466
void * shm_adr
Definition msystem.h:458
INT next_free
Definition msystem.h:407
Definition midas.h:1027
INT num_values
Definition midas.h:1029
DWORD type
Definition midas.h:1028
WORD notify_count
Definition midas.h:1035
INT total_size
Definition midas.h:1032
WORD access_mode
Definition midas.h:1034
INT last_written
Definition midas.h:1038
INT parent_keylist
Definition midas.h:1037
char name[NAME_LENGTH]
Definition midas.h:1030
INT next_key
Definition midas.h:1036
INT item_size
Definition midas.h:1033
INT data
Definition midas.h:1031
INT num_keys
Definition midas.h:1043
INT parent
Definition midas.h:1042
INT first_key
Definition midas.h:1044
NET_COMMAND_HEADER header
Definition msystem.h:293
char param[32]
Definition msystem.h:294
WORD flags
Definition msystem.h:413
WORD access_mode
Definition msystem.h:412
WORD access_mode
Definition msystem.h:475
void(* dispatcher)(INT, INT, void *)
Definition msystem.h:479
void * info
Definition msystem.h:480
INT buf_size
Definition msystem.h:478
HNDLE handle
Definition msystem.h:473
void * data
Definition msystem.h:476
void * copy
Definition msystem.h:477
HNDLE hDB
Definition msystem.h:474
Definition midas.h:1233
DATABASE_HEADER * pheader
Definition odb.cxx:1717
HNDLE handle
Definition msystem.h:487
HNDLE hDB
Definition msystem.h:488
void * info
Definition msystem.h:490
void(* dispatcher)(INT, INT, INT, void *info)
Definition msystem.h:489
std::string routine
Definition odb.cxx:130
std::string filename
Definition odb.cxx:128
std::string text
Definition odb.cxx:131
db_err_msg * next
Definition odb.cxx:126
HNDLE hsort
Definition odb.cxx:708
HNDLE next_free
Definition odb.cxx:707
HNDLE hkey
Definition odb.cxx:703
size_t skey
Definition odb.cxx:704
HNDLE hdata
Definition odb.cxx:705
size_t sdata
Definition odb.cxx:706
char c
Definition system.cxx:1312
static double comma(double a, double b)
Definition tinyexpr.c:248
static double e(void)
Definition tinyexpr.c:136
static te_expr * list(state *s)
Definition tinyexpr.c:567