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 "mjson.h"
40
41#define CHECK_OPEN_RECORD 1
42
43/*------------------------------------------------------------------*/
44
45/********************************************************************\
46* *
47* db_xxx - Database Functions *
48* *
49\********************************************************************/
50
51/* Globals */
52
53#ifdef LOCAL_ROUTINES
56#endif
57
60
63
65
66/*------------------------------------------------------------------*/
67
68#ifdef LOCAL_ROUTINES
70static void db_msg(db_err_msg** msg, INT message_type, const char *filename, INT line, const char *routine, const char *format, ...) MATTRPRINTF(6,7);
82static int db_set_value_wlocked(DATABASE_HEADER* pheader, HNDLE hDB, KEY* pkey_root, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type, db_err_msg** msg);
83static INT db_get_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, void *data, INT * buf_size, DWORD type, db_err_msg** msg);
84static 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);
85static 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);
86static 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);
89#endif // LOCAL_ROUTINES
90
91/*------------------------------------------------------------------*/
92
93/********************************************************************\
94* *
95* db_msg_xxx error message handling *
96* *
97\********************************************************************/
98
99#ifdef LOCAL_ROUTINES
100
102{
105 std::string filename;
106 int line = 0;
107 std::string routine;
108 std::string text;
109};
110
111static db_err_msg* _last_error_message = NULL; // for debuging core dumps
112
113void db_print_msg(const db_err_msg* msg)
114{
115 while (msg != NULL) {
116 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());
117 msg = msg->next;
118 }
119}
120
121void db_msg(db_err_msg** msgp, INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
122{
123 if (!msgp)
124 return;
125
127 char message[1000];
128
129 /* print argument list into message */
130 va_start(argptr, format);
131 vsnprintf(message, sizeof(message)-1, format, argptr);
132 va_end(argptr);
133 message[sizeof(message)-1] = 0; // ensure string is NUL-terminated
134
135 db_err_msg* msg = new db_err_msg;
136
137 msg->next = NULL;
138 msg->message_type = message_type;
139 msg->filename = filename;
140 msg->line = line;
141 msg->routine = routine;
142 msg->text = message;
143
145
146 //printf("new message:\n");
147 //db_print_msg(msg);
148
149 if (*msgp == NULL) {
150 *msgp = msg;
151 return;
152 }
153
154 // append new message to the end of the list
155 db_err_msg *m = (*msgp);
156 while (m->next != NULL) {
157 m = m->next;
158 }
159 assert(m->next == NULL);
160 m->next = msg;
161
162 //printf("Message list with added new message:\n");
163 //db_print_msg(*msgp);
164 return;
165}
166
168{
169 db_err_msg *msg = *msgp;
170 *msgp = NULL;
171
172 if (/* DISABLES CODE */ (0)) {
173 printf("db_flush_msg: %p\n", msg);
174 db_print_msg(msg);
175 }
176
177 while (msg != NULL) {
178 cm_msg(msg->message_type, msg->filename.c_str(), msg->line, msg->routine.c_str(), "%s", msg->text.c_str());
179 db_err_msg* next = msg->next;
180 msg->message_type = 0;
181 msg->next = NULL;
182 delete msg;
183 msg = next;
184 }
185}
186
187#endif // LOCAL_ROUTINES
188
189/*------------------------------------------------------------------*/
190
191#ifdef LOCAL_ROUTINES
192
193/********************************************************************\
194* *
195* Shared Memory Allocation *
196* *
197\********************************************************************/
198
199static bool db_validate_key_offset(const DATABASE_HEADER * pheader, int offset);
200
201/*------------------------------------------------------------------*/
202static void *malloc_key(DATABASE_HEADER * pheader, INT size, const char* caller)
203{
205
206 if (size == 0)
207 return NULL;
208
209 /* quadword alignment for alpha CPU */
210 size = ALIGN8(size);
211
212 //printf("malloc_key(%d) from [%s]\n", size, caller);
213
214 if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
215 return NULL;
216 }
217
218 /* search for free block */
219 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
220
221 //printf("try free block %p size %d, next %d\n", pfree, pfree->size, pfree->next_free);
222
223 while (pfree->size < size && pfree->next_free) {
224 if (!db_validate_key_offset(pheader, pfree->next_free)) {
225 return NULL;
226 }
227 pprev = pfree;
228 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
229 //printf("pfree %p size %d next_free %d\n", pfree, pfree->size, pfree->next_free);
230 }
231
232 //printf("found free block %p size %d\n", pfree, pfree->size);
233
234 /* return if not enough memory */
235 if (pfree->size < size)
236 return 0;
237
238 pfound = pfree;
239
240 /* if found block is first in list, correct pheader */
241 if (pfree == (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key)) {
242 if (size < pfree->size) {
243 /* free block is only used partially */
244 pheader->first_free_key += size;
245 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
246
247 pfree->size = pfound->size - size;
248 pfree->next_free = pfound->next_free;
249 } else {
250 /* free block is used totally */
251 pheader->first_free_key = pfree->next_free;
252 }
253 } else {
254 /* check if free block is used totally */
255 if (pfound->size - size < (int) sizeof(FREE_DESCRIP)) {
256 /* skip block totally */
257 pprev->next_free = pfound->next_free;
258 } else {
259 /* decrease free block */
260 pfree = (FREE_DESCRIP *) ((char *) pfound + size);
261
262 pfree->size = pfound->size - size;
263 pfree->next_free = pfound->next_free;
264
265 pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
266 }
267 }
268
269 assert((void*)pfound != (void*)pheader);
270
271 memset(pfound, 0, size);
272
273 return pfound;
274}
275
276/*------------------------------------------------------------------*/
277static void free_key(DATABASE_HEADER * pheader, void *address, INT size)
278{
280
281 if (size == 0)
282 return;
283
284 assert(address != pheader);
285
286 /* quadword alignment for alpha CPU */
287 size = ALIGN8(size);
288
289 pfree = (FREE_DESCRIP *) address;
290 pprev = NULL;
291
292 /* clear current block */
293 memset(address, 0, size);
294
295 /* if key comes before first free block, adjust pheader */
296 if ((POINTER_T) address - (POINTER_T) pheader < pheader->first_free_key) {
297 pfree->size = size;
298 pfree->next_free = pheader->first_free_key;
299 pheader->first_free_key = (POINTER_T) address - (POINTER_T) pheader;
300 } else {
301 /* find last free block before current block */
302 pprev = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
303
304 while (pprev->next_free < (POINTER_T) address - (POINTER_T) pheader) {
305 if (pprev->next_free <= 0) {
306 cm_msg(MERROR, "free_key", "database is corrupted: pprev=%p, pprev->next_free=%d", pprev, pprev->next_free);
307 return;
308 }
309 pprev = (FREE_DESCRIP *) ((char *) pheader + pprev->next_free);
310 }
311
312 pfree->size = size;
313 pfree->next_free = pprev->next_free;
314
315 pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
316 }
317
318 /* try to melt adjacent free blocks after current block */
319 pnext = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
320 if ((POINTER_T) pnext == (POINTER_T) pfree + pfree->size) {
321 pfree->size += pnext->size;
322 pfree->next_free = pnext->next_free;
323
324 memset(pnext, 0, pnext->size);
325 }
326
327 /* try to melt adjacent free blocks before current block */
328 if (pprev && pprev->next_free == (POINTER_T) pprev - (POINTER_T) pheader + pprev->size) {
329 pprev->size += pfree->size;
330 pprev->next_free = pfree->next_free;
331
332 memset(pfree, 0, pfree->size);
333 }
334}
335
337{
338 if (free_data <= 0)
339 return 0;
340
341 if (free_data < (int)sizeof(DATABASE_HEADER)) {
342 //printf("validate_free_data: failed: %d is inside the database header 0..%d\n", free_data, (int)sizeof(DATABASE_HEADER));
343 return 0;
344 }
345
346 if (free_data < (int)sizeof(DATABASE_HEADER) + pheader->key_size) {
347 //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);
348 return 0;
349 }
350
351 if (free_data > (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size) {
352 //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);
353 return 0;
354 }
355
356 return 1;
357}
358
359/*------------------------------------------------------------------*/
360static void *malloc_data(DATABASE_HEADER * pheader, INT size)
361{
362 if (size == 0)
363 return NULL;
364
365 assert(size > 0);
366
367 /* quadword alignment for alpha CPU */
368 size = ALIGN8(size);
369
370 /* smallest allocation size is 8 bytes to make sure we can always create a new FREE_DESCRIP in free_data() */
371 assert(size >= (int)sizeof(FREE_DESCRIP));
372
373 if (!validate_free_data(pheader, pheader->first_free_data)) {
374 return NULL;
375 }
376
377 /* search for free block */
378 FREE_DESCRIP *pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
381
382 while (1) {
383 //printf("malloc_data: pprev %p, pfree %p, next %d, size %d, want %d\n", pprev, pfree, pfree->next_free, pfree->size, size);
384
385 if (pfree->size >= size) {
386 // we will use this block
387 pfound = pfree;
388 break;
389 }
390
391 if (!pfree->next_free) {
392 // no more free blocks
393 return NULL;
394 }
395
396 if (!validate_free_data(pheader, pfree->next_free)) {
397 // next_free is invalid
398 //printf("malloc_data: pprev %p, pfree %p, next %d, size %d, next is invalid\n", pprev, pfree, pfree->next_free, pfree->size);
399 return NULL;
400 }
401 pprev = pfree;
402 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
403 }
404
405 //printf("malloc_data: pprev %p, pfound %p, size %d, want %d\n", pprev, pfound, pfound->size, size);
406
407 assert(pfound != NULL);
408 assert(size <= pfound->size);
409
410 /* if found block is first in list, correct pheader */
411 if (!pprev) {
412 if (size < pfree->size) {
413 /* free block is only used partially */
414 pheader->first_free_data += size;
415 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
416
417 pfree->size = pfound->size - size;
418 pfree->next_free = pfound->next_free;
419 } else {
420 /* free block is used totally */
421 pheader->first_free_data = pfree->next_free;
422 }
423 } else {
424 /* check if free block is used totally */
425 if (pfound->size - size < (int) sizeof(FREE_DESCRIP)) {
426 /* delete this free block from the free blocks chain */
427 pprev->next_free = pfound->next_free;
428 } else {
429 /* decrease free block */
430 pfree = (FREE_DESCRIP *) ((char *) pfound + size);
431
432 pfree->size = pfound->size - size;
433 pfree->next_free = pfound->next_free;
434
435 pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
436 }
437 }
438
439 assert((void*)pfound != (void*)pheader);
440
441 /* zero memeory */
442 memset(pfound, 0, size);
443
444 return pfound;
445}
446
447/*------------------------------------------------------------------*/
448static int free_data(DATABASE_HEADER * pheader, void *address, INT size, const char* caller)
449{
450 if (size == 0)
451 return DB_SUCCESS;
452
453 assert(address != pheader);
454
455 /* quadword alignment for alpha CPU */
456 size = ALIGN8(size);
457
458 /* smallest allocation size is 8 bytes to make sure we can always create a new FREE_DESCRIP in free_data() */
459 assert(size >= (int)sizeof(FREE_DESCRIP));
460
462 FREE_DESCRIP *pfree = (FREE_DESCRIP *) address;
463 int pfree_offset = (POINTER_T) address - (POINTER_T) pheader;
464
465 /* clear current block */
466 memset(address, 0, size);
467
468 if (pheader->first_free_data == 0) {
469 /* if free list is empty, create the first free block, adjust pheader */
470 pfree->size = size;
471 pfree->next_free = 0;
472 pheader->first_free_data = pfree_offset;
473 /* nothing else to do */
474 return DB_SUCCESS;
475 } else if ((POINTER_T) address - (POINTER_T) pheader < pheader->first_free_data) {
476 /* if data comes before first free block, create new free block, adjust pheader */
477 pfree->size = size;
478 pfree->next_free = pheader->first_free_data;
479 pheader->first_free_data = pfree_offset;
480 /* maybe merge next free block into the new free block */
481 //printf("free_data: created new first free block, maybe merge with old first free block\n");
482 } else {
483 /* find last free block before current block */
484 pprev = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
485
486 while (pprev->next_free < pfree_offset) {
487 if (pprev->next_free == 0) {
488 /* add new free block at the end of the chain of free blocks */
489 //printf("free_data: adding new free block at the very end\n");
490 break;
491 }
492 if (!validate_free_data(pheader, pprev->next_free)) {
493 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);
494 return DB_CORRUPTED;
495 }
496
497 pprev = (FREE_DESCRIP *) ((char *) pheader + pprev->next_free);
498 }
499
500 pfree->size = size;
501 pfree->next_free = pprev->next_free;
502
503 pprev->next_free = pfree_offset;
504 }
505
506 /* try to melt adjacent free blocks after current block */
507 FREE_DESCRIP *pnext = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
508 if ((POINTER_T) pnext == (POINTER_T) pfree + pfree->size) {
509 //printf("free_data: merging first and second free block\n");
510 pfree->size += pnext->size;
511 pfree->next_free = pnext->next_free;
512
513 memset(pnext, 0, pnext->size);
514 }
515
516 /* try to melt adjacent free blocks before current block */
517 if (pprev && pprev->next_free == (POINTER_T) pprev - (POINTER_T) pheader + pprev->size) {
518 //printf("free_data: merging pprev and pfree\n");
519 pprev->size += pfree->size;
520 pprev->next_free = pfree->next_free;
521
522 memset(pfree, 0, pfree->size);
523 }
524
525 return DB_SUCCESS;
526}
527
528/*------------------------------------------------------------------*/
529static void *realloc_data(DATABASE_HEADER * pheader, void *address, INT old_size, INT new_size, const char* caller)
530{
531 void *tmp = NULL;
532
533 if (old_size) {
534 int status;
536 if (tmp == NULL) {
537 cm_msg(MERROR, "realloc_data", "cannot malloc(%d), called from %s", old_size, caller);
538 return NULL;
539 }
540
541 memcpy(tmp, address, old_size);
542
543 status = free_data(pheader, address, old_size, caller);
544 if (status != DB_SUCCESS) {
545 free(tmp);
546 cm_msg(MERROR, "realloc_data", "cannot free_data(%p, %d), called from %s", address, old_size, caller);
547 return NULL;
548 }
549 }
550
551 void *pnew = malloc_data(pheader, new_size);
552
553 if (!pnew) {
554 if (tmp)
555 free(tmp);
556 cm_msg(MERROR, "realloc_data", "cannot malloc_data(%d), called from %s", new_size, caller);
557 return NULL;
558 }
559
560 if (old_size) {
562 free(tmp);
563 }
564
565 return pnew;
566}
567
568#endif // LOCAL_ROUTINES
569
570/*------------------------------------------------------------------*/
571char *strcomb(const char **list)
572/* convert list of strings into single string to be used by db_paste() */
573{
574 INT i, j;
575 static char *str = NULL;
576
577 /* counter number of chars */
578 for (i = 0, j = 0; list[i]; i++)
579 j += strlen(list[i]) + 1;
580 j += 1;
581
582 if (str == NULL)
583 str = (char *) malloc(j);
584 else
585 str = (char *) realloc(str, j);
586
587 str[0] = 0;
588 for (i = 0; list[i]; i++) {
589 strcat(str, list[i]);
590 strcat(str, "\n");
591 }
592
593 return str;
594}
595
596/*------------------------------------------------------------------*/
597
598std::string strcomb1(const char **list)
599/* convert list of strings into single string to be used by db_paste() */
600{
601 std::string s;
602
603 for (int i = 0; list[i]; i++) {
604 s += list[i];
605 s += "\n";
606 }
607
608 return s;
609}
610
611/*------------------------------------------------------------------*/
612
614{
616 int used;
617 char* buf;
618};
619
620static void add_to_buf(struct print_key_info_buf* buf, const char* s)
621{
622 int len = strlen(s);
623 if (buf->used + len + 10 > buf->alloc_size) {
624 int new_size = 1024 + 2*buf->alloc_size + len;
625 //printf("realloc %d->%d, used %d, adding %d\n", buf->alloc_size, new_size, buf->used, len);
626 buf->buf = (char*)realloc(buf->buf, new_size);
627 assert(buf->buf != NULL);
628 buf->alloc_size = new_size;
629 }
630
631 memcpy(buf->buf + buf->used, s, len);
632 buf->used += len;
633 buf->buf[buf->used] = 0; // zero-terminate the string
634}
635
636#ifdef LOCAL_ROUTINES
637
639{
641 int i;
642
643 char str[256];
644
645 sprintf(str, "%08X %08X %08X ",
646 (int) (hKey - sizeof(DATABASE_HEADER)),
647 (int) (pkey->data - sizeof(DATABASE_HEADER)), (int) pkey->total_size);
648
649 assert(strlen(str)+10 < sizeof(str));
650
651 for (i = 0; i < level; i++)
652 strcat(str, " ");
653
654 assert(strlen(str)+10 < sizeof(str));
655
656 strcat(str, pkey->name);
657 strcat(str, "\n");
658
659 assert(strlen(str)+10 < sizeof(str));
660
661 //printf("str [%s]\n", str);
662
664
665 return SUCCESS;
666}
667
668static bool db_validate_data_offset(const DATABASE_HEADER * pheader, int offset);
669
671{
673
674 struct print_key_info_buf buf;
675 buf.buf = NULL;
676 buf.used = 0;
677 buf.alloc_size = 0;
678
680
682
683 char str[256];
684
685 sprintf(str, "Database header size is 0x%04X, all following values are offset by this!\n", (int)sizeof(DATABASE_HEADER));
686 add_to_buf(&buf, str);
687 sprintf(str, "Key area 0x00000000 - 0x%08X, size %d bytes\n", pheader->key_size - 1, pheader->key_size);
688 add_to_buf(&buf, str);
689 sprintf(str, "Data area 0x%08X - 0x%08X, size %d bytes\n\n", pheader->key_size, pheader->key_size + pheader->data_size, pheader->data_size);
690 add_to_buf(&buf, str);
691
692 add_to_buf(&buf, "Keylist:\n");
693 add_to_buf(&buf, "--------\n");
694 total_size_key = 0;
695
696 if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
697 add_to_buf(&buf, "ODB is corrupted: pheader->first_free_key is invalid\n");
699 if (result) {
700 *result = buf.buf;
701 } else {
702 free(buf.buf);
703 }
704 return DB_CORRUPTED;
705 }
706
707 FREE_DESCRIP *pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
708
709 while ((POINTER_T) pfree != (POINTER_T) pheader) {
711 sprintf(str, "Free block at 0x%08X, size 0x%08X, next 0x%08X\n",
712 (int) ((POINTER_T) pfree - (POINTER_T) pheader - sizeof(DATABASE_HEADER)),
713 pfree->size, pfree->next_free ? (int) (pfree->next_free - sizeof(DATABASE_HEADER)) : 0);
714 add_to_buf(&buf, str);
715 if (!db_validate_key_offset(pheader, pfree->next_free)) {
716 add_to_buf(&buf, "ODB is corrupted: next_free is invalid!");
717 break;
718 }
719 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
720 }
721
722 sprintf(str, "\nFree Key area: %d bytes out of %d bytes\n", total_size_key, pheader->key_size);
723 add_to_buf(&buf, str);
724
725 add_to_buf(&buf, "\nData:\n");
726 add_to_buf(&buf, "-----\n");
727 total_size_data = 0;
728
729 if (!db_validate_data_offset(pheader, pheader->first_free_data)) {
730 add_to_buf(&buf, "ODB is corrupted: pheader->first_free_data is invalid\n");
732 if (result) {
733 *result = buf.buf;
734 } else {
735 free(buf.buf);
736 }
737 return DB_CORRUPTED;
738 }
739
740 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
741
742 while ((POINTER_T) pfree != (POINTER_T) pheader) {
743 total_size_data += pfree->size;
744 sprintf(str, "Free block at 0x%08X, size 0x%08X, next 0x%08X\n",
745 (int) ((POINTER_T) pfree - (POINTER_T) pheader - sizeof(DATABASE_HEADER)),
746 pfree->size, pfree->next_free ? (int) (pfree->next_free - sizeof(DATABASE_HEADER)) : 0);
747 add_to_buf(&buf, str);
748 if (!db_validate_data_offset(pheader, pfree->next_free)) {
749 add_to_buf(&buf, "ODB is corrupted: next_free is invalid!");
750 break;
751 }
752 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
753 }
754
755 sprintf(str, "\nFree Data area: %d bytes out of %d bytes\n", total_size_data, pheader->data_size);
756 add_to_buf(&buf, str);
757
758 sprintf(str, "\nFree: %1d (%1.1lf%%) keylist, %1d (%1.1lf%%) data\n",
760 100 * (double) total_size_key / pheader->key_size,
761 total_size_data, 100 * (double) total_size_data / pheader->data_size);
762 add_to_buf(&buf, str);
763
764 if (verbose) {
765 add_to_buf(&buf, "\n\n");
766 add_to_buf(&buf, "Key Data Size\n");
767 add_to_buf(&buf, "----------------------------\n");
768 db_scan_tree(hDB, pheader->root_key, 0, print_key_info, &buf);
769 }
770
771 sprintf(str, "\nTotal ODB size: %d (0x%08X) Bytes, %lg MiB\n",
772 pheader->key_size + pheader->data_size, pheader->key_size + pheader->data_size,
773 ((pheader->key_size + pheader->data_size) / 1E6));
774 add_to_buf(&buf, str);
776
777 if (result) {
778 *result = buf.buf;
779 } else {
780 free(buf.buf);
781 }
782
783 return DB_SUCCESS;
784}
785
786INT db_get_free_mem(HNDLE hDB, INT *key_size, INT *data_size)
787{
788 DATABASE_HEADER *pheader;
790
791 *data_size = 0;
792 *key_size = 0;
793
795
796 pheader = _database[hDB - 1].database_header;
797
798 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
799
800 while ((POINTER_T) pfree != (POINTER_T) pheader) {
801 *key_size += pfree->size;
802 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
803 }
804
805 *data_size = 0;
806 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
807
808 while ((POINTER_T) pfree != (POINTER_T) pheader) {
809 *data_size += pfree->size;
810 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
811 }
812
814 return DB_SUCCESS;
815}
816
817#endif // LOCAL_ROUTINES
818
819// Method to check if a given string is valid UTF-8. Returns 1 if it is.
820// This method was taken from stackoverflow user Christoph, specifically
821// http://stackoverflow.com/questions/1031645/how-to-detect-utf-8-in-plain-c
822static bool is_utf8(const char * string)
823{
824 if (!string)
825 return false;
826
827 return ss_is_valid_utf8(string);
828}
829
830#ifdef LOCAL_ROUTINES
831
832/*------------------------------------------------------------------*/
833static int db_validate_name(const char* name, int maybe_path, const char* caller_name, db_err_msg** msg)
834{
835 //printf("db_validate_name [%s] length %d, maybe_path %d from %s\n", name, (int)strlen(name), maybe_path, caller_name);
836
837 if (name == NULL) {
838 db_msg(msg, MERROR, "db_validate_name", "Invalid name passed to %s: should not be NULL", caller_name);
839 return DB_INVALID_NAME;
840 }
841
842 size_t len = strlen(name);
843
844 if (len < 1) {
845 db_msg(msg, MERROR, "db_validate_name", "Invalid name passed to %s: should not be an empty string", caller_name);
846 return DB_INVALID_NAME;
847 }
848
849 if (strchr(name, '[')) {
850 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"[\"", name, caller_name);
851 return DB_INVALID_NAME;
852 }
853
854 if (name[0] == ' ') {
855 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not start with a space", name, caller_name);
856 return DB_INVALID_NAME;
857 }
858
859 if (name[len-1] == ' ') {
860 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not end with a space", name, caller_name);
861 return DB_INVALID_NAME;
862 }
863
864 if (strchr(name, ']')) {
865 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"[\"", name, caller_name);
866 return DB_INVALID_NAME;
867 }
868
869 if (!is_utf8(name)) {
870 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: invalid unicode UTF-8 encoding", name, caller_name);
871 return DB_INVALID_NAME;
872 }
873
874 if (!maybe_path) {
875 if (strchr(name, '/')) {
876 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"/\"", name, caller_name);
877 return DB_INVALID_NAME;
878 }
879
880 if (strlen(name) >= NAME_LENGTH) {
881 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);
882 return DB_INVALID_NAME;
883 }
884 }
885
886 //if (strcmp(name, "test")==0)
887 //return DB_INVALID_NAME;
888
889 return DB_SUCCESS;
890}
891
892/*------------------------------------------------------------------*/
893static bool db_validate_key_offset(const DATABASE_HEADER * pheader, int offset)
894/* check if key offset lies in valid range */
895{
896 if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
897 return false;
898
899 if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size)
900 return false;
901
902 return true;
903}
904
905static bool db_validate_data_offset(const DATABASE_HEADER * pheader, int offset)
906/* check if data offset lies in valid range */
907{
908 if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
909 return false;
910
911 if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size)
912 return false;
913
914 return true;
915}
916
917static bool db_validate_hkey(const DATABASE_HEADER * pheader, HNDLE hKey)
918{
919 if (hKey == 0) {
920 cm_msg(MERROR, "db_validate_hkey", "Error: invalid zero hkey %d", hKey);
921 return false;
922 }
923 if (!db_validate_key_offset(pheader, hKey)) {
924 cm_msg(MERROR, "db_validate_hkey", "Error: invalid hkey %d", hKey);
925 return false;
926 }
927 return true;
928}
929
930static const KEY* db_get_pkey(const DATABASE_HEADER* pheader, HNDLE hKey, int* pstatus, const char* caller, db_err_msg **msg)
931{
933
934 if (!hKey) {
936 hKey = pheader->root_key;
937 }
938
939 /* check if hKey argument is correct */
940 if (hKey == 0) {
941 if (pstatus)
943 return NULL;
944 }
945
946 /* check if hKey argument is correct */
947 if (!db_validate_key_offset(pheader, hKey)) {
948 if (pstatus)
950 return NULL;
951 }
952
953 const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
954
955 if (pkey->type < 1 || pkey->type >= TID_LAST) {
956 DWORD tid = pkey->type;
957 if (hKey_is_root_key) {
958 db_msg(msg, MERROR, caller, "db_get_pkey: root_key hkey %d invalid key type %d, database root directory is corrupted", hKey, tid);
959 if (pstatus)
961 return NULL;
962 } else {
963 std::string path = db_get_path_locked(pheader, hKey);
964 db_msg(msg, MERROR, caller, "db_get_pkey: hkey %d path \"%s\" invalid key type %d", hKey, path.c_str(), tid);
965 }
966 if (pstatus)
968 return NULL;
969 }
970
971 if (pkey->name[0] == 0) {
972 std::string path = db_get_path_locked(pheader, hKey);
973 db_msg(msg, MERROR, caller, "db_get_pkey: hkey %d path \"%s\" invalid name \"%s\" is empty", hKey, path.c_str(), pkey->name);
974 if (pstatus)
976 return NULL;
977 }
978
979 return pkey;
980}
981
982static const KEYLIST* db_get_pkeylist(const DATABASE_HEADER* pheader, HNDLE hKey, const KEY* pkey, const char* caller, db_err_msg **msg, bool kludge_repair = false)
983{
984 if (pkey->type != TID_KEY) {
985 std::string path = db_get_path_locked(pheader, hKey);
986 db_msg(msg, MERROR, caller, "db_get_pkeylist: hkey %d path \"%s\" unexpected call to db_get_pkeylist(), not a subdirectory, pkey->type %d", hKey, path.c_str(), pkey->type);
987 return NULL;
988 }
989
990 if (!hKey) {
991 hKey = pheader->root_key;
992 }
993
994 if (!db_validate_data_offset(pheader, pkey->data)) {
995 std::string path = db_get_path_locked(pheader, hKey);
996 db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkey->data %d", hKey, path.c_str(), pkey->data);
997 return NULL;
998 }
999
1000 const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->data);
1001
1002 if (pkeylist->parent != hKey) {
1003 std::string path = db_get_path_locked(pheader, hKey);
1004 db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkeylist->parent %d should be hkey %d", hKey, path.c_str(), pkeylist->parent, hKey);
1005 return NULL;
1006 }
1007
1008 if (pkeylist->first_key == 0 && pkeylist->num_keys != 0) {
1009 if (!kludge_repair) {
1010 std::string path = db_get_path_locked(pheader, hKey);
1011 db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkeylist->first_key %d should be non zero for num_keys %d", hKey, path.c_str(), pkeylist->first_key, pkeylist->num_keys);
1012 return NULL;
1013 }
1014
1015 // FIXME: this repair should be done in db_validate_and_repair_key()
1016
1017 std::string path = db_get_path_locked(pheader, hKey);
1018 db_msg(msg, MERROR, caller, "hkey %d path \"%s\" repaired invalid num_keys %d when pkeylist->first_key is zero", hKey, path.c_str(), pkeylist->num_keys);
1019 ((KEYLIST*)pkeylist)->num_keys = 0;
1020 }
1021
1022 return pkeylist;
1023}
1024
1025static HNDLE db_pkey_to_hkey(const DATABASE_HEADER* pheader, const KEY* pkey)
1026{
1027 return (POINTER_T) pkey - (POINTER_T) pheader;
1028}
1029
1030static const KEY* db_get_parent(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, const char* caller, db_err_msg **msg)
1031{
1032 if (pkey->parent_keylist == 0) {
1033 // they asked for the parent of "/", return "/"
1034 return db_get_pkey(pheader, pheader->root_key, pstatus, caller, msg);
1035 }
1036
1037 if (!db_validate_data_offset(pheader, pkey->parent_keylist)) {
1038 db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkey->parent %d", db_pkey_to_hkey(pheader, pkey), db_get_path_locked(pheader, pkey).c_str(), pkey->parent_keylist);
1039 if (pstatus)
1041 return NULL;
1042 }
1043
1044 const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
1045
1046 if (pkeylist->first_key == 0 && pkeylist->num_keys != 0) {
1047 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_locked(pheader, pkey).c_str(), pkeylist->first_key, pkeylist->num_keys);
1048 if (pstatus)
1050 return NULL;
1051 }
1052
1053 return db_get_pkey(pheader, pkeylist->parent, pstatus, caller, msg);
1054}
1055
1056static const KEY* db_enum_first_locked(const DATABASE_HEADER *pheader, const KEY* pkey, db_err_msg **msg)
1057{
1058 HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
1059
1060 if (pkey->type != TID_KEY) {
1061 std::string path = db_get_path_locked(pheader, hKey);
1062 db_msg(msg, MERROR, "db_enum_first_locked", "hkey %d path \"%s\" tid %d is not a directory", hKey, path.c_str(), pkey->type);
1063 return NULL;
1064 }
1065
1066 const KEYLIST *pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
1067
1068 if (!pkeylist) {
1069 // error
1070 return NULL;
1071 }
1072
1073 if (pkeylist->num_keys == 0) {
1074 // empty directory
1075 return NULL;
1076 }
1077
1078 if (pkeylist->first_key == 0) {
1079 // empty directory
1080 return NULL;
1081 }
1082
1083 return db_get_pkey(pheader, pkeylist->first_key, NULL, "db_enum_first_locked", msg);
1084}
1085
1086static const KEY* db_enum_next_locked(const DATABASE_HEADER *pheader, const KEY* pdir, const KEY* pkey, db_err_msg **msg)
1087{
1088 if (pkey->next_key == 0)
1089 return NULL;
1090
1091 return db_get_pkey(pheader, pkey->next_key, NULL, "db_enum_next_locked", msg);
1092}
1093
1094static const KEY* db_resolve_link_locked(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, db_err_msg** msg)
1095{
1096 if (pkey->type != TID_LINK)
1097 return pkey;
1098
1099 // FIXME: need to validate pkey->data
1100
1101 if (*((char *) pheader + pkey->data) == '/') {
1102 return db_find_pkey_locked(pheader, NULL, (char*)pheader + pkey->data, pstatus, msg);
1103 } else {
1104 return db_find_pkey_locked(pheader, pkey, (char*)pheader + pkey->data, pstatus, msg);
1105 }
1106}
1107
1108/*
1109static void db_print_pkey(const DATABASE_HEADER * pheader, const KEY* pkey, int recurse = 0, const char *path = NULL, HNDLE parenthkeylist = 0)
1110{
1111 HNDLE hkey = db_pkey_to_hkey(pheader, pkey);
1112
1113 std::string xpath;
1114 if (path == NULL) {
1115 xpath = db_get_path_locked(pheader, hkey);
1116 path = xpath.c_str();
1117 }
1118
1119 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);
1120
1121 if (pkey->type != TID_KEY) {
1122 printf("\n");
1123 } else {
1124 const KEYLIST *pkeylist = db_get_pkeylist(pheader, hkey, pkey, "db_validate_key", NULL);
1125
1126 if (pkeylist) {
1127 printf(", pkeylist parent %d, num_keys %d, first_key %d", pkeylist->parent, pkeylist->num_keys, pkeylist->first_key);
1128 printf("\n");
1129 }
1130 }
1131}
1132
1133static void db_print_hkey(const DATABASE_HEADER * pheader, HNDLE hkey, int recurse = 0, const char *path = NULL, HNDLE parenthkeylist = 0)
1134{
1135 const KEY *pkey = db_get_pkey(pheader, hkey, NULL, "db_print_key", NULL);
1136
1137 if (!pkey) {
1138 return;
1139 }
1140
1141 db_print_pkey(pheader, pkey, recurse, path, parenthkeylist);
1142}
1143*/
1144
1146{
1147 int status;
1148 bool flag = true;
1149
1150 //printf("path \"%s\", parenthkey %d, hkey %d, pkey->name \"%s\", type %d\n", path, parenthkeylist, hkey, pkey->name, pkey->type);
1151
1152 //std::string xpath = db_get_path_locked(pheader, hkey);
1153 //if (xpath != path)
1154 // printf("hkey %d, path \"%s\" vs \"%s\"\n", hkey, path, xpath.c_str());
1155
1156 //db_print_key(pheader, 0, path, parenthkeylist, hkey);
1157
1158 if (hkey==0 || !db_validate_key_offset(pheader, hkey)) {
1159 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid hkey", hkey, path);
1160 return false;
1161 }
1162
1163 /* check key type */
1164 if (pkey->type <= 0 || pkey->type >= TID_LAST) {
1165 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", name \"%s\", invalid key type %d", hkey, path, pkey->name, pkey->type);
1166 return false;
1167 }
1168
1169 /* check key name */
1170 status = db_validate_name(pkey->name, FALSE, "db_validate_key", msg);
1171 if (status != DB_SUCCESS) {
1172 char newname[NAME_LENGTH];
1173 sprintf(newname, "%p", pkey);
1174 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\": invalid name \"%s\" replaced with \"%s\"", hkey, path, pkey->name, newname);
1175 mstrlcpy(pkey->name, newname, sizeof(pkey->name));
1176 flag = false;
1177 //return false;
1178 }
1179
1180 /* check parent */
1181 if (pkey->parent_keylist != parenthkeylist) {
1182 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);
1183 return false;
1184 }
1185
1186 if (!db_validate_data_offset(pheader, pkey->data)) {
1187 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));
1188 return false;
1189 }
1190
1191 /* check key sizes */
1192 if ((pkey->total_size < 0) || (pkey->total_size > pheader->data_size)) {
1193 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->total_size %d", hkey, path, pkey->total_size);
1194 return false;
1195 }
1196
1197 if ((pkey->item_size < 0) || (pkey->item_size > pheader->data_size)) {
1198 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->item_size: %d", hkey, path, pkey->item_size);
1199 return false;
1200 }
1201
1202 if ((pkey->num_values < 0) || (pkey->num_values > pheader->data_size)) {
1203 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->num_values: %d", hkey, path, pkey->num_values);
1204 return false;
1205 }
1206
1207 /* check and correct key size */
1208 if (pkey->total_size != pkey->item_size * pkey->num_values) {
1209 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);
1210 pkey->total_size = pkey->item_size * pkey->num_values;
1211 flag = false;
1212 }
1213
1214 /* check and correct key size */
1215 if (pkey->data == 0 && pkey->total_size != 0) {
1216 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);
1217 pkey->num_values = 0;
1218 pkey->total_size = 0;
1219 flag = false;
1220 }
1221
1222 if (pkey->type == TID_STRING || pkey->type == TID_LINK) {
1223 const char* s = (char*)pheader + pkey->data;
1224 if (!is_utf8(s)) {
1225 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", string value is not valid UTF-8", hkey, path);
1226 //flag = false;
1227 }
1228 }
1229
1230 /* check for empty link */
1231 if (pkey->type == TID_LINK) {
1232 // minimum symlink length is 3 bytes:
1233 // one byte "/"
1234 // one byte odb entry name
1235 // one byte "\0"
1236 if (pkey->total_size <= 2) {
1237 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK is an empty link", hkey, path);
1238 flag = false;
1239 //return false;
1240 }
1241 }
1242
1243 /* check for too long link */
1244 if (pkey->type == TID_LINK) {
1245 if (pkey->total_size >= MAX_ODB_PATH) {
1246 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);
1247 flag = false;
1248 //return false;
1249 }
1250 }
1251
1252 /* check for link loop */
1253 if (pkey->type == TID_LINK) {
1254 const char* link = (char*)pheader + pkey->data;
1255 int link_len = strlen(link);
1256 int path_len = strlen(path);
1257 if (link_len == path_len) {
1258 // check for link to itself
1259 if (equal_ustring(link, path)) {
1260 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK to \"%s\" is a link to itself", hkey, path, link);
1261 flag = false;
1262 //return false;
1263 }
1264 } else if (link_len < path_len) {
1265 // check for link to the "path" subdirectory
1266 char tmp[MAX_ODB_PATH];
1267 memcpy(tmp, path, link_len);
1268 tmp[link_len] = 0;
1269 if (equal_ustring(link, tmp) && path[link_len] == DIR_SEPARATOR) {
1270 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK to \"%s\" is a loop", hkey, path, link);
1271 flag = false;
1272 //return false;
1273 }
1274 }
1275 }
1276
1277 /* check access mode */
1278 if ((pkey->access_mode & ~(MODE_READ | MODE_WRITE | MODE_DELETE | MODE_EXCLUSIVE | MODE_ALLOC))) {
1279 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->access_mode %d", hkey, path, pkey->access_mode);
1280 flag = false;
1281 //return false;
1282 }
1283
1284 /* check if access time is in the future */
1285 if (pkey->last_written > 0 && ((DWORD)pkey->last_written > ss_time() + 3600)) {
1286 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->last_written time %d", hkey, path, pkey->last_written);
1287 flag = false;
1288 //return false;
1289 }
1290
1291 if (pkey->type == TID_KEY) {
1292 bool pkeylist_ok = true;
1293 // FIXME: notice the kludged repair of pkeylist! K.O.
1294 const KEYLIST *pkeylist = db_get_pkeylist(pheader, hkey, pkey, "db_validate_key", msg, true);
1295
1296 if (!pkeylist) {
1297 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->data %d", hkey, path, pkey->data);
1298 flag = false;
1299 } else {
1300 if (pkeylist->parent != hkey) {
1301 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);
1302 flag = false;
1303 pkeylist_ok = false;
1304 }
1305
1306 if (pkeylist->num_keys < 0 || pkeylist->num_keys > pheader->key_size) {
1307 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->num_keys %d", hkey, path, pkeylist->num_keys);
1308 flag = false;
1309 pkeylist_ok = false;
1310 }
1311
1312 if (pkeylist->num_keys == 0 && pkeylist->first_key == 0) {
1313 // empty key
1314 } else if (pkeylist->first_key == 0 || !db_validate_key_offset(pheader, pkeylist->first_key)) {
1315 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->first_key %d", hkey, path, pkeylist->first_key);
1316 flag = false;
1317 pkeylist_ok = false;
1318 }
1319
1320 if (pkeylist_ok) {
1321 //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);
1322
1323 HNDLE subhkey = pkeylist->first_key;
1324
1325 int count = 0;
1326 while (subhkey != 0) {
1327 KEY* subpkey = (KEY*)db_get_pkey(pheader, subhkey, NULL, "db_validate_key", msg);
1328 if (!subpkey) {
1329 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid subhkey %d", hkey, path, subhkey);
1330 pkeylist_ok = false;
1331 flag = false;
1332 break;
1333 }
1334
1335 std::string buf;
1336 buf += path;
1337 buf += "/";
1338 buf += subpkey->name;
1339
1340 //printf("pkey %p, next %d, name [%s], path %s\n", subpkey, subpkey->next_key, subpkey->name, buf.c_str());
1341
1342 if (recurse) {
1343 flag &= db_validate_and_repair_key_wlocked(pheader, recurse + 1, buf.c_str(), pkey->data, subhkey, subpkey, msg);
1344 }
1345
1346 count++;
1347 subhkey = subpkey->next_key;
1348 }
1349
1350 if (count != pkeylist->num_keys) {
1351 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);
1352 ((KEYLIST*)pkeylist)->num_keys = count;
1353 flag = false;
1354 pkeylist_ok = false;
1355 }
1356 }
1357 }
1358 }
1359
1360 return flag;
1361}
1362
1363/*------------------------------------------------------------------*/
1364
1366{
1367 assert(pdb);
1368 assert(pdb->database_header);
1369
1370 DATABASE_HEADER *pheader = pdb->database_header;
1371
1372 int idx = pdb->client_index;
1373
1374 if (idx < 0 || idx >= pheader->max_client_index || idx >= MAX_CLIENTS) {
1375 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);
1376 ss_semaphore_release(pdb->semaphore);
1377 abort();
1378 }
1379
1380 DATABASE_CLIENT* pclient = &pheader->client[idx]; // safe to dereference on "idx"
1381
1382 if (pclient->name[0] == 0) {
1383 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);
1384 ss_semaphore_release(pdb->semaphore);
1385 abort();
1386 }
1387
1388 int pid = getpid();
1389
1390 if (pclient->pid != pid) {
1391 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);
1392 ss_semaphore_release(pdb->semaphore);
1393 abort();
1394 }
1395
1396 //printf("db_get_my_client_locked: idx %d, name [%s], pid %d\n", idx, pclient->name, pid);
1397
1398 //if (strcmp(pclient->name, "mdump") == 0) {
1399 // ss_semaphore_release(pdb->semaphore);
1400 // for (int i=0; i<15; i++) {
1401 // printf("sleep %d\n", i);
1402 // ::sleep(1);
1403 // }
1404 // abort();
1405 //}
1406
1407 return pclient;
1408}
1409
1410/*------------------------------------------------------------------*/
1412{
1413 /* validate size of data structures (miscompiled, 32/64 bit mismatch, etc */
1414
1415 if (0) {
1416#define S(x) printf("assert(sizeof(%-20s) == %6d);\n", #x, (int)sizeof(x))
1417 // basic data types
1418 S(char *);
1419 S(char);
1420 S(int);
1421 S(long int);
1422 S(float);
1423 S(double);
1424 S(BOOL);
1425 S(WORD);
1426 S(DWORD);
1427 S(INT);
1428 S(POINTER_T);
1430 // data buffers
1434 // history files
1435 S(HIST_RECORD);
1436 S(DEF_RECORD);
1437 S(INDEX_RECORD);
1438 S(TAG);
1439 // ODB shared memory structures
1440 S(KEY);
1441 S(KEYLIST);
1442 S(OPEN_RECORD);
1445 // misc structures
1446 S(EVENT_HEADER);
1447 S(RUNINFO);
1450 S(BANK_HEADER);
1451 S(BANK);
1452 S(BANK32);
1454 S(PROGRAM_INFO);
1455 S(ALARM_CLASS);
1456 S(ALARM);
1457 //S(CHN_SETTINGS);
1458 //S(CHN_STATISTICS);
1459#undef S
1460 }
1461
1462#if 0
1464 printf("EQUIPMENT_INFO offset of event_id: %d\n", (int)((char*)&eq.event_id - (char*)&eq));
1465 printf("EQUIPMENT_INFO offset of eq_type: %d\n", (int)((char*)&eq.eq_type - (char*)&eq));
1466 printf("EQUIPMENT_INFO offset of event_limit: %d\n", (int)((char*)&eq.event_limit - (char*)&eq));
1467 printf("EQUIPMENT_INFO offset of num_subevents: %d\n", (int)((char*)&eq.num_subevents - (char*)&eq));
1468 printf("EQUIPMENT_INFO offset of status: %d\n", (int)((char*)&eq.status - (char*)&eq));
1469 printf("EQUIPMENT_INFO offset of hidden: %d\n", (int)((char*)&eq.hidden - (char*)&eq));
1470#endif
1471
1472 assert(sizeof(UINT8) == 1);
1473 assert(sizeof(INT8) == 1);
1474 assert(sizeof(UINT16) == 2);
1475 assert(sizeof(INT16) == 2);
1476 assert(sizeof(UINT32) == 4);
1477 assert(sizeof(INT32) == 4);
1478 assert(sizeof(UINT64) == 8);
1479 assert(sizeof(INT64) == 8);
1480
1481#ifdef OS_LINUX
1482 assert(sizeof(EVENT_REQUEST) == 16); // ODB v3
1483 assert(sizeof(BUFFER_CLIENT) == 256);
1484 assert(sizeof(BUFFER_HEADER) == 16444);
1485 assert(sizeof(HIST_RECORD) == 20);
1486 assert(sizeof(DEF_RECORD) == 40);
1487 assert(sizeof(INDEX_RECORD) == 12);
1488 assert(sizeof(TAG) == 40);
1489 assert(sizeof(KEY) == 68);
1490 assert(sizeof(KEYLIST) == 12);
1491 assert(sizeof(OPEN_RECORD) == 8);
1492 assert(sizeof(DATABASE_CLIENT) == 2112);
1493 assert(sizeof(DATABASE_HEADER) == 135232);
1494 assert(sizeof(EVENT_HEADER) == 16);
1495 //assert(sizeof(EQUIPMENT_INFO) == 696); has been moved to dynamic checking inside mhttpd.c
1496 assert(sizeof(EQUIPMENT_STATS) == 24);
1497 assert(sizeof(BANK_HEADER) == 8);
1498 assert(sizeof(BANK) == 8);
1499 assert(sizeof(BANK32) == 12);
1500 assert(sizeof(ANA_OUTPUT_INFO) == 792);
1501 assert(sizeof(PROGRAM_INFO) == 316);
1502 assert(sizeof(ALARM) == 460);
1503 assert(sizeof(ALARM_CLASS) == 352);
1504 //assert(sizeof(CHN_SETTINGS) == 648); // ODB v3
1505 //assert(sizeof(CHN_STATISTICS) == 56); // ODB v3
1506#endif
1507}
1508
1518
1520{
1521 int found = 0;
1522 int count = 0;
1523 int status;
1524 int k;
1526
1527 KEY* pkey = (KEY*)xpkey; // drop "const": we already have "allow_write"
1528
1529 HNDLE hKey = db_pkey_to_hkey(uorp->pheader, pkey);
1530
1531 for (k=0; k<uorp->num_keys; k++)
1532 if (uorp->hkeys[k] == hKey) {
1533 found = 1;
1534 count = uorp->counts[k];
1535 break;
1536 }
1537
1538 if (pkey->notify_count == 0 && !found)
1539 return DB_SUCCESS; // no open record here
1540
1541 std::string path = db_get_path_locked(uorp->pheader, hKey);
1542 if (path == "") {
1543 db_msg(msg, MINFO, "db_update_open_record", "Invalid hKey %d", hKey);
1544 return DB_SUCCESS;
1545 }
1546
1547 //if (!db_validate_hkey(uorp->pheader, hKey)) {
1548 // cm_msg(MINFO, "db_update_open_record", "Invalid hKey %d", hKey);
1549 // return DB_SUCCESS;
1550 //}
1551 //
1552 //KEY* pkey = (KEY *) ((char *) uorp->pheader + hKey);
1553
1554 //printf("path [%s], type %d, notify_count %d\n", path, pkey->type, pkey->notify_count);
1555
1556 // extra check: are we looking at the same key?
1557 //assert(xkey->notify_count == pkey->notify_count);
1558
1559#if 0
1560 printf("%s, notify_count %d, found %d, our count %d\n", path, pkey->notify_count, found, count);
1561#endif
1562
1563 if (pkey->notify_count==0 && found) {
1564 db_msg(msg, MINFO, "db_update_open_record", "Added missing open record flag to \"%s\"", path.c_str());
1565 pkey->notify_count = count;
1566 uorp->num_modified++;
1567 return DB_SUCCESS;
1568 }
1569
1570 if (pkey->notify_count!=0 && !found) {
1571 db_msg(msg, MINFO, "db_update_open_record", "Removed open record flag from \"%s\"", path.c_str());
1572 pkey->notify_count = 0;
1573 uorp->num_modified++;
1574
1575 if (pkey->access_mode | MODE_EXCLUSIVE) {
1576 status = db_set_mode_wlocked(uorp->pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 1, msg);
1577 if (status != DB_SUCCESS) {
1578 db_msg(msg, MERROR, "db_update_open_record", "Cannot remove exclusive access mode from \"%s\", db_set_mode() status %d", path.c_str(), status);
1579 return DB_SUCCESS;
1580 }
1581 db_msg(msg, MINFO, "db_update_open_record", "Removed exclusive access mode from \"%s\"", path.c_str());
1582 }
1583 return DB_SUCCESS;
1584 }
1585
1586 if (pkey->notify_count != uorp->counts[k]) {
1587 db_msg(msg, MINFO, "db_update_open_record", "Updated notify_count of \"%s\" from %d to %d", path.c_str(), pkey->notify_count, count);
1588 pkey->notify_count = count;
1589 uorp->num_modified++;
1590 return DB_SUCCESS;
1591 }
1592
1593 return DB_SUCCESS;
1594}
1595
1597{
1598 int status = DB_SUCCESS;
1600 int i, j, k;
1601
1603 uor.num_keys = 0;
1604 uor.hkeys = (HNDLE*)calloc(uor.max_keys, sizeof(HNDLE));
1605 uor.counts = (int*)calloc(uor.max_keys, sizeof(int));
1606 uor.modes = (int*)calloc(uor.max_keys, sizeof(int));
1607 uor.num_modified = 0;
1608
1609 assert(uor.hkeys != NULL);
1610 assert(uor.counts != NULL);
1611 assert(uor.modes != NULL);
1612
1613 uor.pheader = pheader;
1614
1615 for (i = 0; i < pheader->max_client_index; i++) {
1616 DATABASE_CLIENT* pclient = &pheader->client[i];
1617 for (j = 0; j < pclient->max_index; j++)
1618 if (pclient->open_record[j].handle) {
1619 int found = 0;
1620 for (k=0; k<uor.num_keys; k++) {
1621 if (uor.hkeys[k] == pclient->open_record[j].handle) {
1622 uor.counts[k]++;
1623 found = 1;
1624 break;
1625 }
1626 }
1627 if (!found) {
1628 uor.hkeys[uor.num_keys] = pclient->open_record[j].handle;
1629 uor.counts[uor.num_keys] = 1;
1630 uor.modes[uor.num_keys] = pclient->open_record[j].access_mode;
1631 uor.num_keys++;
1632 }
1633 }
1634 }
1635
1636#if 0
1637 for (i=0; i<uor.num_keys; i++)
1638 printf("index %d, handle %d, count %d, access mode %d\n", i, uor.hkeys[i], uor.counts[i], uor.modes[i]);
1639#endif
1640
1641 const KEY* proot = db_get_pkey(pheader, 0, &status, "db_validate_open_record", msg);
1642
1643 if (proot) {
1645 }
1646
1647 if (uor.num_modified) {
1648 db_msg(msg, MINFO, "db_validate_open_records", "Corrected %d ODB entries", uor.num_modified);
1649 }
1650
1651 free(uor.hkeys);
1652 free(uor.counts);
1653 free(uor.modes);
1654
1655 return status;
1656}
1657
1658/*------------------------------------------------------------------*/
1660{
1661 int total_size_key = 0;
1662 int total_size_data = 0;
1663 double ratio;
1665 bool flag = true;
1666
1667 /* validate size of data structures (miscompiled, 32/64 bit mismatch, etc */
1668
1670
1671 /* validate the key free list */
1672
1673 if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
1674 db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, invalid pheader->first_free_key 0x%08X", pheader->first_free_key - (int)sizeof(DATABASE_HEADER));
1675 return false;
1676 }
1677
1678 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
1679
1680 while ((POINTER_T) pfree != (POINTER_T) pheader) {
1681
1682 if (pfree->next_free != 0 && !db_validate_key_offset(pheader, pfree->next_free)) {
1683 db_msg(msg, MERROR, "db_validate_db", "Warning: database corruption, invalid key area next_free 0x%08X", pfree->next_free - (int)sizeof(DATABASE_HEADER));
1684 flag = false;
1685 break;
1686 }
1687
1688 total_size_key += pfree->size;
1689 FREE_DESCRIP *nextpfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
1690
1691 if (pfree->next_free != 0 && nextpfree == pfree) {
1692 db_msg(msg, MERROR, "db_validate_db", "Warning: database corruption, key area next_free 0x%08X is same as current free %p, truncating the free list", pfree->next_free, pfree - (int)sizeof(DATABASE_HEADER));
1693 pfree->next_free = 0;
1694 flag = false;
1695 break;
1696 //return false;
1697 }
1698
1699 pfree = nextpfree;
1700 }
1701
1702 ratio = ((double) (pheader->key_size - total_size_key)) / ((double) pheader->key_size);
1703 if (ratio > 0.9)
1704 db_msg(msg, MERROR, "db_validate_db", "Warning: database key area is %.0f%% full", ratio * 100.0);
1705
1706 if (total_size_key > pheader->key_size) {
1707 db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, total_key_size 0x%08X bigger than pheader->key_size 0x%08X", total_size_key, pheader->key_size);
1708 flag = false;
1709 }
1710
1711 /* validate the data free list */
1712
1713 if (!db_validate_data_offset(pheader, pheader->first_free_data)) {
1714 db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, invalid pheader->first_free_data 0x%08X", pheader->first_free_data - (int)sizeof(DATABASE_HEADER));
1715 return false;
1716 }
1717
1718 //printf("pheader %p, first_free_data %d, key size %d, data size %d\n", pheader, pheader->first_free_data, pheader->key_size, pheader->data_size);
1719
1720 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
1721
1722 while ((POINTER_T) pfree != (POINTER_T) pheader) {
1723
1724 if (pfree->next_free != 0 && !db_validate_data_offset(pheader, pfree->next_free)) {
1725 db_msg(msg, MERROR, "db_validate_db", "Warning: database corruption, invalid data area next_free 0x%08X", pfree->next_free - (int)sizeof(DATABASE_HEADER));
1726 flag = false;
1727 break;
1728 //return false;
1729 }
1730
1731 total_size_data += pfree->size;
1732 FREE_DESCRIP *nextpfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
1733
1734 if (pfree->next_free != 0 && nextpfree == pfree) {
1735 db_msg(msg, MERROR, "db_validate_db", "Warning: database corruption, data area next_free 0x%08X is same as current free %p, truncating the free list", pfree->next_free, pfree - (int)sizeof(DATABASE_HEADER));
1736 pfree->next_free = 0;
1737 flag = false;
1738 break;
1739 //return false;
1740 }
1741
1742 pfree = nextpfree;
1743 }
1744
1745 ratio = ((double) (pheader->data_size - total_size_data)) / ((double) pheader->data_size);
1746 if (ratio > 0.9)
1747 db_msg(msg, MERROR, "db_validate_db", "Warning: database data area is %.0f%% full", ratio * 100.0);
1748
1749 if (total_size_data > pheader->data_size) {
1750 db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, total_size_data 0x%08X bigger than pheader->data_size 0x%08X", total_size_key, pheader->data_size);
1751 flag = false;
1752 //return false;
1753 }
1754
1755 /* validate the tree of keys, starting from the root key */
1756
1757 if (!db_validate_key_offset(pheader, pheader->root_key)) {
1758 db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, pheader->root_key 0x%08X is invalid", pheader->root_key - (int)sizeof(DATABASE_HEADER));
1759 return false;
1760 }
1761
1762 flag &= db_validate_and_repair_key_wlocked(pheader, 1, "", 0, pheader->root_key, (KEY *) ((char *) pheader + pheader->root_key), msg);
1763
1764 if (!flag) {
1765 db_msg(msg, MERROR, "db_validate_db", "Error: ODB corruption detected, see previous messages");
1766 }
1767
1768 return flag;
1769}
1770
1771#endif // LOCAL_ROUTINES
1772
1774#endif /* DOXYGEN_SHOULD_SKIP_THIS */
1775
1776/********************************************************************/
1787INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE * hDB, const char *client_name)
1788{
1789 if (rpc_is_remote())
1791
1792#ifdef LOCAL_ROUTINES
1793 {
1794 INT i, status;
1795 HNDLE handle;
1798 DATABASE_HEADER *pheader;
1799 KEY *pkey;
1803 DWORD timeout;
1805
1806 /* restrict name length */
1808
1809 int odb_size_limit = 100*1000*1000;
1811 cm_msg(MERROR, "db_open_database", "invalid database size: %d bytes. ODB size limit is %d bytes", database_size, odb_size_limit);
1812 return DB_INVALID_PARAM;
1813 }
1814
1815 if (strlen(client_name) >= NAME_LENGTH) {
1816 cm_msg(MERROR, "db_open_database", "client name \'%s\' is longer than %d characters", client_name, NAME_LENGTH-1);
1817 return DB_INVALID_PARAM;
1818 }
1819
1820 if (strchr(client_name, '/') != NULL) {
1821 cm_msg(MERROR, "db_open_database", "client name \'%s\' should not contain the slash \'/\' character", client_name);
1822 return DB_INVALID_PARAM;
1823 }
1824
1825 /* allocate new space for the new database descriptor */
1826 if (_database_entries == 0) {
1827 _database = (DATABASE *) malloc(sizeof(DATABASE));
1828 memset(_database, 0, sizeof(DATABASE));
1829 if (_database == NULL) {
1830 *hDB = 0;
1831 return DB_NO_MEMORY;
1832 }
1833
1835 i = 0;
1836 } else {
1837 /* check if database already open */
1838 for (i = 0; i < _database_entries; i++)
1839 if (_database[i].attached && equal_ustring(_database[i].name, database_name)) {
1840 /* check if database belongs to this thread */
1841 *hDB = i + 1;
1842 return DB_SUCCESS;
1843 }
1844
1845 /* check for a deleted entry */
1846 for (i = 0; i < _database_entries; i++)
1847 if (!_database[i].attached)
1848 break;
1849
1850 /* if not found, create new one */
1851 if (i == _database_entries) {
1854
1856 if (_database == NULL) {
1858 *hDB = 0;
1859 return DB_NO_MEMORY;
1860 }
1861 }
1862 }
1863
1864 handle = (HNDLE) i;
1865
1866 /* open shared memory region */
1867 void* shm_adr = NULL;
1868 size_t shm_size = 0;
1869 HNDLE shm_handle;
1870
1871 status = ss_shm_open(database_name, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2), &shm_adr, &shm_size, &shm_handle, TRUE);
1872
1873 if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
1874 *hDB = 0;
1875 return DB_INVALID_NAME;
1876 }
1877
1878 _database[handle].shm_adr = shm_adr;
1879 _database[handle].shm_size = shm_size;
1880 _database[handle].shm_handle = shm_handle;
1881
1882 _database[handle].database_header = (DATABASE_HEADER *) shm_adr;
1883
1884 /* shortcut to header */
1885 pheader = _database[handle].database_header;
1886
1887 /* save name */
1888 strcpy(_database[handle].name, database_name);
1889
1891
1892 /* clear memeory for debugging */
1893 /* memset(pheader, 0, sizeof(DATABASE_HEADER) + 2*ALIGN8(database_size/2)); */
1894
1895 if (shm_created && pheader->name[0] == 0) {
1896 /* setup header info if database was created */
1897 memset(pheader, 0, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2));
1898
1899 strcpy(pheader->name, database_name);
1900 pheader->version = DATABASE_VERSION;
1901 pheader->key_size = ALIGN8(database_size / 2);
1902 pheader->data_size = ALIGN8(database_size / 2);
1903 pheader->root_key = sizeof(DATABASE_HEADER);
1904 pheader->first_free_key = sizeof(DATABASE_HEADER);
1905 pheader->first_free_data = sizeof(DATABASE_HEADER) + pheader->key_size;
1906
1907 /* set up free list */
1908 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
1909 pfree->size = pheader->key_size;
1910 pfree->next_free = 0;
1911
1912 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
1913 pfree->size = pheader->data_size;
1914 pfree->next_free = 0;
1915
1916 /* create root key */
1917 pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_open_database_A");
1918 assert(pkey);
1919
1920 /* set key properties */
1921 pkey->type = TID_KEY;
1922 pkey->num_values = 1;
1923 pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
1924 strcpy(pkey->name, "root");
1925 pkey->parent_keylist = 0;
1926
1927 /* create keylist */
1928 pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST), "db_open_database_B");
1929 assert(pkeylist);
1930
1931 /* store keylist in data field */
1932 pkey->data = (POINTER_T) pkeylist - (POINTER_T) pheader;
1933 pkey->item_size = sizeof(KEYLIST);
1934 pkey->total_size = sizeof(KEYLIST);
1935
1936 pkeylist->parent = (POINTER_T) pkey - (POINTER_T) pheader;
1937 pkeylist->num_keys = 0;
1938 pkeylist->first_key = 0;
1939 }
1940
1941 /* check database version */
1942 if (pheader->version != DATABASE_VERSION) {
1943 cm_msg(MERROR, "db_open_database",
1944 "Different database format: Shared memory is %d, program is %d", pheader->version, DATABASE_VERSION);
1945 return DB_VERSION_MISMATCH;
1946 }
1947
1948 /* check database size vs shared memory size */
1949 if (_database[handle].shm_size < (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size) {
1950 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);
1951 return DB_VERSION_MISMATCH;
1952 }
1953
1954 /* check root key */
1955 if (!db_validate_key_offset(pheader, pheader->root_key)) {
1956 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key offset %d is invalid", pheader->root_key);
1957 return DB_VERSION_MISMATCH;
1958 } else {
1959 pkey = (KEY*)((char*)pheader + pheader->root_key);
1960
1961 if (pkey->type != TID_KEY) {
1962 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key type %d is not TID_KEY", pkey->type);
1963 return DB_VERSION_MISMATCH;
1964 }
1965
1966 if (strcmp(pkey->name, "root") != 0) {
1967 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key name \"%s\" is not \"root\"", pkey->name);
1968 return DB_VERSION_MISMATCH;
1969 }
1970
1971 // what if we are connecting to an incompatible ODB?
1972 // A call to db_validate_and_repair_key() maybe will
1973 // corrupt it here. But we have no choice,
1974 // if we skip it here and continue,
1975 // db_validate_and_repair_db() will call it later anyway... K.O.
1976
1977 db_err_msg* msg = NULL;
1978 bool ok = db_validate_and_repair_key_wlocked(pheader, 0, "", 0, pheader->root_key, pkey, &msg);
1979 if (msg)
1980 db_flush_msg(&msg);
1981 if (!ok) {
1982 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key is invalid");
1983 return DB_VERSION_MISMATCH;
1984 }
1985 }
1986
1987 /* set default mutex and semaphore timeout */
1988 _database[handle].timeout = 10000;
1989
1990 /* create mutexes for the database */
1991 status = ss_mutex_create(&_database[handle].mutex, TRUE);
1992 if (status != SS_SUCCESS && status != SS_CREATED) {
1993 *hDB = 0;
1994 return DB_NO_SEMAPHORE;
1995 }
1996
1997 /* create semaphore for the database */
1998 status = ss_semaphore_create(database_name, &(_database[handle].semaphore));
1999 if (status != SS_SUCCESS && status != SS_CREATED) {
2000 *hDB = 0;
2001 return DB_NO_SEMAPHORE;
2002 }
2003 _database[handle].lock_cnt = 0;
2004
2005 _database[handle].protect = FALSE;
2006 _database[handle].protect_read = FALSE;
2007 _database[handle].protect_write = FALSE;
2008
2009 /* first lock database */
2010 status = db_lock_database(handle + 1);
2011 if (status != DB_SUCCESS)
2012 return status;
2013
2014 /* we have the database locked, without write protection */
2015
2016 /*
2017 Now we have a DATABASE_HEADER, so let's setup a CLIENT
2018 structure in that database. The information there can also
2019 be seen by other processes.
2020 */
2021
2022 /*
2023 update the client count
2024 */
2025 int num_clients = 0;
2026 int max_client_index = 0;
2027 for (i = 0; i < MAX_CLIENTS; i++) {
2028 if (pheader->client[i].pid == 0)
2029 continue;
2030 num_clients++;
2031 max_client_index = i + 1;
2032 }
2033 pheader->num_clients = num_clients;
2034 pheader->max_client_index = max_client_index;
2035
2036 /*fprintf(stderr,"num_clients: %d, max_client: %d\n",pheader->num_clients,pheader->max_client_index); */
2037
2038 /* remove dead clients */
2039 for (i = 0; i < MAX_CLIENTS; i++) {
2040 if (pheader->client[i].pid == 0)
2041 continue;
2042 if (!ss_pid_exists(pheader->client[i].pid)) {
2044 int client_pid;
2045
2046 mstrlcpy(client_name_tmp, pheader->client[i].name, sizeof(client_name_tmp));
2047 client_pid = pheader->client[i].pid;
2048
2049 // removed: /* decrement notify_count for open records and clear exclusive mode */
2050 // open records are corrected later, by db_validate_open_records()
2051
2052 /* clear entry from client structure in database header */
2053 memset(&(pheader->client[i]), 0, sizeof(DATABASE_CLIENT));
2054
2055 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);
2056 }
2057 }
2058
2059 /*
2060 Look for empty client slot
2061 */
2062 for (i = 0; i < MAX_CLIENTS; i++)
2063 if (pheader->client[i].pid == 0)
2064 break;
2065
2066 if (i == MAX_CLIENTS) {
2067 db_unlock_database(handle + 1);
2068 *hDB = 0;
2069 cm_msg(MERROR, "db_open_database", "maximum number of clients exceeded");
2070 return DB_NO_SLOT;
2071 }
2072
2073 /* store slot index in _database structure */
2074 _database[handle].client_index = i;
2075
2076 /*
2077 Save the index of the last client of that database so that later only
2078 the clients 0..max_client_index-1 have to be searched through.
2079 */
2080 pheader->num_clients++;
2081 if (i + 1 > pheader->max_client_index)
2082 pheader->max_client_index = i + 1;
2083
2084 /* setup database header and client structure */
2085 pclient = &pheader->client[i];
2086
2087 memset(pclient, 0, sizeof(DATABASE_CLIENT));
2088 mstrlcpy(pclient->name, client_name, sizeof(pclient->name));
2089 pclient->pid = ss_getpid();
2090 pclient->num_open_records = 0;
2091
2093
2094 pclient->last_activity = ss_millitime();
2095
2097 pclient->watchdog_timeout = timeout;
2098
2099 /* check ODB for corruption */
2100 db_err_msg* msg = NULL;
2101 bool ok = db_validate_and_repair_db_wlocked(pheader, &msg);
2102 if (msg)
2103 db_flush_msg(&msg);
2104 if (!ok) {
2105 /* do not treat corrupted odb as a fatal error- allow the user
2106 to preceed at own risk- the database is already corrupted,
2107 so no further harm can possibly be made. */
2108 /*
2109 db_unlock_database(handle + 1);
2110 *hDB = 0;
2111 return DB_CORRUPTED;
2112 */
2113 }
2114
2115 /* setup _database entry */
2116 _database[handle].database_data = _database[handle].database_header + 1;
2117 _database[handle].attached = TRUE;
2118 _database[handle].protect = FALSE;
2119 _database[handle].protect_read = FALSE;
2120 _database[handle].protect_write = FALSE;
2121
2122 *hDB = (handle + 1);
2123
2125 if (status != DB_SUCCESS) {
2126 db_unlock_database(handle + 1);
2127 if (msg)
2128 db_flush_msg(&msg);
2129 cm_msg(MERROR, "db_open_database", "Error: db_validate_open_records() status %d", status);
2130 return status;
2131 }
2132
2133 db_unlock_database(handle + 1);
2134
2135 if (msg)
2136 db_flush_msg(&msg);
2137
2138 if (shm_created)
2139 return DB_CREATED;
2140 }
2141#endif /* LOCAL_ROUTINES */
2142
2143 return DB_SUCCESS;
2144}
2145
2146/********************************************************************/
2153{
2154 if (rpc_is_remote())
2156
2157#ifdef LOCAL_ROUTINES
2158 else {
2159 INT destroy_flag, i, j;
2160 char xname[256];
2161
2162 if (hDB > _database_entries || hDB <= 0) {
2163 cm_msg(MERROR, "db_close_database", "invalid database handle %d", hDB);
2164 return DB_INVALID_HANDLE;
2165 }
2166
2167 /* flush database to disk */
2169
2170 /* first lock database */
2172
2173 DATABASE* pdb = &_database[hDB - 1];
2174
2175 if (!pdb->attached) {
2177 cm_msg(MERROR, "db_close_database", "database not attached");
2178 return DB_INVALID_HANDLE;
2179 }
2180
2181 DATABASE_HEADER *pheader = pdb->database_header;
2183
2184 db_allow_write_locked(&_database[hDB-1], "db_close_database");
2185
2186 /* close all open records */
2187 for (i = 0; i < pclient->max_index; i++)
2188 if (pclient->open_record[i].handle)
2189 db_remove_open_record_wlocked(pdb, pheader, pclient->open_record[i].handle);
2190
2191 /* mark entry in _database as empty */
2192 pdb->attached = FALSE;
2193
2194 /* clear entry from client structure in database header */
2195 memset(pclient, 0, sizeof(DATABASE_CLIENT));
2196
2197 /* calculate new max_client_index entry */
2198 for (i = MAX_CLIENTS - 1; i >= 0; i--)
2199 if (pheader->client[i].pid != 0)
2200 break;
2201 pheader->max_client_index = i + 1;
2202
2203 /* count new number of clients */
2204 for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
2205 if (pheader->client[i].pid != 0)
2206 j++;
2207 pheader->num_clients = j;
2208
2209 destroy_flag = (pheader->num_clients == 0);
2210
2211 /* flush shared memory to disk before it gets deleted */
2212 if (destroy_flag)
2213 ss_shm_flush(pheader->name, pdb->shm_adr, pdb->shm_size, pdb->shm_handle, true);
2214
2215 mstrlcpy(xname, pheader->name, sizeof(xname));
2216
2217 /* unmap shared memory, delete it if we are the last */
2218 ss_shm_close(xname, pdb->shm_adr, pdb->shm_size, pdb->shm_handle, destroy_flag);
2219
2220 pheader = NULL; // after ss_shm_close(), pheader points nowhere
2221 pdb->database_header = NULL; // ditto
2222
2223 /* unlock database */
2225
2226 /* delete semaphore */
2228
2229 /* delete mutex */
2230 ss_mutex_delete(pdb->mutex);
2231
2232 /* update _database_entries */
2233 if (hDB == _database_entries)
2235
2236 if (_database_entries > 0)
2238 else {
2239 free(_database);
2240 _database = NULL;
2241 }
2242
2243 /* if we are the last one, also delete other semaphores */
2244 if (destroy_flag) {
2245 //extern INT _semaphore_elog, _semaphore_alarm, _semaphore_history, _semaphore_msg;
2247
2248 if (_semaphore_elog)
2250 if (_semaphore_alarm)
2254 //if (_semaphore_msg)
2255 // ss_semaphore_delete(_semaphore_msg, TRUE);
2256 }
2257
2258 }
2259#endif /* LOCAL_ROUTINES */
2260
2261 return DB_SUCCESS;
2262}
2263
2265#ifndef DOXYGEN_SHOULD_SKIP_THIS
2266
2267/*------------------------------------------------------------------*/
2269/********************************************************************\
2270
2271 Routine: db_flush_database
2272
2273 Purpose: Flushes the shared memory of a database to its disk file.
2274
2275 Input:
2276 HNDLE hDB Handle to the database, which is used as
2277 an index to the _database array.
2278
2279 Output:
2280 none
2281
2282 Function value:
2283 DB_SUCCESS Successful completion
2284 DB_INVALID_HANDLE Database handle is invalid
2285 RPC_NET_ERROR Network error
2286
2287\********************************************************************/
2288{
2289 if (rpc_is_remote())
2291
2292#ifdef LOCAL_ROUTINES
2293 else {
2294 int status, size;
2296 uint32_t last_flush = 0;
2297
2298 if (hDB > _database_entries || hDB <= 0) {
2299 cm_msg(MERROR, "db_flush_database", "invalid database handle");
2300 return DB_INVALID_HANDLE;
2301 }
2302
2303 /* create keys if not present */
2304 size = sizeof(flush_period);
2305 db_get_value(hDB, 0, "/System/Flush/Flush period", &flush_period, &size, TID_UINT32, true);
2306 size = sizeof(last_flush);
2307 db_get_value(hDB, 0, "/System/Flush/Last flush", &last_flush, &size, TID_UINT32, true);
2308
2309 HNDLE hkey;
2310 status = db_find_key(hDB, 0, "/System/Flush/Last flush", &hkey);
2311 if (status != DB_SUCCESS) {
2312 cm_msg(MERROR, "db_flush_database", "Cannot obtain key /System/Flush/Last flush");
2313 return DB_INVALID_HANDLE;
2314 }
2315
2317 DATABASE *pdb = &_database[hDB - 1];
2318 DATABASE_HEADER *pheader = pdb->database_header;
2319
2320 if (!_database[hDB - 1].attached) {
2322 cm_msg(MERROR, "db_flush_database", "invalid database handle");
2323 return DB_INVALID_HANDLE;
2324 }
2325
2326 db_err_msg *msg = nullptr;
2327
2328 const KEY *pkey = db_get_pkey(pheader, hkey, &status, "db_flush_database", &msg);
2329
2330 if (!pkey) {
2332 if (msg)
2333 db_flush_msg(&msg);
2334 return CM_NO_CLIENT;
2335 }
2336
2337 size = sizeof(last_flush);
2338 status = db_get_data_locked(pheader, pkey, 0, &last_flush, &size, TID_UINT32, &msg);
2339
2340 /* only flush if period has expired */
2341 if (ss_time() > last_flush + flush_period) {
2342 db_allow_write_locked(pdb, "db_flush_database");
2343
2344 /* update last flush time in ODB */
2345 last_flush = ss_time();
2346 db_set_value_wlocked(pheader, hDB, 0, "/System/Flush/Last flush", &last_flush, sizeof(last_flush), 1, TID_UINT32, &msg);
2347
2348 /* flush shared memory to disk */
2349 ss_shm_flush(pheader->name, _database[hDB - 1].shm_adr, _database[hDB - 1].shm_size, _database[hDB - 1].shm_handle, false);
2350 }
2351
2353 }
2354#endif /* LOCAL_ROUTINES */
2355
2356 return DB_SUCCESS;
2357}
2358
2359/*------------------------------------------------------------------*/
2361/********************************************************************\
2362
2363 Routine: db_close_all_databases
2364
2365 Purpose: Close all open databases and open records
2366
2367 Input:
2368 none
2369
2370 Output:
2371 none
2372
2373 Function value:
2374 DB_SUCCESS Successful completion
2375
2376\********************************************************************/
2377{
2378 INT status;
2379
2380 if (rpc_is_remote()) {
2382 if (status != DB_SUCCESS)
2383 return status;
2384 }
2385
2388
2389#ifdef LOCAL_ROUTINES
2390 {
2391 INT i;
2392
2393 for (i = _database_entries; i > 0; i--)
2395 }
2396#endif /* LOCAL_ROUTINES */
2397
2398 return DB_SUCCESS;
2399}
2400
2401/*------------------------------------------------------------------*/
2402INT db_set_client_name(HNDLE hDB, const char *client_name)
2403/********************************************************************\
2404
2405 Routine: db_set_client_name
2406
2407 Purpose: Set client name for a database. Used by cm_connect_experiment
2408 if a client name is duplicate and changed.
2409
2410 Input:
2411 INT hDB Handle to database
2412 char *client_name Name of this application
2413
2414 Output:
2415
2416 Function value:
2417 DB_SUCCESS Successful completion
2418 RPC_NET_ERROR Network error
2419
2420\********************************************************************/
2421{
2422 if (rpc_is_remote())
2423 return rpc_call(RPC_DB_SET_CLIENT_NAME, hDB, client_name);
2424
2425#ifdef LOCAL_ROUTINES
2426 {
2427 if (hDB > _database_entries || hDB <= 0) {
2428 cm_msg(MERROR, "db_set_client_name", "invalid database handle %d", hDB);
2429 return DB_INVALID_HANDLE;
2430 }
2431
2433
2434 DATABASE *pdb = &_database[hDB - 1];
2436 mstrlcpy(pclient->name, client_name, sizeof(pclient->name));
2437
2439 }
2440#endif /* LOCAL_ROUTINES */
2441
2442 return DB_SUCCESS;
2443}
2444
2446#endif /* DOXYGEN_SHOULD_SKIP_THIS */
2447
2448/********************************************************************/
2456{
2457#ifdef LOCAL_ROUTINES
2458 int status;
2459
2460 if (hDB > _database_entries || hDB <= 0) {
2461 cm_msg(MERROR, "db_lock_database", "invalid database handle %d, aborting...", hDB);
2462 abort();
2463 return DB_INVALID_HANDLE;
2464 }
2465
2466 /* obtain access mutex in multi-thread applications */
2467 status = ss_mutex_wait_for(_database[hDB - 1].mutex, _database[hDB - 1].timeout);
2468 if (status != SS_SUCCESS) {
2469 cm_msg(MERROR, "db_lock_database", "cannot lock ODB mutex, timeout %d ms, status %d, aborting...", _database[hDB - 1].timeout, status);
2470 abort();
2471 }
2472
2473 /* protect this function against recursive call from signal handlers */
2474 if (_database[hDB - 1].inside_lock_unlock) {
2475 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");
2476 abort();
2477 }
2478
2480
2481 //static int x = 0;
2482 //x++;
2483 //if (x > 5000) {
2484 // printf("inside db_lock_database(), press Ctrl-C now!\n");
2485 // sleep(5);
2486 //}
2487
2488 // test recursive locking
2489 // static int out=0;
2490 // out++;
2491 // printf("HERE %d!\n", out);
2492 // if (out>10) abort();
2493 // db_lock_database(hDB);
2494 // printf("OUT %d!\n", out);
2495
2496 if (_database[hDB - 1].lock_cnt == 0) {
2497 _database[hDB - 1].lock_cnt = 1;
2498 /* wait max. 5 minutes for semaphore (required if locking process is being debugged) */
2499 status = ss_semaphore_wait_for(_database[hDB - 1].semaphore, _database[hDB - 1].timeout);
2500 if (status == SS_TIMEOUT) {
2501 cm_msg(MERROR, "db_lock_database", "cannot lock ODB semaphore, timeout %d ms, aborting...", _database[hDB - 1].timeout);
2502 //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.
2503 abort();
2504 }
2505 if (status != SS_SUCCESS) {
2506 cm_msg(MERROR, "db_lock_database", "cannot lock ODB semaphore, timeout %d ms, ss_semaphore_wait_for() status %d, aborting...", _database[hDB - 1].timeout, status);
2507 abort();
2508 }
2509 } else {
2510 _database[hDB - 1].lock_cnt++; // we have already the lock (recursive call), so just increase counter
2511 }
2512
2513#ifdef CHECK_LOCK_COUNT
2514 {
2515 char str[256];
2516
2517 sprintf(str, "db_lock_database, lock_cnt=%d", _database[hDB - 1].lock_cnt);
2519 }
2520#endif
2521
2522 if (_database[hDB - 1].protect) {
2523 if (_database[hDB - 1].database_header == NULL) {
2524 int status;
2525 assert(!_database[hDB - 1].protect_read);
2526 assert(!_database[hDB - 1].protect_write);
2527 status = ss_shm_unprotect(_database[hDB - 1].shm_handle, &_database[hDB - 1].shm_adr, _database[hDB - 1].shm_size, TRUE, FALSE, "db_lock_database");
2528 if (status != SS_SUCCESS) {
2529 cm_msg(MERROR, "db_lock_database", "cannot lock ODB, ss_shm_unprotect(TRUE,FALSE) failed with status %d, aborting...", status);
2531 ss_semaphore_release(_database[hDB - 1].semaphore);
2532 abort();
2533 }
2534 _database[hDB - 1].database_header = (DATABASE_HEADER *) _database[hDB - 1].shm_adr;
2537 }
2538 }
2539
2541
2542#endif /* LOCAL_ROUTINES */
2543
2544 return DB_SUCCESS;
2545}
2546
2547#ifdef LOCAL_ROUTINES
2549{
2550 assert(p);
2551 if (p->protect && !p->protect_write) {
2552 int status;
2553 assert(p->lock_cnt > 0);
2554 assert(p->database_header != NULL);
2555 assert(p->protect_read);
2557 if (status != SS_SUCCESS) {
2558 cm_msg(MERROR, "db_allow_write_locked", "cannot write to ODB, ss_shm_unprotect(TRUE,TRUE) failed with status %d, aborting...", status);
2561 abort();
2562 }
2564 p->protect_read = TRUE;
2565 p->protect_write = TRUE;
2566 }
2567 return DB_SUCCESS;
2568}
2569#endif /* LOCAL_ROUTINES */
2570
2571/********************************************************************/
2578{
2579#ifdef LOCAL_ROUTINES
2580
2581 if (hDB > _database_entries || hDB <= 0) {
2582 cm_msg(MERROR, "db_unlock_database", "invalid database handle %d", hDB);
2583 return DB_INVALID_HANDLE;
2584 }
2585#ifdef CHECK_LOCK_COUNT
2586 {
2587 char str[256];
2588
2589 sprintf(str, "db_unlock_database, lock_cnt=%d", _database[hDB - 1].lock_cnt);
2591 }
2592#endif
2593
2594 /* protect this function against recursive call from signal handlers */
2595 if (_database[hDB - 1].inside_lock_unlock) {
2596 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");
2597 abort();
2598 }
2599
2601
2602 //static int x = 0;
2603 //x++;
2604 //if (x > 5000) {
2605 // printf("inside db_unlock_database(), press Ctrl-C now!\n");
2606 // sleep(5);
2607 //}
2608
2609 if (_database[hDB - 1].lock_cnt == 1) {
2610 ss_semaphore_release(_database[hDB - 1].semaphore);
2611
2612 if (_database[hDB - 1].protect && _database[hDB - 1].database_header) {
2613 int status;
2614 assert(_database[hDB - 1].protect_read);
2615 assert(_database[hDB - 1].database_header);
2617 status = ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].shm_adr, _database[hDB - 1].shm_size);
2618 if (status != SS_SUCCESS) {
2619 cm_msg(MERROR, "db_unlock_database", "cannot unlock ODB, ss_shm_protect() failed with status %d, aborting...", status);
2621 abort();
2622 }
2625 }
2626 }
2627
2628 assert(_database[hDB - 1].lock_cnt > 0);
2629 _database[hDB - 1].lock_cnt--;
2630
2632
2633 /* release mutex for multi-thread applications */
2634 ss_mutex_release(_database[hDB - 1].mutex);
2635
2636#endif /* LOCAL_ROUTINES */
2637 return DB_SUCCESS;
2638}
2639
2640/********************************************************************/
2641#if 0
2643{
2644#ifdef LOCAL_ROUTINES
2645
2646 /* return zero if no ODB is open or we run remotely */
2647 if (_database_entries == 0)
2648 return 0;
2649
2650 if (hDB > _database_entries || hDB <= 0) {
2651 cm_msg(MERROR, "db_get_lock_cnt", "invalid database handle %d, aborting...", hDB);
2652 fprintf(stderr, "db_get_lock_cnt: invalid database handle %d, aborting...\n", hDB);
2653 abort();
2654 return DB_INVALID_HANDLE;
2655 }
2656
2657 return _database[hDB - 1].lock_cnt;
2658#else
2659 return 0;
2660#endif
2661}
2662#endif
2663
2665{
2666#ifdef LOCAL_ROUTINES
2667
2668 /* return zero if no ODB is open or we run remotely */
2669 if (_database_entries == 0)
2670 return 0;
2671
2672 if (hDB > _database_entries || hDB <= 0) {
2673 cm_msg(MERROR, "db_set_lock_timeout", "invalid database handle %d", hDB);
2674 return 0;
2675 }
2676
2677 if (timeout_millisec > 0) {
2679 }
2680
2681 return _database[hDB - 1].timeout;
2682#else
2683 return 0;
2684#endif
2685}
2686
2687#ifdef LOCAL_ROUTINES
2688
2693{
2694 bool found = false;
2695 int pid = ss_getpid();
2696 for (int i = 0; i < _database_entries; i++) {
2697 if (_database[i].attached) {
2698 db_lock_database(i + 1);
2699 db_allow_write_locked(&_database[i], "db_update_last_activity");
2700 assert(_database[i].database_header);
2701 /* update the last_activity entry to show that we are alive */
2702 for (int j=0; j<_database[i].database_header->max_client_index; j++) {
2704 //printf("client %d pid %d vs our pid %d\n", j, pdbclient->pid, pid);
2705 if (pdbclient->pid == pid) {
2707 found = true;
2708 }
2709 }
2710 db_unlock_database(i + 1);
2711 }
2712 }
2713
2714 if (!found) {
2715 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...");
2716 abort();
2717 }
2718
2719 return DB_SUCCESS;
2720}
2721
2722#endif // LOCAL_ROUTINES
2723
2724#ifdef LOCAL_ROUTINES
2726{
2728
2729 /* decrement notify_count for open records and clear exclusive mode */
2730 int k;
2731 for (k = 0; k < pdbclient->max_index; k++)
2732 if (pdbclient->open_record[k].handle) {
2733 KEY* pkey = (KEY *) ((char *) pheader + pdbclient->open_record[k].handle);
2734 if (pkey->notify_count > 0)
2735 pkey->notify_count--;
2736
2737 if (pdbclient->open_record[k].access_mode & MODE_WRITE)
2738 db_set_mode_wlocked(pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2, msg);
2739 }
2740
2741 /* clear entry from client structure in buffer header */
2742 memset(pdbclient, 0, sizeof(DATABASE_CLIENT));
2743
2744 /* calculate new max_client_index entry */
2745 for (k = MAX_CLIENTS - 1; k >= 0; k--)
2746 if (pheader->client[k].pid != 0)
2747 break;
2748 pheader->max_client_index = k + 1;
2749
2750 /* count new number of clients */
2751 int nc;
2752 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
2753 if (pheader->client[k].pid != 0)
2754 nc++;
2755 pheader->num_clients = nc;
2756}
2757#endif
2758
2759#ifdef LOCAL_ROUTINES
2761{
2762 if (!pid)
2763 pid = ss_getpid();
2764
2765 char str[256];
2766 int status = 0;
2767
2768 sprintf(str, "System/Clients/%0d", pid);
2769 KEY* pkey = (KEY*)db_find_pkey_locked(pheader, NULL, str, &status, msg);
2770 if (!pkey) {
2771 return status;
2772 }
2773
2775 HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
2777 int32_t data = 0;
2778 db_set_value_wlocked(pheader, hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32, msg);
2779
2780 return status;
2781}
2782#endif
2783
2784/********************************************************************/
2792{
2793#ifdef LOCAL_ROUTINES
2794 if (hDB > _database_entries || hDB <= 0) {
2795 cm_msg(MERROR, "db_delete_client_info", "invalid database handle");
2796 return DB_INVALID_HANDLE;
2797 }
2798
2799 if (!_database[hDB - 1].attached) {
2800 cm_msg(MERROR, "db_delete_client_info", "invalid database handle");
2801 return DB_INVALID_HANDLE;
2802 }
2803
2804 /* lock database */
2806
2807 DATABASE *pdb = &_database[hDB - 1];
2808 DATABASE_HEADER *pheader = pdb->database_header;
2809
2810 db_allow_write_locked(pdb, "db_delete_client_info");
2811
2812 db_err_msg* msg = NULL;
2813
2814 int status = db_delete_client_info_wlocked(hDB, pheader, pid, &msg);
2815
2817
2818 if (msg)
2819 db_flush_msg(&msg);
2820
2821 return status;
2822#else
2823 return DB_SUCCESS;
2824#endif
2825}
2826
2828{
2829#ifdef LOCAL_ROUTINES
2830 int i;
2831 /* check online databases */
2832 for (i = 0; i < _database_entries; i++) {
2833 DATABASE *pdb = &_database[i];
2834 if (!pdb->attached) // empty slot
2835 continue;
2836
2837 db_lock_database(i + 1);
2838 db_allow_write_locked(pdb, "db_cleanup");
2839
2840 DATABASE_HEADER* pheader = pdb->database_header;
2842
2843 /* update the last_activity entry to show that we are alive */
2844 pclient->last_activity = actual_time;
2845
2846 /* don't check other clients if interval is stange */
2847 if (wrong_interval) {
2848 db_unlock_database(i + 1);
2849 continue;
2850 }
2851
2852 db_err_msg *msg = NULL;
2853
2854 /* now check other clients */
2855 int j;
2856 for (j = 0; j < pheader->max_client_index; j++) {
2857 DATABASE_CLIENT *pdbclient = &pheader->client[j];
2858 int client_pid = pdbclient->pid;
2859 if (client_pid == 0) // empty slot
2860 continue;
2861
2862 if (!ss_pid_exists(client_pid)) {
2863 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);
2864
2865 db_delete_client_wlocked(pheader, j, &msg);
2866 db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2867
2868 continue;
2869 }
2870
2871 /* now make again the check with the buffer locked */
2873 if ((pdbclient->watchdog_timeout &&
2874 actual_time > pdbclient->last_activity &&
2875 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout)
2876 ) {
2877 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)",
2878 pdbclient->name, pheader->name, client_pid,
2879 who,
2880 (actual_time - pdbclient->last_activity) / 1000.0,
2881 pdbclient->watchdog_timeout / 1000.0);
2882
2883 db_delete_client_wlocked(pheader, j, &msg);
2884 db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2885 }
2886 }
2887
2888 db_unlock_database(i + 1);
2889 if (msg)
2890 db_flush_msg(&msg);
2891 }
2892#endif
2893}
2894
2895#ifdef LOCAL_ROUTINES
2896
2897void db_cleanup2(const char* client_name, int ignore_timeout, DWORD actual_time, const char *who)
2898{
2899 /* check online databases */
2900 int i;
2901 for (i = 0; i < _database_entries; i++) {
2902 if (_database[i].attached) {
2903 /* update the last_activity entry to show that we are alive */
2904
2905 db_lock_database(i + 1);
2906
2907 db_err_msg *msg = NULL;
2908
2909 DATABASE* pdb = &_database[i];
2910
2911 db_allow_write_locked(pdb, "db_cleanup2");
2912
2914
2915 DATABASE_HEADER* pheader = pdb->database_header;
2917 pclient->last_activity = now;
2918
2919 /* now check other clients */
2920 int j;
2921 for (j = 0; j < pheader->max_client_index; j++) {
2922 DATABASE_CLIENT* pdbclient = &pheader->client[j];
2923 if (j == pdb->client_index) // do not check ourselves
2924 continue;
2925 if (!pdbclient->pid) // empty slot
2926 continue;
2927 if ((client_name == NULL || client_name[0] == 0
2928 || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
2929 int client_pid = pdbclient->pid;
2930
2931 if (!ss_pid_exists(client_pid)) {
2932 db_msg(&msg, MINFO, "db_cleanup2", "Client \'%s\' on database \'%s\' pid %d does not exist and db_cleanup2 called by %s removed it",
2933 pdbclient->name, pheader->name,
2934 client_pid,
2935 who);
2936
2937 db_delete_client_wlocked(pheader, j, &msg);
2938 db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2939
2940 /* go again though whole list */
2941 j = 0;
2942 continue;
2943 }
2944
2946 if (ignore_timeout)
2948 else
2949 interval = pdbclient->watchdog_timeout;
2950
2951 /* If client process has no activity, clear its buffer entry. */
2952
2953 if ((interval > 0 && now - pdbclient->last_activity > interval)) {
2954 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)",
2955 pdbclient->name, pheader->name,
2956 who,
2957 (now - pdbclient->last_activity) / 1000.0, interval / 1000.0);
2958
2959 db_delete_client_wlocked(pheader, j, &msg);
2960 db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2961
2962 /* go again though whole list */
2963 j = 0;
2964 continue;
2965 }
2966 }
2967 }
2968
2969 db_unlock_database(i + 1);
2970 if (msg)
2971 db_flush_msg(&msg);
2972 }
2973 }
2974}
2975
2977{
2978 /* set watchdog flag of all open databases */
2979 for (int i = _database_entries; i > 0; i--) {
2980
2982
2983 DATABASE* pdb = &_database[i - 1];
2984
2985 if (!pdb->attached) {
2987 continue;
2988 }
2989
2991
2992 db_allow_write_locked(pdb, "db_set_watchdog_params");
2993
2994 /* clear entry from client structure in buffer header */
2995 pclient->watchdog_timeout = timeout;
2996
2997 /* show activity */
2998 pclient->last_activity = ss_millitime();
2999
3001 }
3002}
3003
3004/********************************************************************/
3014INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD * timeout, DWORD * last)
3015{
3016 if (hDB > _database_entries || hDB <= 0) {
3017 cm_msg(MERROR, "db_get_watchdog_info", "invalid database handle");
3018 return DB_INVALID_HANDLE;
3019 }
3020
3021 if (!_database[hDB - 1].attached) {
3022 cm_msg(MERROR, "db_get_watchdog_info", "invalid database handle");
3023 return DB_INVALID_HANDLE;
3024 }
3025
3026 /* lock database */
3028
3029 DATABASE *pdb = &_database[hDB - 1];
3030 DATABASE_HEADER *pheader = pdb->database_header;
3031
3032 /* find client */
3033 for (int i = 0; i < pheader->max_client_index; i++) {
3034 DATABASE_CLIENT *pclient = &pheader->client[i];
3035 if (pclient->pid && equal_ustring(pclient->name, client_name)) {
3036 *timeout = pclient->watchdog_timeout;
3037 *last = ss_millitime() - pclient->last_activity;
3039 return DB_SUCCESS;
3040 }
3041 }
3042
3043 *timeout = *last = 0;
3044
3046
3047 return CM_NO_CLIENT;
3048}
3049
3050/********************************************************************/
3060{
3061 if (hDB > _database_entries || hDB <= 0) {
3062 cm_msg(MERROR, "db_check_client", "invalid database handle");
3063 return DB_INVALID_HANDLE;
3064 }
3065
3066 if (!_database[hDB - 1].attached) {
3067 cm_msg(MERROR, "db_check_client", "invalid database handle");
3068 return DB_INVALID_HANDLE;
3069 }
3070
3071 int status;
3072
3074
3075 db_err_msg* msg = NULL;
3076
3077 DATABASE *pdb = &_database[hDB - 1];
3078 DATABASE_HEADER *pheader = pdb->database_header;
3079
3080 const KEY* pkey = db_get_pkey(pheader, hKeyClient, &status, "db_check_client", &msg);
3081
3082 if (!pkey) {
3084 if (msg)
3085 db_flush_msg(&msg);
3086 return CM_NO_CLIENT;
3087 }
3088
3089 int client_pid = atoi(pkey->name);
3090
3091 const KEY* pkey_name = db_find_pkey_locked(pheader, pkey, "Name", &status, &msg);
3092
3093 if (!pkey_name) {
3095 if (msg)
3096 db_flush_msg(&msg);
3097 return CM_NO_CLIENT;
3098 }
3099
3100 char name[256];
3101 name[0] = 0;
3102 int size = sizeof(name);
3103 status = db_get_data_locked(pheader, pkey_name, 0, name, &size, TID_STRING, &msg);
3104
3105 //fprintf(stderr, "db_check_client: hkey %d, status %d, pid %d, name \'%s\'\n", hKeyClient, status, client_pid, name);
3106
3107 if (status != DB_SUCCESS) {
3109 if (msg)
3110 db_flush_msg(&msg);
3111 return CM_NO_CLIENT;
3112 }
3113
3114 bool dead = false;
3115 bool found = false;
3116
3117 /* loop through clients */
3118 for (int i = 0; i < pheader->max_client_index; i++) {
3119 DATABASE_CLIENT *pclient = &pheader->client[i];
3120 if (pclient->pid == client_pid) {
3121 found = true;
3122 break;
3123 }
3124 }
3125
3126 if (found) {
3127 /* check that the client is still running: PID still exists */
3128 if (!ss_pid_exists(client_pid)) {
3129 dead = true;
3130 }
3131 }
3132
3134
3135 if (!found || dead) {
3136 /* client not found : delete ODB stucture */
3137
3138 db_allow_write_locked(pdb, "db_check_client");
3139
3141
3142 if (status != DB_SUCCESS)
3143 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);
3144 else if (!found)
3145 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);
3146 else if (dead)
3147 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);
3148
3150 }
3151
3153 if (msg)
3154 db_flush_msg(&msg);
3155
3156 return status;
3157}
3158
3159#endif // LOCAL_ROUTINES
3160
3161/********************************************************************/
3168{
3169 if (rpc_is_remote())
3170 return DB_SUCCESS;
3171
3172#ifdef LOCAL_ROUTINES
3173 if (hDB > _database_entries || hDB <= 0) {
3174 cm_msg(MERROR, "db_protect_database", "invalid database handle %d", hDB);
3175 return DB_INVALID_HANDLE;
3176 }
3177
3178 _database[hDB - 1].protect = TRUE;
3179 ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].database_header, _database[hDB - 1].shm_size);
3181#endif /* LOCAL_ROUTINES */
3182 return DB_SUCCESS;
3183}
3184
3185/*---- helper routines ---------------------------------------------*/
3186
3187const char *extract_key(const char *key_list, char *key_name, int key_name_length)
3188{
3189 int i = 0;
3190
3191 if (*key_list == '/')
3192 key_list++;
3193
3194 while (*key_list && *key_list != '/' && ++i < key_name_length)
3195 *key_name++ = *key_list++;
3196 *key_name = 0;
3197
3198 return key_list;
3199}
3200
3201BOOL equal_ustring(const char *str1, const char *str2)
3202{
3203 if (str1 == NULL && str2 != NULL)
3204 return FALSE;
3205 if (str1 != NULL && str2 == NULL)
3206 return FALSE;
3207 if (str1 == NULL && str2 == NULL)
3208 return TRUE;
3209 if (strlen(str1) != strlen(str2))
3210 return FALSE;
3211
3212 while (*str1)
3213 if (toupper(*str1++) != toupper(*str2++))
3214 return FALSE;
3215
3216 if (*str2)
3217 return FALSE;
3218
3219 return TRUE;
3220}
3221
3222BOOL ends_with_ustring(const char *str, const char *suffix)
3223{
3224 int len_str = strlen(str);
3225 int len_suffix = strlen(suffix);
3226
3227 // suffix is longer than the string
3228 if (len_suffix > len_str)
3229 return FALSE;
3230
3232}
3233
3234//utility function to match string against wildcard pattern
3235BOOL strmatch(char* pattern, char* str){
3236 switch(*pattern){
3237 case 0:
3238 if(*str == 0){
3239 //end of pattern
3240 return true;
3241 }else
3242 return false;
3243 case '*':
3244 {
3245 int i=0;
3246 // check for end of the string
3247 if(pattern[1] == 0) return true;
3248 // loop on all possible matches
3249 while(str[i] != 0) if(strmatch(pattern + 1, str+(i++))) return true;
3250 return false;
3251 }
3252 case '?':
3253 if(*str == 0){
3254 //end of string
3255 return false;
3256 } else {
3257 return strmatch(pattern+1, str+1);
3258 }
3259 default:
3260 if(*str == 0){
3261 //end of string
3262 return false;
3263 } else if(toupper(*str) == toupper(*pattern)){
3264 //recursion
3265 return strmatch(pattern+1, str+1);
3266 } else {
3267 return false;
3268 }
3269 }
3270}
3271
3272//utility function to extract array indexes
3273void strarrayindex(char* odbpath, int* index1, int* index2){
3274 char* pc;
3275
3276 *index1 = *index2 = 0;
3277 if (odbpath[strlen(odbpath) - 1] == ']') {
3278 if (strchr(odbpath, '[')) {
3279 if (*(strchr(odbpath, '[') + 1) == '*')
3280 *index1 = -1;
3281 else if (strchr((strchr(odbpath, '[') + 1), '.') ||
3282 strchr((strchr(odbpath, '[') + 1), '-')) {
3283 *index1 = atoi(strchr(odbpath, '[') + 1);
3284 pc = strchr(odbpath, '[') + 1;
3285 while (*pc != '.' && *pc != '-')
3286 pc++;
3287 while (*pc == '.' || *pc == '-')
3288 pc++;
3289 *index2 = atoi(pc);
3290 } else
3291 *index1 = atoi(strchr(odbpath, '[') + 1);
3292 }
3293
3294 //remove everything after bracket
3295 *strchr(odbpath, '[') = 0;
3296 }
3297}
3298
3299/********************************************************************/
3308INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
3309{
3310
3311 if (rpc_is_remote())
3312 return rpc_call(RPC_DB_CREATE_KEY, hDB, hKey, key_name, type);
3313
3314#ifdef LOCAL_ROUTINES
3315 {
3316 int status;
3317
3318 if (hDB > _database_entries || hDB <= 0) {
3319 cm_msg(MERROR, "db_create_key", "invalid database handle");
3320 return DB_INVALID_HANDLE;
3321 }
3322
3323 if (!_database[hDB - 1].attached) {
3324 cm_msg(MERROR, "db_create_key", "invalid database handle");
3325 return DB_INVALID_HANDLE;
3326 }
3327
3328 /* lock database */
3330
3332
3333 db_err_msg *msg = NULL;
3334
3335 KEY* pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_create_key", &msg);
3336
3337 if (!pkey) {
3339 if (msg)
3340 db_flush_msg(&msg);
3341 return DB_INVALID_HANDLE;
3342 }
3343
3344 db_allow_write_locked(&_database[hDB-1], "db_create_key");
3345
3346 KEY* newpkey = NULL;
3347
3348 status = db_create_key_wlocked(pheader, pkey, key_name, type, &newpkey, &msg);
3349
3351
3352 if (msg)
3353 db_flush_msg(&msg);
3354
3355 return status;
3356 }
3357#endif /* LOCAL_ROUTINES */
3358
3359 return DB_SUCCESS;
3360}
3361
3362#ifdef LOCAL_ROUTINES
3363
3364int db_create_key_wlocked(DATABASE_HEADER* pheader, KEY* parentKey, const char *key_name, DWORD type, KEY** pnewkey, db_err_msg** msg)
3365{
3366 int status;
3367
3368 if (pnewkey)
3369 *pnewkey = NULL;
3370
3371 if (parentKey== NULL) {
3372 parentKey = (KEY*) db_get_pkey(pheader, pheader->root_key, &status, "db_create_key_wlocked", msg);
3373 if (!parentKey) {
3374 return status;
3375 }
3376 }
3377
3378 status = db_validate_name(key_name, TRUE, "db_create_key_wlocked", msg);
3379 if (status != DB_SUCCESS) {
3380 return status;
3381 }
3382
3383 /* check type */
3385 db_msg(msg, MERROR, "db_create_key", "invalid key type %d to create \'%s\' in \'%s\'", type, key_name, db_get_path_locked(pheader, parentKey).c_str());
3386 return DB_INVALID_PARAM;
3387 }
3388
3389 const KEY* pdir = parentKey;
3390
3391 if (pdir->type != TID_KEY) {
3392 db_msg(msg, MERROR, "db_create_key", "cannot create \'%s\' in \'%s\' tid is %d, not a directory", key_name, db_get_path_locked(pheader, pdir).c_str(), pdir->type);
3393 return DB_NO_KEY;
3394 }
3395
3396 KEY* pcreated = NULL;
3397
3398 const char* pkey_name = key_name;
3399 do {
3400 // key name is limited to NAME_LENGTH, but buffer has to be slightly longer
3401 // to prevent truncation before db_validate_name checks for correct length. K.O.
3402 char name[NAME_LENGTH+100];
3403
3404 /* extract single key from key_name */
3406
3407 status = db_validate_name(name, FALSE, "db_create_key", msg);
3408 if (status != DB_SUCCESS) {
3409 return status;
3410 }
3411
3412 /* do not allow empty names, like '/dir/dir//dir/' */
3413 if (name[0] == 0) {
3414 return DB_INVALID_PARAM;
3415 }
3416
3417 /* check if parent or current directory */
3418 if (strcmp(name, "..") == 0) {
3419 pdir = db_get_parent(pheader, pdir, &status, "db_create_key", msg);
3420 if (!pdir) {
3421 return status;
3422 }
3423 continue;
3424 }
3425 if (strcmp(name, ".") == 0)
3426 continue;
3427
3428 KEY* pprev = NULL;
3429 const KEY* pitem = db_enum_first_locked(pheader, pdir, msg);
3430
3431 while (pitem) {
3432 if (equal_ustring(name, pitem->name)) {
3433 break;
3434 }
3435 pprev = (KEY*)pitem;
3436 pitem = db_enum_next_locked(pheader, pdir, pitem, msg);
3437 }
3438
3439 if (!pitem) {
3440 /* not found: create new key */
3441
3442 /* check parent for write access */
3443 const KEY* pkeyparent = db_get_parent(pheader, pdir, &status, "db_create_key", msg);
3444 if (!pkeyparent) {
3445 return status;
3446 }
3447
3448 if (!(pkeyparent->access_mode & MODE_WRITE) || (pkeyparent->access_mode & MODE_EXCLUSIVE)) {
3449 return DB_NO_ACCESS;
3450 }
3451
3452 KEYLIST* pkeylist = (KEYLIST*)db_get_pkeylist(pheader, db_pkey_to_hkey(pheader, pdir), pdir, "db_create_key_wlocked", msg);
3453
3454 if (!pkeylist) {
3455 return DB_CORRUPTED;
3456 }
3457
3458 pkeylist->num_keys++;
3459
3460 if (*pkey_name == '/' || type == TID_KEY) {
3461 /* create new key with keylist */
3462 KEY* pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_create_key_subdir");
3463
3464 if (pkey == NULL) {
3465 if (pkeylist->num_keys > 0)
3466 pkeylist->num_keys--;
3467
3468 db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s\'", key_name, db_get_path_locked(pheader, parentKey).c_str());
3469 return DB_FULL;
3470 }
3471
3472 /* append key to key list */
3473 if (pprev)
3474 pprev->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
3475 else
3476 pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
3477
3478 /* set key properties */
3479 pkey->type = TID_KEY;
3480 pkey->num_values = 1;
3481 pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
3482 mstrlcpy(pkey->name, name, sizeof(pkey->name));
3483 pkey->parent_keylist = (POINTER_T) pkeylist - (POINTER_T) pheader;
3484
3485 /* find space for new keylist */
3486 pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST), "db_create_key_B");
3487
3488 if (pkeylist == NULL) {
3489 db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s'", key_name, db_get_path_locked(pheader, parentKey).c_str());
3490 return DB_FULL;
3491 }
3492
3493 /* store keylist in data field */
3494 pkey->data = (POINTER_T) pkeylist - (POINTER_T) pheader;
3495 pkey->item_size = sizeof(KEYLIST);
3496 pkey->total_size = sizeof(KEYLIST);
3497
3498 pkeylist->parent = (POINTER_T) pkey - (POINTER_T) pheader;
3499 pkeylist->num_keys = 0;
3500 pkeylist->first_key = 0;
3501
3502 pcreated = pkey;
3503 pdir = pkey; // descend into newly created subdirectory
3504 } else {
3505 /* create new key with data */
3506 KEY* pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_create_key_data");
3507
3508 if (pkey == NULL) {
3509 if (pkeylist->num_keys > 0)
3510 pkeylist->num_keys--;
3511
3512 db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s\'", key_name, db_get_path_locked(pheader, parentKey).c_str());
3513 return DB_FULL;
3514 }
3515
3516 /* append key to key list */
3517 if (pprev)
3518 pprev->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
3519 else
3520 pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
3521
3522 pkey->type = type;
3523 pkey->num_values = 1;
3524 pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
3525 mstrlcpy(pkey->name, name, sizeof(pkey->name));
3526 pkey->parent_keylist = (POINTER_T) pkeylist - (POINTER_T) pheader;
3527
3528 /* allocate data */
3529 pkey->item_size = rpc_tid_size(type);
3530 if (pkey->item_size > 0) {
3531 void* data = malloc_data(pheader, pkey->item_size);
3532 if (data == NULL) {
3533 pkey->total_size = 0;
3534 db_msg(msg, MERROR, "db_create_key", "online database full while creating \'%s\' in \'%s\'", key_name, db_get_path_locked(pheader, parentKey).c_str());
3535 return DB_FULL;
3536 }
3537
3538 pkey->total_size = pkey->item_size;
3539 pkey->data = (POINTER_T)data - (POINTER_T)pheader;
3540 } else {
3541 /* first data is empty */
3542 pkey->item_size = 0;
3543 pkey->total_size = 0;
3544 pkey->data = 0;
3545 }
3546 pcreated = pkey;
3547 }
3548 } else {
3549 /* key found: descend */
3550
3551 /* resolve links */
3552 if (pitem->type == TID_LINK && pkey_name[0]) {
3553 pdir = db_resolve_link_locked(pheader, pitem, &status, msg);
3554 if (!pdir) {
3555 return status;
3556 }
3557 continue;
3558 }
3559
3560 if (!(*pkey_name == '/')) {
3561 if (pitem->type != type) {
3562 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_locked(pheader, pitem).c_str(), key_name, type, db_get_path_locked(pheader, parentKey).c_str());
3563 return DB_TYPE_MISMATCH;
3564 }
3565 //db_print_pkey(pheader, pitem);
3566
3567 if (pnewkey)
3568 *pnewkey = (KEY*)pitem;
3569
3570 return DB_KEY_EXIST;
3571 }
3572
3573 if (pitem->type != TID_KEY) {
3574 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_locked(pheader, pitem).c_str(), key_name, db_get_path_locked(pheader, parentKey).c_str());
3575 return DB_TYPE_MISMATCH;
3576 }
3577
3578 pdir = pitem;
3579 }
3580 } while (*pkey_name == '/');
3581
3582 assert(pcreated != NULL);
3583
3584 if (pnewkey)
3585 *pnewkey = pcreated;
3586
3587 return DB_SUCCESS;
3588}
3589
3590#endif /* LOCAL_ROUTINES */
3591
3592/********************************************************************/
3602{
3603 HNDLE hkey;
3604 int status;
3605
3606 if (rpc_is_remote())
3608
3609 if (destination == NULL) {
3610 cm_msg(MERROR, "db_create_link", "link destination name is NULL");
3611 return DB_INVALID_NAME;
3612 }
3613
3614 if (destination[0] != '/') {
3615 cm_msg(MERROR, "db_create_link", "link destination name \'%s\' should start with \'/\', relative links are forbidden", destination);
3616 return DB_INVALID_NAME;
3617 }
3618
3619 if (strlen(destination) < 1) {
3620 cm_msg(MERROR, "db_create_link", "link destination name \'%s\' is too short", destination);
3621 return DB_INVALID_NAME;
3622 }
3623
3624 if ((destination[0] == '/') && (destination[1] == 0)) {
3625 cm_msg(MERROR, "db_create_link", "links to \"/\" are forbidden");
3626 return DB_INVALID_NAME;
3627 }
3628
3629 /* check if destination exists */
3631 if (status != DB_SUCCESS) {
3632 cm_msg(MERROR, "db_create_link", "Link destination \"%s\" does not exist", destination);
3633 return DB_NO_KEY;
3634 }
3635
3636 //printf("db_create_link: [%s] hkey %d\n", destination, hkey);
3637
3638 /* check if link already exists */
3640 if (status != DB_SUCCESS) {
3641 // create new link
3643 } else {
3644 // check if key is TID_LINK
3645 KEY key;
3646 db_get_key(hDB, hkey, &key);
3647 if (key.type != TID_LINK) {
3648 cm_msg(MERROR, "db_create_link", "Existing key \"%s\" is not a link", link_name);
3649 return DB_KEY_EXIST;
3650 }
3651
3652 // modify existing link
3654 }
3655
3656 return status;
3657}
3658
3659/********************************************************************/
3670{
3671#ifdef LOCAL_ROUTINES
3672 {
3673 DATABASE_HEADER *pheader;
3678 INT status;
3679 BOOL locked = FALSE;
3680
3681 if (hDB > _database_entries || hDB <= 0) {
3682 cm_msg(MERROR, "db_delete_key1", "invalid database handle");
3683 return DB_INVALID_HANDLE;
3684 }
3685
3686 if (!_database[hDB - 1].attached) {
3687 cm_msg(MERROR, "db_delete_key1", "invalid database handle");
3688 return DB_INVALID_HANDLE;
3689 }
3690
3691 if (hKey < (int) sizeof(DATABASE_HEADER)) {
3692 cm_msg(MERROR, "db_delete_key1", "invalid key handle");
3693 return DB_INVALID_HANDLE;
3694 }
3695
3696 /* lock database at the top level */
3697 if (level == 0) {
3699 locked = TRUE;
3700 }
3701
3702 pheader = _database[hDB - 1].database_header;
3703
3704 /* check if hKey argument is correct */
3705 if (!db_validate_hkey(pheader, hKey)) {
3706 if (locked)
3708 return DB_INVALID_HANDLE;
3709 }
3710
3711 pkey = (KEY *) ((char *) pheader + hKey);
3712
3713 /* check if someone has opened key or parent */
3714 if (level == 0)
3715 do {
3716#ifdef CHECK_OPEN_RECORD
3717 if (pkey->notify_count) {
3718 if (locked)
3720 return DB_OPEN_RECORD;
3721 }
3722#endif
3723 if (pkey->parent_keylist == 0)
3724 break;
3725
3726 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
3727 // FIXME: validate pkeylist->parent
3728 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
3729 } while (TRUE);
3730
3731 pkey = (KEY *) ((char *) pheader + hKey);
3732 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
3733
3735
3736 /* first recures subtree for keys */
3737 if (pkey->type == TID_KEY && pkeylist->first_key) {
3738 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
3739
3740 do {
3741 pnext_key = (KEY *) (POINTER_T) pkey->next_key; // FIXME: what is this casting of hKey to pointer?
3742
3744
3745 if (status == DB_NO_ACCESS)
3746 deny_delete = TRUE;
3747
3748 if (pnext_key) {
3749 // FIXME: validate pnext_key
3750 pkey = (KEY *) ((char *) pheader + (POINTER_T) pnext_key);
3751 }
3752 } while (pnext_key);
3753 }
3754
3755 /* follow links if requested */
3756 if (pkey->type == TID_LINK && follow_links) {
3757 status = db_find_key1(hDB, 0, (char *) pheader + pkey->data, &hKeyLink);
3758 if (status == DB_SUCCESS && follow_links < 100)
3760
3761 if (follow_links == 100)
3762 cm_msg(MERROR, "db_delete_key1", "try to delete cyclic link");
3763 }
3764
3765 /* check if hKey argument is correct */
3766 if (!db_validate_hkey(pheader, hKey)) {
3767 if (locked)
3769 return DB_INVALID_HANDLE;
3770 }
3771
3772 pkey = (KEY *) ((char *) pheader + hKey);
3773
3774 /* return if key was already deleted by cyclic link */
3775 if (pkey->parent_keylist == 0) {
3776 if (locked)
3778 return DB_SUCCESS;
3779 }
3780
3781 /* now delete key */
3782 if (hKey != pheader->root_key) {
3783 if (!(pkey->access_mode & MODE_DELETE) || deny_delete) {
3784 if (locked)
3786 return DB_NO_ACCESS;
3787 }
3788#ifdef CHECK_OPEN_RECORD
3789 if (pkey->notify_count) {
3790 if (locked)
3792 return DB_OPEN_RECORD;
3793 }
3794#endif
3795 db_allow_write_locked(&_database[hDB - 1], "db_delete_key1");
3796
3797 /* delete key data */
3798 if (pkey->type == TID_KEY)
3799 free_key(pheader, (char *) pheader + pkey->data, pkey->total_size);
3800 else
3801 free_data(pheader, (char *) pheader + pkey->data, pkey->total_size, "db_delete_key1");
3802
3803 /* unlink key from list */
3804 pnext_key = (KEY *) (POINTER_T) pkey->next_key;
3805 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
3806
3807 if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
3808 /* key is first in list */
3809 pkeylist->first_key = (POINTER_T) pnext_key;
3810 } else {
3811 /* find predecessor */
3812 pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
3813 while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey)
3814 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
3815 pkey_tmp->next_key = (POINTER_T) pnext_key;
3816 }
3817
3818 /* delete key */
3819 free_key(pheader, pkey, sizeof(KEY));
3820 pkeylist->num_keys--;
3821 }
3822
3823 if (locked)
3825 }
3826#endif /* LOCAL_ROUTINES */
3827
3828 return DB_SUCCESS;
3829}
3830
3831/********************************************************************/
3863
3864#ifdef LOCAL_ROUTINES
3865static const KEY* db_find_pkey_locked(const DATABASE_HEADER *pheader, const KEY* pkey, const char *key_name, int *pstatus, db_err_msg **msg)
3866{
3867 if (pkey == NULL) {
3868 pkey = db_get_pkey(pheader, pheader->root_key, pstatus, "db_find_key", msg);
3869 if (!pkey) {
3870 return NULL;
3871 }
3872 }
3873
3874 HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
3875
3876 if (pkey->type != TID_KEY) {
3877 DWORD tid = pkey->type;
3878 std::string path = db_get_path_locked(pheader, hKey);
3879 db_msg(msg, MERROR, "db_find_key", "hkey %d path \"%s\" tid %d is not a directory, looking for \"%s\"", hKey, path.c_str(), tid, key_name);
3880 if (pstatus)
3881 *pstatus = DB_NO_KEY;
3882 return NULL;
3883 }
3884
3885 if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
3886 if (!(pkey->access_mode & MODE_READ)) {
3887 if (pstatus)
3889 return NULL;
3890 }
3891 return pkey;
3892 }
3893
3894 const KEYLIST *pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
3895 if (!pkeylist) {
3896 if (pstatus)
3898 return NULL;
3899 }
3900
3902
3903 const char *pkey_name = key_name;
3904 do {
3905 assert(pkeylist!=NULL); // should never happen!
3906
3907 char str[MAX_ODB_PATH];
3908
3909 /* extract single subkey from key_name */
3910 pkey_name = extract_key(pkey_name, str, sizeof(str));
3911
3912 /* strip trailing '[n]' */
3913 if (strchr(str, '[') && str[strlen(str) - 1] == ']')
3914 *strchr(str, '[') = 0;
3915
3916 /* check if parent or current directory */
3917 if (strcmp(str, "..") == 0) {
3918 if (pkey->parent_keylist) {
3919 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
3920 // FIXME: validate pkeylist->parent
3921 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
3922 }
3923 continue;
3924 }
3925 if (strcmp(str, ".") == 0)
3926 continue;
3927
3929
3930 hKey = pkeylist->first_key;
3931
3932 if (hKey == 0) {
3933 // empty subdirectory
3934 if (pstatus)
3935 *pstatus = DB_NO_KEY;
3936 return NULL;
3937 }
3938
3939 int i;
3940 for (i = 0; i < pkeylist->num_keys; i++) {
3941 pkey = db_get_pkey(pheader, hKey, pstatus, "db_find_key", msg);
3942
3943 if (!pkey) {
3944 std::string path = db_get_path_locked(pheader, last_good_hkey);
3945 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);
3946 return NULL;
3947 }
3948
3949 if (!db_validate_key_offset(pheader, pkey->next_key)) {
3950 std::string path = db_get_path_locked(pheader, hKey);
3951 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);
3952 if (pstatus)
3954 return NULL;
3955 }
3956
3957 if (equal_ustring(str, pkey->name))
3958 break;
3959
3960 if (pkey->next_key == 0) {
3961 if (pstatus)
3962 *pstatus = DB_NO_KEY;
3963 return NULL;
3964 }
3965
3966 hKey = pkey->next_key;
3967 }
3968
3969 if (i == pkeylist->num_keys) {
3970 if (pstatus)
3971 *pstatus = DB_NO_KEY;
3972 return NULL;
3973 }
3974
3975 /* resolve links */
3976 if (pkey->type == TID_LINK) {
3977 /* copy destination, strip '/' */
3978 mstrlcpy(str, (char *) pheader + pkey->data, sizeof(str));
3979 if (str[strlen(str) - 1] == '/')
3980 str[strlen(str) - 1] = 0;
3981
3982 /* if link is pointer to array index, return link instead of destination */
3983 if (str[strlen(str) - 1] == ']')
3984 break;
3985
3986 /* append rest of key name if existing */
3987 if (pkey_name[0]) {
3988 mstrlcat(str, pkey_name, sizeof(str));
3989 return db_find_pkey_locked(pheader, NULL, str, pstatus, msg);
3990 } else {
3991 /* if last key in chain is a link, return its destination */
3992 int status = 0;
3993 const KEY *plink = db_find_pkey_locked(pheader, NULL, str, &status, msg);
3994 if (pstatus) {
3995 if (status == DB_NO_KEY)
3997 else
3998 *pstatus = status;
3999 }
4000 return plink;
4001 }
4002 }
4003
4004 /* key found: check if last in chain */
4005 if (*pkey_name == '/') {
4006 if (pkey->type != TID_KEY) {
4007 if (pstatus)
4008 *pstatus = DB_NO_KEY;
4009 return NULL;
4010 }
4011 }
4012
4013 if (pkey->type == TID_KEY) {
4014 /* descend one level */
4015 pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
4016
4017 if (!pkeylist) {
4018 if (pstatus)
4020 return NULL;
4021 }
4022 } else {
4023 pkeylist = NULL;
4024 }
4025
4026 } while (*pkey_name == '/' && *(pkey_name + 1));
4027
4028 return pkey;
4029}
4030
4031static int db_find_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, const char *key_name, HNDLE *subhKey, db_err_msg **msg)
4032{
4033 int status;
4034 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_find_key", msg);
4035 if (!pkey) {
4036 if (subhKey)
4037 *subhKey = 0;
4038 return status;
4039 }
4040
4041 const KEY* plink = db_find_pkey_locked(pheader, pkey, key_name, &status, msg);
4042
4043 if (!plink) {
4044 *subhKey = 0;
4045 return status;
4046 }
4047
4048 *subhKey = db_pkey_to_hkey(pheader, plink);
4049
4050 return DB_SUCCESS;
4051}
4052#endif /* LOCAL_ROUTINES */
4053
4054/********************************************************************/
4079INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4080{
4081 if (rpc_is_remote())
4082 return rpc_call(RPC_DB_FIND_KEY, hDB, hKey, key_name, subhKey);
4083
4084#ifdef LOCAL_ROUTINES
4085 {
4086 DATABASE_HEADER *pheader;
4087 INT status;
4088
4089 *subhKey = 0;
4090
4091 if (hDB > _database_entries || hDB <= 0) {
4092 cm_msg(MERROR, "db_find_key", "invalid database handle");
4093 return DB_INVALID_HANDLE;
4094 }
4095
4096 if (!_database[hDB - 1].attached) {
4097 cm_msg(MERROR, "db_find_key", "invalid database handle");
4098 return DB_INVALID_HANDLE;
4099 }
4100
4101 db_err_msg *msg = NULL;
4102
4104
4105 pheader = _database[hDB - 1].database_header;
4106
4107 status = db_find_key_locked(pheader, hKey, key_name, subhKey, &msg);
4108
4110
4111 if (msg)
4112 db_flush_msg(&msg);
4113
4114 return status;
4115 }
4116#endif /* LOCAL_ROUTINES */
4117
4118 return DB_SUCCESS;
4119}
4120
4122#ifndef DOXYGEN_SHOULD_SKIP_THIS
4123
4124/*------------------------------------------------------------------*/
4125INT db_find_key1(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4126/********************************************************************\
4127
4128 Routine: db_find_key1
4129
4130 Purpose: Same as db_find_key, but without DB locking
4131
4132 Input:
4133 HNDLE bufer_handle Handle to the database
4134 HNDLE hKey Key handle to start the search
4135 char *key_name Name of key in the form "/key/key/key"
4136
4137 Output:
4138 INT *handle Key handle
4139
4140 Function value:
4141 DB_SUCCESS Successful completion
4142 DB_INVALID_HANDLE Database handle is invalid
4143 DB_NO_KEY Key doesn't exist
4144 DB_NO_ACCESS No access to read key
4145
4146\********************************************************************/
4147{
4148 if (rpc_is_remote())
4149 return rpc_call(RPC_DB_FIND_KEY, hDB, hKey, key_name, subhKey);
4150
4151#ifdef LOCAL_ROUTINES
4152 {
4153 DATABASE_HEADER *pheader;
4155 KEY *pkey;
4156 const char *pkey_name;
4157 INT i;
4158
4159 *subhKey = 0;
4160
4161 if (hDB > _database_entries || hDB <= 0) {
4162 cm_msg(MERROR, "db_find_key", "invalid database handle");
4163 return DB_INVALID_HANDLE;
4164 }
4165
4166 if (!_database[hDB - 1].attached) {
4167 cm_msg(MERROR, "db_find_key", "invalid database handle");
4168 return DB_INVALID_HANDLE;
4169 }
4170
4171 pheader = _database[hDB - 1].database_header;
4172 if (!hKey)
4173 hKey = pheader->root_key;
4174
4175 /* check if hKey argument is correct */
4176 if (!db_validate_hkey(pheader, hKey)) {
4177 return DB_INVALID_HANDLE;
4178 }
4179
4180 pkey = (KEY *) ((char *) pheader + hKey);
4181
4182 if (pkey->type != TID_KEY) {
4183 cm_msg(MERROR, "db_find_key", "key has no subkeys");
4184 *subhKey = 0;
4185 return DB_NO_KEY;
4186 }
4187 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4188
4189 if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
4190 if (!(pkey->access_mode & MODE_READ)) {
4191 *subhKey = 0;
4192 return DB_NO_ACCESS;
4193 }
4194
4195 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4196
4197 return DB_SUCCESS;
4198 }
4199
4200 pkey_name = key_name;
4201 do {
4202 char str[MAX_ODB_PATH];
4203
4204 /* extract single subkey from key_name */
4205 pkey_name = extract_key(pkey_name, str, sizeof(str));
4206
4207 /* check if parent or current directory */
4208 if (strcmp(str, "..") == 0) {
4209 if (pkey->parent_keylist) {
4210 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4211 // FIXME: validate pkeylist->parent
4212 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
4213 }
4214 continue;
4215 }
4216 if (strcmp(str, ".") == 0)
4217 continue;
4218
4219 /* check if key is in keylist */
4220 // FIXME: validate pkeylist->first_key
4221 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
4222
4223 for (i = 0; i < pkeylist->num_keys; i++) {
4224 if (equal_ustring(str, pkey->name))
4225 break;
4226
4227 // FIXME: validate pkey->next_key
4228 pkey = (KEY *) ((char *) pheader + pkey->next_key);
4229 }
4230
4231 if (i == pkeylist->num_keys) {
4232 *subhKey = 0;
4233 return DB_NO_KEY;
4234 }
4235
4236 /* resolve links */
4237 if (pkey->type == TID_LINK) {
4238 /* copy destination, strip '/' */
4239 strcpy(str, (char *) pheader + pkey->data);
4240 if (str[strlen(str) - 1] == '/')
4241 str[strlen(str) - 1] = 0;
4242
4243 /* append rest of key name if existing */
4244 if (pkey_name[0]) {
4246 return db_find_key1(hDB, 0, str, subhKey);
4247 } else {
4248 /* if last key in chain is a link, return its destination */
4249 return db_find_link1(hDB, 0, str, subhKey);
4250 }
4251 }
4252
4253 /* key found: check if last in chain */
4254 if (*pkey_name == '/') {
4255 if (pkey->type != TID_KEY) {
4256 *subhKey = 0;
4257 return DB_NO_KEY;
4258 }
4259 }
4260
4261 /* descend one level */
4262 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4263
4264 } while (*pkey_name == '/' && *(pkey_name + 1));
4265
4266 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4267 }
4268#endif /* LOCAL_ROUTINES */
4269
4270 return DB_SUCCESS;
4271}
4272
4273/*------------------------------------------------------------------*/
4274INT db_find_link(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4275/********************************************************************\
4276
4277 Routine: db_find_link
4278
4279 Purpose: Find a key or link by name and return its handle
4280 (internal address). The only difference of this routine
4281 compared with db_find_key is that if the LAST key in
4282 the chain is a link, it is NOT evaluated. Links not being
4283 the last in the chain are evaluated.
4284
4285 Input:
4286 HNDLE bufer_handle Handle to the database
4287 HNDLE hKey Key handle to start the search
4288 char *key_name Name of key in the form "/key/key/key"
4289
4290 Output:
4291 INT *handle Key handle
4292
4293 Function value:
4294 DB_SUCCESS Successful completion
4295 DB_INVALID_HANDLE Database handle is invalid
4296 DB_NO_KEY Key doesn't exist
4297 DB_NO_ACCESS No access to read key
4298
4299\********************************************************************/
4300{
4301 if (rpc_is_remote())
4302 return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
4303
4304#ifdef LOCAL_ROUTINES
4305 {
4306 DATABASE_HEADER *pheader;
4308 KEY *pkey;
4309 const char *pkey_name;
4310 INT i;
4311
4312 *subhKey = 0;
4313
4314 if (hDB > _database_entries || hDB <= 0) {
4315 cm_msg(MERROR, "db_find_link", "Invalid database handle");
4316 return DB_INVALID_HANDLE;
4317 }
4318
4319 if (!_database[hDB - 1].attached) {
4320 cm_msg(MERROR, "db_find_link", "invalid database handle");
4321 return DB_INVALID_HANDLE;
4322 }
4323
4325
4326 pheader = _database[hDB - 1].database_header;
4327 if (!hKey)
4328 hKey = pheader->root_key;
4329
4330 /* check if hKey argument is correct */
4331 if (!db_validate_hkey(pheader, hKey)) {
4333 return DB_INVALID_HANDLE;
4334 }
4335
4336 pkey = (KEY *) ((char *) pheader + hKey);
4337
4338 if (pkey->type != TID_KEY) {
4340 cm_msg(MERROR, "db_find_link", "key has no subkeys");
4341 return DB_NO_KEY;
4342 }
4343 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4344
4345 if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
4346 if (!(pkey->access_mode & MODE_READ)) {
4347 *subhKey = 0;
4349 return DB_NO_ACCESS;
4350 }
4351
4352 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4353
4355 return DB_SUCCESS;
4356 }
4357
4358 pkey_name = key_name;
4359 do {
4360 char str[MAX_ODB_PATH];
4361
4362 /* extract single subkey from key_name */
4363 pkey_name = extract_key(pkey_name, str, sizeof(str));
4364
4365 /* check if parent or current directory */
4366 if (strcmp(str, "..") == 0) {
4367 if (pkey->parent_keylist) {
4368 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4369 // FIXME: validate pkeylist->parent
4370 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
4371 }
4372 continue;
4373 }
4374 if (strcmp(str, ".") == 0)
4375 continue;
4376
4377 /* check if key is in keylist */
4378 // FIXME: validate pkeylist->first_key
4379 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
4380
4381 for (i = 0; i < pkeylist->num_keys; i++) {
4382 if (!db_validate_key_offset(pheader, pkey->next_key)) {
4383 int pkey_next_key = pkey->next_key;
4385 cm_msg(MERROR, "db_find_link", "Warning: database corruption, key \"%s\", next_key 0x%08X is invalid", key_name, pkey_next_key - (int)sizeof(DATABASE_HEADER));
4386 *subhKey = 0;
4387 return DB_CORRUPTED;
4388 }
4389
4390 if (equal_ustring(str, pkey->name))
4391 break;
4392
4393 pkey = (KEY *) ((char *) pheader + pkey->next_key); // FIXME: pkey->next_key could be zero
4394 }
4395
4396 if (i == pkeylist->num_keys) {
4397 *subhKey = 0;
4399 return DB_NO_KEY;
4400 }
4401
4402 /* resolve links if not last in chain */
4403 if (pkey->type == TID_LINK && *pkey_name == '/') {
4404 /* copy destination, strip '/' */
4405 strcpy(str, (char *) pheader + pkey->data);
4406 if (str[strlen(str) - 1] == '/')
4407 str[strlen(str) - 1] = 0;
4408
4409 /* append rest of key name */
4412 return db_find_link(hDB, 0, str, subhKey);
4413 }
4414
4415 /* key found: check if last in chain */
4416 if ((*pkey_name == '/')) {
4417 if (pkey->type != TID_KEY) {
4418 *subhKey = 0;
4420 return DB_NO_KEY;
4421 }
4422 }
4423
4424 /* descend one level */
4425 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4426
4427 } while (*pkey_name == '/' && *(pkey_name + 1));
4428
4429 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4430
4432 }
4433#endif /* LOCAL_ROUTINES */
4434
4435 return DB_SUCCESS;
4436}
4437
4438/*------------------------------------------------------------------*/
4439INT db_find_link1(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4440/********************************************************************\
4441
4442 Routine: db_find_link1
4443
4444 Purpose: Same ad db_find_link, but without DB locking
4445
4446 Input:
4447 HNDLE bufer_handle Handle to the database
4448 HNDLE hKey Key handle to start the search
4449 char *key_name Name of key in the form "/key/key/key"
4450
4451 Output:
4452 INT *handle Key handle
4453
4454 Function value:
4455 DB_SUCCESS Successful completion
4456 DB_INVALID_HANDLE Database handle is invalid
4457 DB_NO_KEY Key doesn't exist
4458 DB_NO_ACCESS No access to read key
4459
4460\********************************************************************/
4461{
4462 if (rpc_is_remote())
4463 return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
4464
4465#ifdef LOCAL_ROUTINES
4466 {
4467 DATABASE_HEADER *pheader;
4469 KEY *pkey;
4470 const char *pkey_name;
4471 INT i;
4472
4473 *subhKey = 0;
4474
4475 if (hDB > _database_entries || hDB <= 0) {
4476 cm_msg(MERROR, "db_find_link", "Invalid database handle");
4477 return DB_INVALID_HANDLE;
4478 }
4479
4480 if (!_database[hDB - 1].attached) {
4481 cm_msg(MERROR, "db_find_link", "invalid database handle");
4482 return DB_INVALID_HANDLE;
4483 }
4484
4485 pheader = _database[hDB - 1].database_header;
4486 if (!hKey)
4487 hKey = pheader->root_key;
4488
4489 /* check if hKey argument is correct */
4490 if (!db_validate_hkey(pheader, hKey)) {
4491 return DB_INVALID_HANDLE;
4492 }
4493
4494 pkey = (KEY *) ((char *) pheader + hKey);
4495
4496 if (pkey->type != TID_KEY) {
4497 cm_msg(MERROR, "db_find_link", "key has no subkeys");
4498 return DB_NO_KEY;
4499 }
4500 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4501
4502 if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
4503 if (!(pkey->access_mode & MODE_READ)) {
4504 *subhKey = 0;
4505 return DB_NO_ACCESS;
4506 }
4507
4508 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4509
4510 return DB_SUCCESS;
4511 }
4512
4513 pkey_name = key_name;
4514 do {
4515 char str[MAX_ODB_PATH];
4516 /* extract single subkey from key_name */
4517 pkey_name = extract_key(pkey_name, str, sizeof(str));
4518
4519 /* check if parent or current directory */
4520 if (strcmp(str, "..") == 0) {
4521 if (pkey->parent_keylist) {
4522 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4523 // FIXME: validate pkeylist->parent
4524 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
4525 }
4526 continue;
4527 }
4528 if (strcmp(str, ".") == 0)
4529 continue;
4530
4531 /* check if key is in keylist */
4532 // FIXME: validate pkeylist->first_key
4533 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
4534
4535 for (i = 0; i < pkeylist->num_keys; i++) {
4536 if (!db_validate_key_offset(pheader, pkey->next_key)) {
4537 cm_msg(MERROR, "db_find_link1", "Warning: database corruption, key \"%s\", next_key 0x%08X is invalid", key_name, pkey->next_key - (int)sizeof(DATABASE_HEADER));
4538 *subhKey = 0;
4539 return DB_CORRUPTED;
4540 }
4541
4542 if (equal_ustring(str, pkey->name))
4543 break;
4544
4545 pkey = (KEY *) ((char *) pheader + pkey->next_key); // FIXME: pkey->next_key could be zero
4546 }
4547
4548 if (i == pkeylist->num_keys) {
4549 *subhKey = 0;
4550 return DB_NO_KEY;
4551 }
4552
4553 /* resolve links if not last in chain */
4554 if (pkey->type == TID_LINK && *pkey_name == '/') {
4555 /* copy destination, strip '/' */
4556 strcpy(str, (char *) pheader + pkey->data);
4557 if (str[strlen(str) - 1] == '/')
4558 str[strlen(str) - 1] = 0;
4559
4560 /* append rest of key name */
4562 return db_find_link1(hDB, 0, str, subhKey);
4563 }
4564
4565 /* key found: check if last in chain */
4566 if ((*pkey_name == '/')) {
4567 if (pkey->type != TID_KEY) {
4568 *subhKey = 0;
4569 return DB_NO_KEY;
4570 }
4571 }
4572
4573 /* descend one level */
4574 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4575
4576 } while (*pkey_name == '/' && *(pkey_name + 1));
4577
4578 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4579 }
4580#endif /* LOCAL_ROUTINES */
4581
4582 return DB_SUCCESS;
4583}
4584
4585/*------------------------------------------------------------------*/
4587/********************************************************************\
4588
4589 Routine: db_find_keys
4590
4591 Purpose: finds all ODB keys matching an odb path
4592 '*' and '?' wildcard expansion available
4593
4594 Input:
4595 HNDLE hDB Handle to the database
4596 HNDLE hKeyRoot Key handle to start the search
4597 char *odbpath Pattern of keys in the form "/key/key/key", can include '*' and '?'
4598
4599 Output:
4600 std::vector<HNDLE> &hKeyVector Key handle
4601
4602 Function value:
4603 DB_SUCCESS Successful completion
4604 DB_INVALID_HANDLE Database handle is invalid
4605 DB_NO_KEY No access to any key
4606 DB_NO_ACCESS No access to a read key
4607
4608\********************************************************************/
4609{
4610 HNDLE hKey, hSubKey=0;
4611 KEY key;
4612 INT status;
4613 char *pattern = NULL;
4614 char *subkeypath = NULL;
4615 char *parentkeypath = NULL;
4616 char localpath[256];
4617
4618 //make work on a local copy od odbpath to allow recursion
4619 strcpy(localpath, odbpath);
4621
4622 char *wildcard = strpbrk(localpath, "*?");
4623 if(wildcard){
4624 //wildcard found
4625 subkeypath = strchr(wildcard, '/');
4626 if(subkeypath){
4627 //truncate the string at slash
4628 *subkeypath = 0;
4629 subkeypath++;
4630 }
4632 if(parentkeypath){
4633 //truncate there too
4634 *parentkeypath = 0;
4635 pattern = parentkeypath+1;
4636 if((parentkeypath-1) == localpath){
4637 //path starts with '/', no parent path
4639 } else {
4641 }
4642 } else {
4643 //wildcard at top level, start with pattern
4644 pattern = localpath;
4646 }
4647 }
4648
4649 //if available search for parent key path
4650 if(parentkeypath){
4652 if (status != DB_SUCCESS)
4653 return status;
4654 } else {
4655 hKey = hKeyRoot;
4656 }
4657
4658 //if a pattern is found
4659 if(pattern){
4660 //try match all subkeys
4661 status = DB_NO_KEY;
4662 for (int i=0 ; ; i++)
4663 {
4665 if (!hSubKey)
4666 break; // end of list reached
4668
4669 if(strmatch(pattern, key.name)){
4670 //match
4671 if(!subkeypath){
4672 //found
4673 hKeyVector.push_back(hSubKey);
4674
4675 } else if (key.type == TID_KEY){
4676 //recurse with hSubKey as root key and subkeypath as path
4678 if (subkeystatus != DB_NO_KEY)
4680
4681 if (status != DB_SUCCESS && status != DB_NO_KEY)
4682 break;
4683 }
4684 }
4685 }
4686
4687 return status;
4688 } else {
4689 //no pattern: hKey matches!
4690 db_get_key(hDB, hKey, &key);
4691 hKeyVector.push_back(hKey);
4692
4693 return DB_SUCCESS;
4694 }
4695
4696}
4697
4698/*------------------------------------------------------------------*/
4700/********************************************************************\
4701
4702 Routine: db_get_parent
4703
4704 Purpose: return an handle to the parent key
4705
4706 Input:
4707 HNDLE bufer_handle Handle to the database
4708 HNDLE hKey Key handle of the key
4709
4710 Output:
4711 INT *handle Parent key handle
4712
4713 Function value:
4714 DB_SUCCESS Successful completion
4715
4716\********************************************************************/
4717{
4718 if (rpc_is_remote())
4720
4721#ifdef LOCAL_ROUTINES
4722 {
4723
4724 DATABASE_HEADER *pheader;
4725 const KEY *pkey;
4726
4727 if (hDB > _database_entries || hDB <= 0) {
4728 cm_msg(MERROR, "db_get_parent", "invalid database handle");
4729 return DB_INVALID_HANDLE;
4730 }
4731
4732 if (!_database[hDB - 1].attached) {
4733 cm_msg(MERROR, "db_get_parent", "invalid database handle");
4734 return DB_INVALID_HANDLE;
4735 }
4736
4737 if (hKey < (int) sizeof(DATABASE_HEADER)) {
4738 cm_msg(MERROR, "db_get_parent", "invalid key handle");
4739 return DB_INVALID_HANDLE;
4740 }
4741
4743
4744 pheader = _database[hDB - 1].database_header;
4745 pkey = (const KEY *) ((char *) pheader + hKey);
4746
4747 /* find parent key */
4748 const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4749
4750 if (!db_validate_hkey(pheader, pkeylist->parent)) {
4752 return DB_INVALID_HANDLE;
4753 }
4754
4755 pkey = (const KEY *) ((char *) pheader + pkeylist->parent);
4756
4757 *parenthKey = (POINTER_T) pkey - (POINTER_T) pheader;
4758
4760 }
4761#endif
4762
4763 return DB_SUCCESS;
4764}
4765
4766/*------------------------------------------------------------------*/
4768/********************************************************************\
4769
4770 Routine: db_scan_tree
4771
4772 Purpose: Scan a subtree recursively and call 'callback' for each key
4773
4774 Input:
4775 HNDLE hDB Handle to the database
4776 HNDLE hKeyRoot Key to start scan from, 0 for root
4777 INT level Recursion level
4778 void *callback Callback routine called with params:
4779 hDB Copy of hDB
4780 hKey Copy of hKey
4781 key Key associated with hKey
4782 INT Recursion level
4783 info Copy of *info
4784 void *info Optional data copied to callback routine
4785
4786 Output:
4787 implicit via callback
4788
4789 Function value:
4790 DB_SUCCESS Successful completion
4791 <all error codes of db_get_key>
4792
4793\********************************************************************/
4794{
4795 HNDLE hSubkey;
4796 KEY key;
4797 INT i, status;
4798
4800 if (status != DB_SUCCESS)
4801 return status;
4802
4804 if (status == 0)
4805 return status;
4806
4807 if (key.type == TID_KEY) {
4808 for (i = 0;; i++) {
4810
4811 if (!hSubkey)
4812 break;
4813
4815 }
4816 }
4817
4818 return DB_SUCCESS;
4819}
4820
4821#ifdef LOCAL_ROUTINES
4822
4823int 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)
4824{
4825 assert(pkey != NULL);
4826 assert(level < MAX_ODB_PATH);
4827
4828 int status = callback(pheader, pkey, level, info, msg);
4829 if (status == 0)
4830 return status;
4831
4832 if (pkey->type == TID_KEY) {
4833 const KEY* subkey = db_enum_first_locked(pheader, pkey, msg);
4834 while (subkey != NULL) {
4835 db_scan_tree_locked(pheader, subkey, level + 1, callback, info, msg);
4836 subkey = db_enum_next_locked(pheader, pkey, subkey, msg);
4837 }
4838 }
4839
4840 return DB_SUCCESS;
4841}
4842
4843#endif // LOCAL_ROUTINES
4844
4845/*------------------------------------------------------------------*/
4847/********************************************************************\
4848
4849 Routine: db_scan_tree_link
4850
4851 Purpose: Scan a subtree recursively and call 'callback' for each key.
4852 Similar to db_scan_tree but without following links.
4853
4854 Input:
4855 HNDLE hDB Handle to the database
4856 HNDLE hKeyRoot Key to start scan from, 0 for root
4857 INT level Recursion level
4858 void *callback Callback routine called with params:
4859 hDB Copy of hDB
4860 hKey Copy of hKey
4861 key Key associated with hKey
4862 INT Recursion level
4863 info Copy of *info
4864 void *info Optional data copied to callback routine
4865
4866 Output:
4867 implicit via callback
4868
4869 Function value:
4870 DB_SUCCESS Successful completion
4871 <all error codes of db_get_key>
4872
4873\********************************************************************/
4874{
4875 HNDLE hSubkey;
4876 KEY key;
4877 INT i, status;
4878
4880 if (status != DB_SUCCESS)
4881 return status;
4882
4883 callback(hDB, hKey, &key, level, info);
4884
4885 if (key.type == TID_KEY) {
4886 for (i = 0;; i++) {
4888
4889 if (!hSubkey)
4890 break;
4891
4893 }
4894 }
4895
4896 return DB_SUCCESS;
4897}
4898
4899#ifdef LOCAL_ROUTINES
4900/*------------------------------------------------------------------*/
4901static std::string db_get_path_locked(const DATABASE_HEADER* pheader, const KEY* pkey)
4902{
4903 std::string path = "";
4904 while (1) {
4905 //printf("db_get_path_locked: hkey %d, pkey name \"%s\", type %d, parent %d, path \"%s\"\n", hKey, pkey->name, pkey->type, pkey->parent_keylist, path.c_str());
4906
4907 /* check key type */
4908 if (pkey->type <= 0 || pkey->type >= TID_LAST) {
4909 char buf[256];
4910 sprintf(buf, "(INVALID_KEY_TYPE_%d)", pkey->type);
4911 std::string xpath;
4912 xpath += buf;
4913 if (path.length() > 0) {
4914 xpath += "/";
4915 xpath += path;
4916 }
4917 return xpath;
4918 }
4919
4920 /* add key name in front of path */
4921 std::string str = path;
4922 path = "";
4923 if (pkey->name[0] == 0) {
4924 path += "(EMPTY_NAME)";
4925 } else {
4926 path += pkey->name;
4927 }
4928 if (str.length() > 0)
4929 path += "/";
4930 path += str;
4931
4932 if (!pkey->parent_keylist) {
4933 return path;
4934 }
4935
4936 if (!db_validate_data_offset(pheader, pkey->parent_keylist)) {
4937 return "(INVALID_PARENT_KEYLIST)/" + path;
4938 }
4939
4940 /* find parent key */
4941 const KEYLIST* pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4942
4943 if (pkeylist->parent == pheader->root_key) {
4944 return "/" + path;
4945 }
4946
4947 if (pkeylist->parent == 0) {
4948 return "(NULL_PARENT)/" + path;
4949 }
4950
4951 if (!db_validate_key_offset(pheader, pkeylist->parent)) {
4952 return "(INVALID_PARENT)/" + path;
4953 }
4954
4955 pkey = (const KEY *) ((char *) pheader + pkeylist->parent);
4956 };
4957
4958 /* NOT REACHED */
4959}
4960
4961/*------------------------------------------------------------------*/
4962static std::string db_get_path_locked(const DATABASE_HEADER* pheader, HNDLE hKey)
4963{
4964 //printf("db_get_path_locked: hkey %d\n", hKey);
4965
4966 if (!hKey)
4967 hKey = pheader->root_key;
4968
4969 if (hKey == pheader->root_key) {
4970 return "/";
4971 }
4972
4973 /* check if hKey argument is correct */
4974 if (hKey == 0) {
4975 return "(ZERO_HKEY)";
4976 }
4977
4978 /* check if hKey argument is correct */
4979 if (!db_validate_key_offset(pheader, hKey)) {
4980 return "(INVALID_HKEY)";
4981 }
4982
4983 const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
4984
4985 return db_get_path_locked(pheader, pkey);
4986}
4987#endif /* LOCAL_ROUTINES */
4988
4989/*------------------------------------------------------------------*/
4990INT db_get_path(HNDLE hDB, HNDLE hKey, char *path, INT buf_size)
4991/********************************************************************\
4992
4993 Routine: db_get_path
4994
4995 Purpose: Get full path of a key
4996
4997 Input:
4998 HNDLE hDB Handle to the database
4999 HNDLE hKey Key handle
5000 INT buf_size Maximum size of path buffer (including
5001 trailing zero)
5002
5003 Output:
5004 char path[buf_size] Path string
5005
5006 Function value:
5007 DB_SUCCESS Successful completion
5008 DB_INVALID_HANDLE Database handle is invalid
5009 DB_NO_MEMORY path buffer is to small to contain full
5010 string
5011
5012\********************************************************************/
5013{
5014 if (rpc_is_remote())
5015 return rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, buf_size);
5016
5017#ifdef LOCAL_ROUTINES
5018 {
5019 DATABASE_HEADER *pheader;
5020
5021 if (hDB > _database_entries || hDB <= 0) {
5022 cm_msg(MERROR, "db_get_path", "invalid database handle");
5023 return DB_INVALID_HANDLE;
5024 }
5025
5026 if (!_database[hDB - 1].attached) {
5027 cm_msg(MERROR, "db_get_path", "invalid database handle");
5028 return DB_INVALID_HANDLE;
5029 }
5030
5032
5033 pheader = _database[hDB - 1].database_header;
5034
5035 std::string xpath = db_get_path_locked(pheader, hKey);
5036
5038
5039 mstrlcpy(path, xpath.c_str(), buf_size);
5040
5041 return DB_SUCCESS;
5042 }
5043#endif /* LOCAL_ROUTINES */
5044
5045 return DB_SUCCESS;
5046}
5047
5048/*------------------------------------------------------------------*/
5050/********************************************************************\
5051
5052 Routine: db_get_path
5053
5054 Purpose: Get full path of a key
5055
5056 Input:
5057 HNDLE hDB Handle to the database
5058 HNDLE hKey Key handle
5059
5060 Function value: Path string
5061
5062\********************************************************************/
5063{
5064 if (rpc_is_remote()) {
5065 char path[MAX_ODB_PATH];
5066 int status = rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, sizeof(path));
5067 if (status != DB_SUCCESS) {
5068 sprintf(path, "(RPC_DB_GET_PATH status %d)", status);
5069 }
5070 return path;
5071 }
5072
5073#ifdef LOCAL_ROUTINES
5074 {
5075 DATABASE_HEADER *pheader;
5076
5077 if (hDB > _database_entries || hDB <= 0) {
5078 cm_msg(MERROR, "db_get_path", "invalid database handle");
5079 return "(DB_INVALID_HANDLE)";
5080 }
5081
5082 if (!_database[hDB - 1].attached) {
5083 cm_msg(MERROR, "db_get_path", "invalid database handle");
5084 return "(DB_INVALID_HANDLE)";
5085 }
5086
5088
5089 pheader = _database[hDB - 1].database_header;
5090
5091 std::string xpath = db_get_path_locked(pheader, hKey);
5092
5094
5095 return xpath;
5096 }
5097#endif /* LOCAL_ROUTINES */
5098
5099 return "(no LOCAL_ROUTINES)";
5100}
5101
5102#ifdef LOCAL_ROUTINES
5103/*------------------------------------------------------------------*/
5105{
5106 /* check if this key has notify count set */
5107 if (key->notify_count) {
5108
5110
5112
5113 std::string path = db_get_path_locked(pheader, hKey);
5114
5115 std::string line = msprintf("%s open %d times by", path.c_str(), key->notify_count);
5116
5117 //printf("path [%s] key.name [%s]\n", path, key->name);
5118
5119 int count = 0;
5120 for (int i = 0; i < pheader->max_client_index; i++) {
5121 DATABASE_CLIENT *pclient = &pheader->client[i];
5122 for (int j = 0; j < pclient->max_index; j++)
5123 if (pclient->open_record[j].handle == hKey) {
5124 count++;
5125 line += " \"";
5126 line += pclient->name;
5127 line += "\"";
5128 //sprintf(line + strlen(line), ", handle %d, mode %d ", pclient->open_record[j].handle, pclient->open_record[j].access_mode);
5129 }
5130 }
5131
5132 if (count < 1) {
5133 line += " a deleted client";
5134 }
5135
5136 line += "\n";
5137
5138 std::string *result = (std::string*)xresult;
5139 *result = line;
5140
5142 }
5143 return DB_SUCCESS;
5144}
5145
5147{
5148 std::string *result = (std::string*)xresult;
5149
5150 /* check if this key has notify count set */
5151 if (key->notify_count) {
5154 db_allow_write_locked(&_database[hDB - 1], "db_fix_open_records");
5155
5156 int i;
5157 for (i = 0; i < pheader->max_client_index; i++) {
5158 DATABASE_CLIENT *pclient = &pheader->client[i];
5159 int j;
5160 for (j = 0; j < pclient->max_index; j++)
5161 if (pclient->open_record[j].handle == hKey)
5162 break;
5163 if (j < pclient->max_index)
5164 break;
5165 }
5166 if (i == pheader->max_client_index) {
5167 /* check if hKey argument is correct */
5168 if (!db_validate_hkey(pheader, hKey)) {
5170 return DB_SUCCESS;
5171 }
5172
5173 /* reset notify count */
5174 KEY *pkey = (KEY *) ((char *) pheader + hKey);
5175 pkey->notify_count = 0;
5176
5177 std::string path = db_get_path_locked(pheader, hKey);
5178 *result += path;
5179 *result += " fixed\n";
5180 }
5181
5183 }
5184 return DB_SUCCESS;
5185}
5186#endif /* LOCAL_ROUTINES */
5187
5189/********************************************************************\
5190
5191 Routine: db_get_open_records
5192
5193 Purpose: Return a string with all open records
5194
5195 Input:
5196 HNDLE hDB Handle to the database
5197 HNDLE hKey Key to start search from, 0 for root
5198 INT buf_size Size of string
5199 INT fix If TRUE, fix records which are open
5200 but have no client belonging to it.
5201
5202 Output:
5203 char *str Result string. Individual records are
5204 separated with new lines.
5205
5206 Function value:
5207 DB_SUCCESS Successful completion
5208
5209\********************************************************************/
5210{
5211 str[0] = 0;
5212
5213 if (rpc_is_remote())
5214 return rpc_call(RPC_DB_GET_OPEN_RECORDS, hDB, hKey, str, buf_size);
5215
5216 std::string result;
5217
5218#ifdef LOCAL_ROUTINES
5219
5220 if (fix)
5221 db_scan_tree(hDB, hKey, 0, db_fix_open_records, &result); // FIXME: should use db_scan_tree_wlocked()
5222 else
5223 db_scan_tree(hDB, hKey, 0, db_find_open_records, &result); // FIXME: should use db_scan_tree_locked()
5224
5225#endif
5226
5227 mstrlcpy(str, result.c_str(), buf_size);
5228
5229 return DB_SUCCESS;
5230}
5231
5233#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5234
5235
5236/********************************************************************/
5261INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data,
5262 INT data_size, INT num_values, DWORD type)
5263{
5264 if (rpc_is_remote())
5265 return rpc_call(RPC_DB_SET_VALUE, hDB, hKeyRoot, key_name, data, data_size, num_values, type);
5266
5267#ifdef LOCAL_ROUTINES
5268 {
5269 INT status;
5270
5271 if (num_values == 0)
5272 return DB_INVALID_PARAM;
5273
5275
5277
5278 db_allow_write_locked(&_database[hDB-1], "db_set_value");
5279
5280 db_err_msg* msg = NULL;
5281
5282 KEY* pkey_root = (KEY*)db_get_pkey(pheader, hKeyRoot, &status, "db_set_value", &msg);
5283
5284 if (pkey_root) {
5285 status = db_set_value_wlocked(pheader, hDB, pkey_root, key_name, data, data_size, num_values, type, &msg);
5286 }
5287
5289 if (msg)
5290 db_flush_msg(&msg);
5291
5292 return status;
5293 }
5294#endif /* LOCAL_ROUTINES */
5295
5296 return DB_SUCCESS;
5297}
5298
5299#ifdef LOCAL_ROUTINES
5300static int db_set_value_wlocked(DATABASE_HEADER* pheader, HNDLE hDB, KEY* pkey_root, const char *key_name, const void *data, INT data_size, INT num_values, DWORD type, db_err_msg** msg)
5301{
5302 INT status;
5303
5304 if (num_values == 0)
5305 return DB_INVALID_PARAM;
5306
5307 KEY* pkey = (KEY*)db_find_pkey_locked(pheader, pkey_root, key_name, &status, msg);
5308
5309 if (!pkey) {
5310 status = db_create_key_wlocked(pheader, pkey_root, key_name, type, &pkey, msg);
5311 if (status != DB_SUCCESS && status != DB_CREATED)
5312 return status;
5313 }
5314
5315 if (data_size == 0) {
5316 db_msg(msg, MERROR, "db_set_value", "zero data size not allowed");
5317 return DB_TYPE_MISMATCH;
5318 }
5319
5320 if (type != TID_STRING && type != TID_LINK && data_size != rpc_tid_size(type) * num_values) {
5321 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);
5322 return DB_TYPE_MISMATCH;
5323 }
5324
5325 status = db_check_set_data_locked(pheader, pkey, data, data_size, num_values, type, "db_set_value", msg);
5326
5327 if (status != DB_SUCCESS)
5328 return status;
5329
5330 status = db_set_data_wlocked(pheader, pkey, data, data_size, num_values, type, "db_set_value", msg);
5331
5332 if (status != DB_SUCCESS)
5333 return status;
5334
5335 db_notify_clients_locked(pheader, hDB, db_pkey_to_hkey(pheader, pkey), -1, TRUE, msg);
5336
5337 return DB_SUCCESS;
5338}
5339#endif /* LOCAL_ROUTINES */
5340
5341/********************************************************************/
5365INT db_set_value_index(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data,
5366 INT data_size, INT idx, DWORD type, BOOL trunc)
5367{
5368 int status;
5369 HNDLE hkey;
5370
5371 status = db_find_key(hDB, hKeyRoot, key_name, &hkey);
5372 if (!hkey) {
5373 status = db_create_key(hDB, hKeyRoot, key_name, type);
5374 status = db_find_key(hDB, hKeyRoot, key_name, &hkey);
5375 if (status != DB_SUCCESS)
5376 return status;
5377 } else
5378 if (trunc) {
5380 if (status != DB_SUCCESS)
5381 return status;
5382 }
5383
5384 return db_set_data_index(hDB, hkey, data, data_size, idx, type);
5385}
5386
5387/********************************************************************/
5415INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT * buf_size, DWORD type, BOOL create)
5416{
5417 if (rpc_is_remote())
5418 return rpc_call(RPC_DB_GET_VALUE, hDB, hKeyRoot, key_name, data, buf_size, type, create);
5419
5420#ifdef LOCAL_ROUTINES
5421 {
5422 INT status;
5423
5424 if (hDB > _database_entries || hDB <= 0) {
5425 cm_msg(MERROR, "db_get_value", "invalid database handle %d", hDB);
5426 return DB_INVALID_HANDLE;
5427 }
5428
5429 if (!_database[hDB - 1].attached) {
5430 cm_msg(MERROR, "db_get_value", "invalid database handle %d", hDB);
5431 return DB_INVALID_HANDLE;
5432 }
5433
5434 /* check if key name contains index */
5435 char keyname[MAX_ODB_PATH];
5436 mstrlcpy(keyname, key_name, sizeof(keyname));
5437 int idx = -1;
5438 if (strchr(keyname, '[') && strchr(keyname, ']')) {
5439 char* p;
5440 for (p = strchr(keyname, '[') + 1; *p && *p != ']'; p++)
5441 if (!isdigit(*p))
5442 break;
5443
5444 if (*p && *p == ']') {
5445 idx = atoi(strchr(keyname, '[') + 1);
5446 *strchr(keyname, '[') = 0;
5447 }
5448 }
5449
5450 /* now lock database */
5452
5454 db_err_msg* msg = NULL;
5455
5456 const KEY* pkey_root = db_get_pkey(pheader, hKeyRoot, &status, "db_get_value", &msg);
5457
5458 if (!pkey_root) {
5460 if (msg)
5461 db_flush_msg(&msg);
5462 return status;
5463 }
5464
5465 const KEY* pkey = db_find_pkey_locked(pheader, pkey_root, keyname, &status, &msg);
5466
5467 if (!pkey) {
5468 if (create) {
5469 db_allow_write_locked(&_database[hDB-1], "db_get_value");
5470 /* set default value if key was created */
5471
5472 /* get string size from data size */
5473 int size;
5474 if (type == TID_STRING || type == TID_LINK)
5475 size = *buf_size;
5476 else
5477 size = rpc_tid_size(type);
5478
5479 status = db_set_value_wlocked(pheader, hDB, (KEY*)pkey_root, keyname, data, *buf_size, *buf_size / size, type, &msg);
5481 if (msg)
5482 db_flush_msg(&msg);
5483 return status;
5484 } else {
5486 if (msg)
5487 db_flush_msg(&msg);
5488 return DB_NO_KEY;
5489 }
5490 }
5491
5492 status = db_get_data_locked(pheader, pkey, idx, data, buf_size, type, &msg);
5493
5495 if (msg)
5496 db_flush_msg(&msg);
5497
5498 return status;
5499 }
5500#endif /* LOCAL_ROUTINES */
5501
5502 return DB_SUCCESS;
5503}
5504
5505#ifdef LOCAL_ROUTINES
5506static INT db_get_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, void *data, INT * buf_size, DWORD type, db_err_msg** msg)
5507{
5508 /* check for correct type */
5509 if (pkey->type != (type)) {
5510 db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" is of type %s, not %s", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(pkey->type), rpc_tid_name(type));
5511 return DB_TYPE_MISMATCH;
5512 }
5513
5514 /* check for correct type */
5515 if (pkey->type == TID_KEY) {
5516 db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" is of type %s, cannot contain data", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(pkey->type));
5517 return DB_TYPE_MISMATCH;
5518 }
5519
5520 /* check for read access */
5521 if (!(pkey->access_mode & MODE_READ)) {
5522 db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" has no read access", db_get_path_locked(pheader, pkey).c_str());
5523 return DB_NO_ACCESS;
5524 }
5525
5526 /* check if buffer is too small */
5527 if ((idx == -1 && pkey->num_values * pkey->item_size > *buf_size) || (idx != -1 && pkey->item_size > *buf_size)) {
5528 memcpy(data, (char *) pheader + pkey->data, *buf_size);
5529 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_locked(pheader, pkey).c_str(), pkey->num_values * pkey->item_size, pkey->num_values, pkey->item_size, *buf_size);
5530 return DB_TRUNCATED;
5531 }
5532
5533 /* check if index in boundaries */
5534 if (idx != -1 && idx >= pkey->num_values) {
5535 cm_msg(MERROR, "db_get_data_locked", "odb entry \"%s\" index %d is out of valid range 0..%d", db_get_path_locked(pheader, pkey).c_str(), idx, pkey->num_values-1);
5536 return DB_INVALID_PARAM;
5537 }
5538
5539 /* copy key data */
5540 if (idx == -1) {
5541 memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
5542 *buf_size = pkey->num_values * pkey->item_size;
5543 } else {
5544 memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, pkey->item_size);
5545 *buf_size = pkey->item_size;
5546 }
5547
5548 return DB_SUCCESS;
5549}
5550#endif /* LOCAL_ROUTINES */
5551
5552/********************************************************************/
5587{
5588 if (rpc_is_remote())
5590
5591#ifdef LOCAL_ROUTINES
5592 {
5593 DATABASE_HEADER *pheader;
5594 INT i;
5595 char str[256];
5596 HNDLE parent;
5597
5598 if (hDB > _database_entries || hDB <= 0) {
5599 cm_msg(MERROR, "db_enum_key", "invalid database handle");
5600 return DB_INVALID_HANDLE;
5601 }
5602
5603 if (!_database[hDB - 1].attached) {
5604 cm_msg(MERROR, "db_enum_key", "invalid database handle");
5605 return DB_INVALID_HANDLE;
5606 }
5607
5608 *subkey_handle = 0;
5609
5610 /* first lock database */
5612
5613 pheader = _database[hDB - 1].database_header;
5614 if (!hKey)
5615 hKey = pheader->root_key;
5616
5617 /* check if hKey argument is correct */
5618 if (!db_validate_hkey(pheader, hKey)) {
5620 return DB_INVALID_HANDLE;
5621 }
5622
5623 db_err_msg *msg = NULL;
5624 int status;
5625
5626 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_enum_key", &msg);
5627
5628 if (!pkey) {
5630 db_flush_msg(&msg);
5631 return DB_NO_MORE_SUBKEYS;
5632 }
5633
5634 if (pkey->type != TID_KEY) {
5636 return DB_NO_MORE_SUBKEYS;
5637 }
5638
5639 const KEYLIST* pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_enum_key", &msg);
5640
5641 if (!pkeylist) {
5643 db_flush_msg(&msg);
5644 return DB_NO_MORE_SUBKEYS;
5645 }
5646
5647 if (idx >= pkeylist->num_keys) {
5649 return DB_NO_MORE_SUBKEYS;
5650 }
5651
5652 pkey = db_get_pkey(pheader, pkeylist->first_key, &status, "db_enum_key", &msg);
5653
5654 if (!pkey) {
5655 std::string path = db_get_path_locked(pheader, hKey);
5656 HNDLE xfirst_key = pkeylist->first_key;
5658 if (msg)
5659 db_flush_msg(&msg);
5660 cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" invalid first_key %d", hKey, path.c_str(), xfirst_key);
5661 return DB_NO_MORE_SUBKEYS;
5662 }
5663
5664 for (i = 0; i < idx; i++) {
5665 if (pkey->next_key == 0) {
5666 std::string path = db_get_path_locked(pheader, hKey);
5668 cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" unexpected end of key list at index %d", hKey, path.c_str(), i);
5669 return DB_NO_MORE_SUBKEYS;
5670 }
5671
5672 pkey = db_get_pkey(pheader, pkey->next_key, &status, "db_enum_key", &msg);
5673
5674 if (!pkey) {
5675 std::string path = db_get_path_locked(pheader, hKey);
5677 db_flush_msg(&msg);
5678 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);
5679 return DB_NO_MORE_SUBKEYS;
5680 }
5681 }
5682
5683 /* resolve links */
5684 if (pkey->type == TID_LINK) {
5685 strcpy(str, (char *) pheader + pkey->data);
5686
5687 /* no not resolve if link to array index */
5688 if (strlen(str) > 0 && str[strlen(str) - 1] == ']') {
5689 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5691 return DB_SUCCESS;
5692 }
5693
5694 if (*str == '/') {
5695 /* absolute path */
5697 return db_find_key(hDB, 0, str, subkey_handle);
5698 } else {
5699 /* relative path */
5700 if (pkey->parent_keylist) {
5701 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
5702 parent = pkeylist->parent;
5704 return db_find_key(hDB, parent, str, subkey_handle);
5705 } else {
5707 return db_find_key(hDB, 0, str, subkey_handle);
5708 }
5709 }
5710 }
5711
5712 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5714 }
5715#endif /* LOCAL_ROUTINES */
5716
5717 return DB_SUCCESS;
5718}
5719
5721#ifndef DOXYGEN_SHOULD_SKIP_THIS
5722
5723
5724/*------------------------------------------------------------------*/
5726/********************************************************************\
5727
5728 Routine: db_enum_link
5729
5730 Purpose: Enumerate subkeys from a key, don't follow links
5731
5732 Input:
5733 HNDLE hDB Handle to the database
5734 HNDLE hKey Handle of key to enumerate, zero for the
5735 root key
5736 INT idx Subkey index, sould be initially 0, then
5737 incremented in each call until
5738 *subhKey becomes zero and the function
5739 returns DB_NO_MORE_SUBKEYS
5740
5741 Output:
5742 HNDLE *subkey_handle Handle of subkey which can be used in
5743 db_get_key and db_get_data
5744
5745 Function value:
5746 DB_SUCCESS Successful completion
5747 DB_INVALID_HANDLE Database handle is invalid
5748 DB_NO_MORE_SUBKEYS Last subkey reached
5749
5750\********************************************************************/
5751{
5752 if (rpc_is_remote())
5754
5755#ifdef LOCAL_ROUTINES
5756 {
5757 DATABASE_HEADER *pheader;
5759 KEY *pkey;
5760 INT i;
5761
5762 if (hDB > _database_entries || hDB <= 0) {
5763 cm_msg(MERROR, "db_enum_link", "invalid database handle");
5764 return DB_INVALID_HANDLE;
5765 }
5766
5767 if (!_database[hDB - 1].attached) {
5768 cm_msg(MERROR, "db_enum_link", "invalid database handle");
5769 return DB_INVALID_HANDLE;
5770 }
5771
5772 *subkey_handle = 0;
5773
5774 /* first lock database */
5776
5777 pheader = _database[hDB - 1].database_header;
5778 if (!hKey)
5779 hKey = pheader->root_key;
5780
5781 /* check if hKey argument is correct */
5782 if (!db_validate_hkey(pheader, hKey)) {
5784 return DB_INVALID_HANDLE;
5785 }
5786
5787 pkey = (KEY *) ((char *) pheader + hKey);
5788
5789 if (pkey->type != TID_KEY) {
5791 return DB_NO_MORE_SUBKEYS;
5792 }
5793 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
5794
5795 if (idx >= pkeylist->num_keys) {
5797 return DB_NO_MORE_SUBKEYS;
5798 }
5799
5800 // FIXME: validate pkeylist->first_key
5801 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
5802 for (i = 0; i < idx; i++) {
5803 // FIXME: validate pkey->next_key
5804 pkey = (KEY *) ((char *) pheader + pkey->next_key);
5805 }
5806
5807 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5809 }
5810#endif /* LOCAL_ROUTINES */
5811
5812 return DB_SUCCESS;
5813}
5814
5815/*------------------------------------------------------------------*/
5817/********************************************************************\
5818
5819 Routine: db_get_next_link
5820
5821 Purpose: Get next key in ODB after hKey
5822
5823 Input:
5824 HNDLE hDB Handle to the database
5825 HNDLE hKey Handle of key to enumerate, zero for the
5826 root key
5827
5828 Output:
5829 HNDLE *subkey_handle Handle of subkey which can be used in
5830 db_get_key and db_get_data
5831
5832 Function value:
5833 DB_SUCCESS Successful completion
5834 DB_INVALID_HANDLE Database handle is invalid
5835 DB_NO_MORE_SUBKEYS Last subkey reached
5836
5837\********************************************************************/
5838{
5839 if (rpc_is_remote())
5841
5842#ifdef LOCAL_ROUTINES
5843 {
5844 DATABASE_HEADER *pheader;
5846 KEY *pkey;
5847 INT descent;
5848
5849 if (hDB > _database_entries || hDB <= 0) {
5850 cm_msg(MERROR, "db_enum_link", "invalid database handle");
5851 return DB_INVALID_HANDLE;
5852 }
5853
5854 if (!_database[hDB - 1].attached) {
5855 cm_msg(MERROR, "db_enum_link", "invalid database handle");
5856 return DB_INVALID_HANDLE;
5857 }
5858
5859 *subkey_handle = 0;
5860
5861 /* first lock database */
5863
5864 pheader = _database[hDB - 1].database_header;
5865 if (!hKey)
5866 hKey = pheader->root_key;
5867
5868 /* check if hKey argument is correct */
5869 if (!db_validate_hkey(pheader, hKey)) {
5871 return DB_INVALID_HANDLE;
5872 }
5873
5874 pkey = (KEY *) ((char *) pheader + hKey);
5875
5876 descent = TRUE;
5877 do {
5878 if (pkey->type != TID_KEY || !descent) {
5879 if (pkey->next_key) {
5880 /* key has next key, return it */
5881 // FIXME: validate pkey->next_key
5882 pkey = (KEY *) ((char *) pheader + pkey->next_key);
5883
5884 if (pkey->type != TID_KEY) {
5885 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5887 return DB_SUCCESS;
5888 }
5889
5890 /* key has subkeys, so descent on the next iteration */
5891 descent = TRUE;
5892 } else {
5893 if (pkey->parent_keylist == 0) {
5894 /* return if we are back to the root key */
5896 return DB_NO_MORE_SUBKEYS;
5897 }
5898
5899 /* key is last in list, traverse up */
5900 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
5901
5902 // FIXME: validate pkeylist->parent
5903 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
5904 descent = FALSE;
5905 }
5906 } else {
5907 if (descent) {
5908 /* find first subkey */
5909 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
5910
5911 if (pkeylist->num_keys == 0) {
5912 /* if key has no subkeys, look for next key on this level */
5913 descent = FALSE;
5914 } else {
5915 /* get first subkey */
5916 // FIXME: validate pkeylist->first_key
5917 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
5918
5919 if (pkey->type != TID_KEY) {
5920 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5922 return DB_SUCCESS;
5923 }
5924 }
5925 }
5926 }
5927
5928 } while (TRUE);
5929 }
5930#endif /* LOCAL_ROUTINES */
5931
5932 return DB_SUCCESS;
5933}
5934
5936#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5937
5938/********************************************************************/
5939
5940#ifdef LOCAL_ROUTINES
5941
5943{
5944 if (!hKey)
5945 hKey = pheader->root_key;
5946
5947 /* check if hKey argument is correct */
5948 if (!db_validate_hkey(pheader, hKey)) {
5949 return DB_INVALID_HANDLE;
5950 }
5951
5952 const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
5953
5954 if (pkey->type < 1 || pkey->type >= TID_LAST) {
5955 int pkey_type = pkey->type;
5956 db_msg(msg, MERROR, "db_get_key", "hkey %d invalid key type %d", hKey, pkey_type);
5957 return DB_INVALID_HANDLE;
5958 }
5959
5960 /* check for link to array index */
5961 if (pkey->type == TID_LINK) {
5962 char link_name[MAX_ODB_PATH];
5963 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
5964 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
5965 if (strchr(link_name, '[') == NULL)
5966 return DB_INVALID_LINK;
5967
5969 if (db_find_key_locked(pheader, 0, link_name, &hkeylink, msg) != DB_SUCCESS)
5970 return DB_INVALID_LINK;
5971 int status = db_get_key_locked(pheader, hkeylink, key, msg);
5972 key->num_values = 1; // fake number of values
5973 return status;
5974 }
5975 }
5976
5977 memcpy(key, pkey, sizeof(KEY));
5978
5979 return DB_SUCCESS;
5980}
5981#endif /* LOCAL_ROUTINES */
5982
5983/********************************************************************/
6020{
6021 if (rpc_is_remote())
6022 return rpc_call(RPC_DB_GET_KEY, hDB, hKey, key);
6023
6024#ifdef LOCAL_ROUTINES
6025 {
6026 DATABASE_HEADER *pheader;
6027 int status;
6028
6029 if (hDB > _database_entries || hDB <= 0) {
6030 cm_msg(MERROR, "db_get_key", "invalid database handle");
6031 return DB_INVALID_HANDLE;
6032 }
6033
6034 if (!_database[hDB - 1].attached) {
6035 cm_msg(MERROR, "db_get_key", "invalid database handle");
6036 return DB_INVALID_HANDLE;
6037 }
6038
6039 if (hKey < (int) sizeof(DATABASE_HEADER) && hKey != 0) {
6040 cm_msg(MERROR, "db_get_key", "invalid key handle");
6041 return DB_INVALID_HANDLE;
6042 }
6043
6044 db_err_msg *msg = NULL;
6045
6047
6048 pheader = _database[hDB - 1].database_header;
6049
6050 status = db_get_key_locked(pheader, hKey, key, &msg);
6051
6053
6054 if (msg)
6055 db_flush_msg(&msg);
6056
6057 return status;
6058 }
6059#endif /* LOCAL_ROUTINES */
6060
6061 return DB_SUCCESS;
6062}
6063
6064/********************************************************************/
6073{
6074 if (rpc_is_remote())
6075 return rpc_call(RPC_DB_GET_LINK, hDB, hKey, key);
6076
6077#ifdef LOCAL_ROUTINES
6078 {
6079 DATABASE_HEADER *pheader;
6080
6081 if (hDB > _database_entries || hDB <= 0) {
6082 cm_msg(MERROR, "db_get_link", "invalid database handle");
6083 return DB_INVALID_HANDLE;
6084 }
6085
6086 if (!_database[hDB - 1].attached) {
6087 cm_msg(MERROR, "db_get_link", "invalid database handle");
6088 return DB_INVALID_HANDLE;
6089 }
6090
6091 if (hKey < (int) sizeof(DATABASE_HEADER) && hKey != 0) {
6092 cm_msg(MERROR, "db_get_link", "invalid key handle");
6093 return DB_INVALID_HANDLE;
6094 }
6095
6096 db_err_msg *msg = NULL;
6097
6099
6100 pheader = _database[hDB - 1].database_header;
6101
6102 int status = DB_SUCCESS;
6103
6104 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_get_link", &msg);
6105 if (pkey) {
6106 memcpy(key, pkey, sizeof(KEY));
6107 } else {
6108 memset(key, 0, sizeof(KEY));
6109 //abort();
6110 }
6111
6113
6114 if (msg)
6115 db_flush_msg(&msg);
6116
6117 return status;
6118 }
6119#endif /* LOCAL_ROUTINES */
6120
6121 return DB_SUCCESS;
6122}
6123
6124/********************************************************************/
6133{
6134 if (rpc_is_remote())
6136
6137#ifdef LOCAL_ROUTINES
6138 {
6139 DATABASE_HEADER *pheader;
6140 KEY *pkey;
6141
6142 if (hDB > _database_entries || hDB <= 0) {
6143 cm_msg(MERROR, "db_get_key", "invalid database handle");
6144 return DB_INVALID_HANDLE;
6145 }
6146
6147 if (!_database[hDB - 1].attached) {
6148 cm_msg(MERROR, "db_get_key", "invalid database handle");
6149 return DB_INVALID_HANDLE;
6150 }
6151
6152 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6153 cm_msg(MERROR, "db_get_key", "invalid key handle");
6154 return DB_INVALID_HANDLE;
6155 }
6156
6158
6159 pheader = _database[hDB - 1].database_header;
6160
6161 /* check if hKey argument is correct */
6162 if (!db_validate_hkey(pheader, hKey)) {
6164 return DB_INVALID_HANDLE;
6165 }
6166
6167 pkey = (KEY *) ((char *) pheader + hKey);
6168
6169 *delta = ss_time() - pkey->last_written;
6170
6172
6173 }
6174#endif /* LOCAL_ROUTINES */
6175
6176 return DB_SUCCESS;
6177}
6178
6179/********************************************************************/
6191INT db_get_key_info(HNDLE hDB, HNDLE hKey, char *name, INT name_size, INT * type, INT * num_values, INT * item_size)
6192{
6193 if (rpc_is_remote())
6194 return rpc_call(RPC_DB_GET_KEY_INFO, hDB, hKey, name, name_size, type, num_values, item_size);
6195
6196#ifdef LOCAL_ROUTINES
6197 {
6198 DATABASE_HEADER *pheader;
6199 KEY *pkey;
6201
6202 if (hDB > _database_entries || hDB <= 0) {
6203 cm_msg(MERROR, "db_get_key_info", "invalid database handle");
6204 return DB_INVALID_HANDLE;
6205 }
6206
6207 if (!_database[hDB - 1].attached) {
6208 cm_msg(MERROR, "db_get_key_info", "invalid database handle");
6209 return DB_INVALID_HANDLE;
6210 }
6211
6212 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6213 cm_msg(MERROR, "db_get_key_info", "invalid key handle");
6214 return DB_INVALID_HANDLE;
6215 }
6216
6218
6219 pheader = _database[hDB - 1].database_header;
6220
6221 /* check if hKey argument is correct */
6222 if (!db_validate_hkey(pheader, hKey)) {
6224 return DB_INVALID_HANDLE;
6225 }
6226
6227 pkey = (KEY *) ((char *) pheader + hKey);
6228
6229 if ((INT) strlen(pkey->name) + 1 > name_size) {
6230 /* truncate name */
6231 memcpy(name, pkey->name, name_size - 1);
6232 name[name_size] = 0;
6233 } else
6234 strcpy(name, pkey->name);
6235
6236 /* convert "root" to "/" */
6237 if (strcmp(name, "root") == 0)
6238 strcpy(name, "/");
6239
6240 *type = pkey->type;
6241 *num_values = pkey->num_values;
6242 *item_size = pkey->item_size;
6243
6244 if (pkey->type == TID_KEY) {
6245 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
6246 *num_values = pkeylist->num_keys;
6247 }
6248
6250 }
6251#endif /* LOCAL_ROUTINES */
6252
6253 return DB_SUCCESS;
6254}
6255
6257#ifndef DOXYGEN_SHOULD_SKIP_THIS
6258
6259
6260/*------------------------------------------------------------------*/
6262/********************************************************************\
6263
6264 Routine: db_get_key
6265
6266 Purpose: Rename a key
6267
6268 Input:
6269 HNDLE hDB Handle to the database
6270 HNDLE hKey Handle of key
6271 char *name New key name
6272
6273 Output:
6274 <none>
6275
6276 Function value:
6277 DB_SUCCESS Successful completion
6278 DB_INVALID_HANDLE Database handle is invalid
6279 DB_INVALID_NAME Key name contains '/'
6280
6281\********************************************************************/
6282{
6283 if (rpc_is_remote())
6285
6286#ifdef LOCAL_ROUTINES
6287 {
6288 DATABASE_HEADER *pheader;
6289 KEY *pkey;
6290 int status;
6291
6292 if (hDB > _database_entries || hDB <= 0) {
6293 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6294 return DB_INVALID_HANDLE;
6295 }
6296
6297 if (!_database[hDB - 1].attached) {
6298 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6299 return DB_INVALID_HANDLE;
6300 }
6301
6302 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6303 cm_msg(MERROR, "db_rename_key", "invalid key handle");
6304 return DB_INVALID_HANDLE;
6305 }
6306
6307 db_err_msg* msg = NULL;
6308 status = db_validate_name(name, FALSE, "db_rename_key", &msg);
6309 if (msg)
6310 db_flush_msg(&msg);
6311 if (status != DB_SUCCESS)
6312 return status;
6313
6314 if (name == NULL) {
6315 cm_msg(MERROR, "db_rename_key", "key name is NULL");
6316 return DB_INVALID_NAME;
6317 }
6318
6319 if (strlen(name) < 1) {
6320 cm_msg(MERROR, "db_rename_key", "key name is too short");
6321 return DB_INVALID_NAME;
6322 }
6323
6324 if (strchr(name, '/')) {
6325 cm_msg(MERROR, "db_rename_key", "key name may not contain \"/\"");
6326 return DB_INVALID_NAME;
6327 }
6328
6330
6331 pheader = _database[hDB - 1].database_header;
6332
6333 /* check if hKey argument is correct */
6334 if (!db_validate_hkey(pheader, hKey)) {
6336 return DB_INVALID_HANDLE;
6337 }
6338
6339 pkey = (KEY *) ((char *) pheader + hKey);
6340
6341 if (!pkey->type) {
6342 int pkey_type = pkey->type;
6344 cm_msg(MERROR, "db_rename_key", "hkey %d invalid key type %d", hKey, pkey_type);
6345 return DB_INVALID_HANDLE;
6346 }
6347
6348 db_allow_write_locked(&_database[hDB - 1], "db_rename_key");
6349
6350 mstrlcpy(pkey->name, name, NAME_LENGTH);
6351
6353
6354 }
6355#endif /* LOCAL_ROUTINES */
6356
6357 return DB_SUCCESS;
6358}
6359
6360/*------------------------------------------------------------------*/
6362/********************************************************************\
6363
6364 Routine: db_reorder_key
6365
6366 Purpose: Reorder key so that key hKey apprears at position 'index'
6367 in keylist (or at bottom if index<0)
6368
6369 Input:
6370 HNDLE hDB Handle to the database
6371 HNDLE hKey Handle of key
6372 INT idx New positio of key in keylist
6373
6374 Output:
6375 <none>
6376
6377 Function value:
6378 DB_SUCCESS Successful completion
6379 DB_INVALID_HANDLE Database handle is invalid
6380 DB_NO_ACCESS Key is locked for write
6381 DB_OPEN_RECORD Key, subkey or parent key is open
6382
6383\********************************************************************/
6384{
6385 if (rpc_is_remote())
6387
6388#ifdef LOCAL_ROUTINES
6389 {
6390 DATABASE_HEADER *pheader;
6393 INT i;
6394
6395 if (hDB > _database_entries || hDB <= 0) {
6396 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6397 return DB_INVALID_HANDLE;
6398 }
6399
6400 if (!_database[hDB - 1].attached) {
6401 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6402 return DB_INVALID_HANDLE;
6403 }
6404
6405 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6406 cm_msg(MERROR, "db_rename_key", "invalid key handle");
6407 return DB_INVALID_HANDLE;
6408 }
6409
6411
6412 pheader = _database[hDB - 1].database_header;
6413
6414 /* check if hKey argument is correct */
6415 if (!db_validate_hkey(pheader, hKey)) {
6417 return DB_INVALID_HANDLE;
6418 }
6419
6420 pkey = (KEY *) ((char *) pheader + hKey);
6421
6422 if (!pkey->type) {
6423 int pkey_type = pkey->type;
6425 cm_msg(MERROR, "db_reorder_key", "hkey %d invalid key type %d", hKey, pkey_type);
6426 return DB_INVALID_HANDLE;
6427 }
6428
6429 if (!(pkey->access_mode & MODE_WRITE)) {
6431 return DB_NO_ACCESS;
6432 }
6433
6434 /* check if someone has opened key or parent */
6435 do {
6436#ifdef CHECK_OPEN_RECORD
6437 if (pkey->notify_count) {
6439 return DB_OPEN_RECORD;
6440 }
6441#endif
6442 if (pkey->parent_keylist == 0)
6443 break;
6444
6445 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
6446 // FIXME: validate pkeylist->parent
6447 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
6448 } while (TRUE);
6449
6450 db_allow_write_locked(&_database[hDB - 1], "db_reorder_key");
6451
6452 pkey = (KEY *) ((char *) pheader + hKey); // NB: hKey is already validated
6453 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
6454
6455 /* first remove key from list */
6456 pnext_key = (KEY *) (POINTER_T) pkey->next_key; // FIXME: what is this pointer cast?
6457
6458 if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
6459 /* key is first in list */
6460 pkeylist->first_key = (POINTER_T) pnext_key;
6461 } else {
6462 /* find predecessor */
6463 // FIXME: validate pkeylist->first_key
6464 pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
6465 while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey) {
6466 // FIXME: validate pkey_tmp->next_key
6467 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
6468 }
6469 pkey_tmp->next_key = (POINTER_T) pnext_key;
6470 }
6471
6472 /* add key to list at proper index */
6473 // FIXME: validate pkeylist->first_key
6474 pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
6475 if (idx < 0 || idx >= pkeylist->num_keys - 1) {
6476 /* add at bottom */
6477
6478 /* find last key */
6479 for (i = 0; i < pkeylist->num_keys - 2; i++) {
6480 // FIXME: validate pkey_tmp->next_key
6481 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
6482 }
6483
6484 pkey_tmp->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
6485 pkey->next_key = 0;
6486 } else {
6487 if (idx == 0) {
6488 /* add at top */
6489 pkey->next_key = pkeylist->first_key;
6490 pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
6491 } else {
6492 /* add at position index */
6493 for (i = 0; i < idx - 1; i++) {
6494 // FIXME: validate pkey_tmp->next_key
6495 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
6496 }
6497
6498 pkey->next_key = pkey_tmp->next_key;
6499 pkey_tmp->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
6500 }
6501 }
6502
6504
6505 }
6506#endif /* LOCAL_ROUTINES */
6507
6508 return DB_SUCCESS;
6509}
6510
6512#endif /* DOXYGEN_SHOULD_SKIP_THIS */
6513
6514
6515/********************************************************************/
6540{
6541 if (rpc_is_remote())
6542 return rpc_call(RPC_DB_GET_DATA, hDB, hKey, data, buf_size, type);
6543
6544#ifdef LOCAL_ROUTINES
6545 {
6546 int status;
6547
6548 if (hDB > _database_entries || hDB <= 0) {
6549 cm_msg(MERROR, "db_get_data", "Invalid database handle");
6550 return DB_INVALID_HANDLE;
6551 }
6552
6553 if (!_database[hDB - 1].attached) {
6554 cm_msg(MERROR, "db_get_data", "invalid database handle");
6555 return DB_INVALID_HANDLE;
6556 }
6557
6558 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6559 cm_msg(MERROR, "db_get_data", "invalid key handle");
6560 return DB_INVALID_HANDLE;
6561 }
6562
6564
6566 db_err_msg* msg = NULL;
6567
6568 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_get_data", &msg);
6569
6570 if (!pkey) {
6572 if (msg)
6573 db_flush_msg(&msg);
6574 return status;
6575 }
6576
6577 /* check for read access */
6578 if (!(pkey->access_mode & MODE_READ)) {
6580 if (msg)
6581 db_flush_msg(&msg);
6582 return DB_NO_ACCESS;
6583 }
6584
6585 /* follow links to array index */
6586 if (pkey->type == TID_LINK) {
6587 std::string link_name = (char *) pheader + pkey->data;
6588 if (link_name.length() > 0 && link_name.back() == ']') {
6589 size_t pos = link_name.rfind("[");
6590 if (pos == std::string::npos) {
6591 db_msg(&msg, MERROR, "db_get_data", "missing \"[\" in symlink to array element \"%s\" in \"%s\"", link_name.c_str(), db_get_path_locked(pheader, pkey).c_str());
6593 if (msg)
6594 db_flush_msg(&msg);
6595 return DB_INVALID_LINK;
6596 }
6597 int idx = atoi(link_name.c_str()+pos+1);
6598 link_name.resize(pos);
6599 //printf("link name [%s] idx %d\n", link_name.c_str(), idx);
6600
6601 // relative symlinks did not work in the old db_get_data(), make sure they do not work now. K.O.
6602 if (link_name[0] != '/') {
6603 db_msg(&msg, MERROR, "db_get_data", "symlink \"%s\" should start with \"/\" in \"%s\"", link_name.c_str(), db_get_path_locked(pheader, pkey).c_str());
6605 if (msg)
6606 db_flush_msg(&msg);
6607 return DB_INVALID_LINK;
6608 }
6609
6610 const KEY* pkey = db_find_pkey_locked(pheader, NULL, link_name.c_str(), &status, &msg);
6611
6612 if (!pkey) {
6614 if (msg)
6615 db_flush_msg(&msg);
6616 return status;
6617 }
6618
6619 //printf("db_get_data [%s] type [%s] idx %d\n", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(type), idx);
6620
6621 status = db_get_data_locked(pheader, pkey, idx, data, buf_size, type, &msg);
6622
6624 if (msg)
6625 db_flush_msg(&msg);
6626 return status;
6627 }
6628 }
6629
6630 //printf("db_get_data [%s] type [%s]\n", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(type));
6631
6632 status = db_get_data_locked(pheader, pkey, -1, data, buf_size, type, &msg);
6633
6635 if (msg)
6636 db_flush_msg(&msg);
6637
6638 return status;
6639 }
6640#endif /* LOCAL_ROUTINES */
6641
6642 return DB_SUCCESS;
6643}
6644
6645/********************************************************************/
6657{
6658 if (rpc_is_remote())
6659 return rpc_call(RPC_DB_GET_LINK_DATA, hDB, hKey, data, buf_size, type);
6660
6661#ifdef LOCAL_ROUTINES
6662 {
6663 DATABASE_HEADER *pheader;
6664 KEY *pkey;
6665
6666 if (hDB > _database_entries || hDB <= 0) {
6667 cm_msg(MERROR, "db_get_data", "Invalid database handle");
6668 return DB_INVALID_HANDLE;
6669 }
6670
6671 if (!_database[hDB - 1].attached) {
6672 cm_msg(MERROR, "db_get_data", "invalid database handle");
6673 return DB_INVALID_HANDLE;
6674 }
6675
6676 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6677 cm_msg(MERROR, "db_get_data", "invalid key handle");
6678 return DB_INVALID_HANDLE;
6679 }
6680
6682
6683 pheader = _database[hDB - 1].database_header;
6684
6685 /* check if hKey argument is correct */
6686 if (!db_validate_hkey(pheader, hKey)) {
6688 return DB_INVALID_HANDLE;
6689 }
6690
6691 pkey = (KEY *) ((char *) pheader + hKey);
6692
6693 /* check for read access */
6694 if (!(pkey->access_mode & MODE_READ)) {
6696 return DB_NO_ACCESS;
6697 }
6698
6699 if (!pkey->type) {
6700 int pkey_type = pkey->type;
6702 cm_msg(MERROR, "db_get_data", "hkey %d invalid key type %d", hKey, pkey_type);
6703 return DB_INVALID_HANDLE;
6704 }
6705
6706 if (pkey->type != type) {
6707 int pkey_type = pkey->type;
6708 char pkey_name[NAME_LENGTH];
6709 mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
6711 cm_msg(MERROR, "db_get_data", "\"%s\" is of type %s, not %s", pkey_name, rpc_tid_name(pkey_type), rpc_tid_name(type));
6712 return DB_TYPE_MISMATCH;
6713 }
6714
6715 /* keys cannot contain data */
6716 if (pkey->type == TID_KEY) {
6718 cm_msg(MERROR, "db_get_data", "Key cannot contain data");
6719 return DB_TYPE_MISMATCH;
6720 }
6721
6722 /* check if key has data */
6723 if (pkey->data == 0) {
6724 memset(data, 0, *buf_size);
6725 *buf_size = 0;
6727 return DB_SUCCESS;
6728 }
6729
6730 /* check if buffer is too small */
6731 if (pkey->num_values * pkey->item_size > *buf_size) {
6732 int pkey_size = pkey->num_values * pkey->item_size;
6733 memcpy(data, (char *) pheader + pkey->data, *buf_size);
6735 std::string path = db_get_path(hDB, hKey);
6736 cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
6737 return DB_TRUNCATED;
6738 }
6739
6740 /* copy key data */
6741 memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
6742 *buf_size = pkey->num_values * pkey->item_size;
6743
6745
6746 }
6747#endif /* LOCAL_ROUTINES */
6748
6749 return DB_SUCCESS;
6750}
6751
6753#ifndef DOXYGEN_SHOULD_SKIP_THIS
6754
6755/*------------------------------------------------------------------*/
6756INT db_get_data1(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, DWORD type, INT * num_values)
6757/********************************************************************\
6758
6759 Routine: db_get_data1
6760
6761 Purpose: Get key data from a handle, return number of values
6762
6763 Input:
6764 HNDLE hDB Handle to the database
6765 HNDLE hKey Handle of key
6766 INT *buf_size Size of data buffer
6767 DWORD type Type of data
6768
6769 Output:
6770 void *data Key data
6771 INT *buf_size Size of key data
6772 INT *num_values Number of values
6773
6774 Function value:
6775 DB_SUCCESS Successful completion
6776 DB_INVALID_HANDLE Database handle is invalid
6777 DB_TRUNCATED Return buffer is smaller than key data
6778 DB_TYPE_MISMATCH Type mismatch
6779
6780\********************************************************************/
6781{
6782 if (rpc_is_remote())
6783 return rpc_call(RPC_DB_GET_DATA1, hDB, hKey, data, buf_size, type, num_values);
6784
6785#ifdef LOCAL_ROUTINES
6786 {
6787 DATABASE_HEADER *pheader;
6788 KEY *pkey;
6789
6790 if (hDB > _database_entries || hDB <= 0) {
6791 cm_msg(MERROR, "db_get_data", "Invalid database handle");
6792 return DB_INVALID_HANDLE;
6793 }
6794
6795 if (!_database[hDB - 1].attached) {
6796 cm_msg(MERROR, "db_get_data", "invalid database handle");
6797 return DB_INVALID_HANDLE;
6798 }
6799
6800 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6801 cm_msg(MERROR, "db_get_data", "invalid key handle");
6802 return DB_INVALID_HANDLE;
6803 }
6804
6806
6807 pheader = _database[hDB - 1].database_header;
6808
6809 /* check if hKey argument is correct */
6810 if (!db_validate_hkey(pheader, hKey)) {
6812 return DB_INVALID_HANDLE;
6813 }
6814
6815 pkey = (KEY *) ((char *) pheader + hKey);
6816
6817 /* check for read access */
6818 if (!(pkey->access_mode & MODE_READ)) {
6820 return DB_NO_ACCESS;
6821 }
6822
6823 if (!pkey->type) {
6824 int pkey_type = pkey->type;
6826 cm_msg(MERROR, "db_get_data", "hkey %d invalid key type %d", hKey, pkey_type);
6827 return DB_INVALID_HANDLE;
6828 }
6829
6830 if (pkey->type != type) {
6831 int pkey_type = pkey->type;
6832 char pkey_name[NAME_LENGTH];
6833 mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
6835 cm_msg(MERROR, "db_get_data", "\"%s\" is of type %s, not %s", pkey_name, rpc_tid_name(pkey_type), rpc_tid_name(type));
6836 return DB_TYPE_MISMATCH;
6837 }
6838
6839 /* keys cannot contain data */
6840 if (pkey->type == TID_KEY) {
6842 cm_msg(MERROR, "db_get_data", "Key cannot contain data");
6843 return DB_TYPE_MISMATCH;
6844 }
6845
6846 /* check if key has data */
6847 if (pkey->data == 0) {
6848 memset(data, 0, *buf_size);
6849 *buf_size = 0;
6851 return DB_SUCCESS;
6852 }
6853
6854 /* check if buffer is too small */
6855 if (pkey->num_values * pkey->item_size > *buf_size) {
6856 int pkey_size = pkey->num_values * pkey->item_size;
6857 memcpy(data, (char *) pheader + pkey->data, *buf_size);
6859 std::string path = db_get_path(hDB, hKey);
6860 cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
6861 return DB_TRUNCATED;
6862 }
6863
6864 /* copy key data */
6865 memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
6866 *buf_size = pkey->num_values * pkey->item_size;
6867 *num_values = pkey->num_values;
6868
6870
6871 }
6872#endif /* LOCAL_ROUTINES */
6873
6874 return DB_SUCCESS;
6875}
6876
6878#endif /* DOXYGEN_SHOULD_SKIP_THIS */
6879
6880/********************************************************************/
6894{
6895 if (rpc_is_remote())
6896 return rpc_call(RPC_DB_GET_DATA_INDEX, hDB, hKey, data, buf_size, idx, type);
6897
6898#ifdef LOCAL_ROUTINES
6899 {
6900 DATABASE_HEADER *pheader;
6901 KEY *pkey;
6902
6903 if (hDB > _database_entries || hDB <= 0) {
6904 cm_msg(MERROR, "db_get_data", "Invalid database handle");
6905 return DB_INVALID_HANDLE;
6906 }
6907
6908 if (!_database[hDB - 1].attached) {
6909 cm_msg(MERROR, "db_get_data", "invalid database handle");
6910 return DB_INVALID_HANDLE;
6911 }
6912
6913 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6914 cm_msg(MERROR, "db_get_data", "invalid key handle");
6915 return DB_INVALID_HANDLE;
6916 }
6917
6919
6920 pheader = _database[hDB - 1].database_header;
6921
6922 /* check if hKey argument is correct */
6923 if (!db_validate_hkey(pheader, hKey)) {
6925 return DB_INVALID_HANDLE;
6926 }
6927
6928 pkey = (KEY *) ((char *) pheader + hKey);
6929
6930 /* check for read access */
6931 if (!(pkey->access_mode & MODE_READ)) {
6933 return DB_NO_ACCESS;
6934 }
6935
6936 if (!pkey->type) {
6937 int pkey_type = pkey->type;
6939 cm_msg(MERROR, "db_get_data_index", "hkey %d invalid key type %d", hKey, pkey_type);
6940 return DB_INVALID_HANDLE;
6941 }
6942
6943 if (pkey->type != type) {
6944 int pkey_type = pkey->type;
6945 char pkey_name[NAME_LENGTH];
6946 mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
6948 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));
6949 return DB_TYPE_MISMATCH;
6950 }
6951
6952 /* keys cannot contain data */
6953 if (pkey->type == TID_KEY) {
6955 cm_msg(MERROR, "db_get_data_index", "Key cannot contain data");
6956 return DB_TYPE_MISMATCH;
6957 }
6958
6959 /* check if key has data */
6960 if (pkey->data == 0) {
6961 memset(data, 0, *buf_size);
6962 *buf_size = 0;
6964 return DB_SUCCESS;
6965 }
6966
6967 /* check if index in range */
6968 if (idx < 0 || idx >= pkey->num_values) {
6969 int pkey_num_values = pkey->num_values;
6970 memset(data, 0, *buf_size);
6972
6973 std::string path = db_get_path(hDB, hKey);
6974 cm_msg(MERROR, "db_get_data_index", "index (%d) exceeds array length (%d) for key \"%s\"", idx, pkey_num_values, path.c_str());
6975 return DB_OUT_OF_RANGE;
6976 }
6977
6978 /* check if buffer is too small */
6979 if (pkey->item_size > *buf_size) {
6980 int pkey_size = pkey->item_size;
6981 /* copy data */
6982 memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, *buf_size);
6984 std::string path = db_get_path(hDB, hKey);
6985 cm_msg(MERROR, "db_get_data_index", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
6986 return DB_TRUNCATED;
6987 }
6988
6989 /* copy key data */
6990 memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, pkey->item_size);
6991 *buf_size = pkey->item_size;
6992
6994
6995 }
6996#endif /* LOCAL_ROUTINES */
6997
6998 return DB_SUCCESS;
6999}
7000
7001#ifdef LOCAL_ROUTINES
7002/********************************************************************/
7014static 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)
7015{
7016 /* if no buf_size given (Java!), calculate it */
7017 if (data_size == 0)
7018 data_size = pkey->item_size * num_values;
7019
7020 /* resize data size if necessary */
7021 if (pkey->total_size != data_size) {
7022 // FIXME: validate pkey->data!
7023 pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, data_size, caller);
7024
7025 if (pkey->data == 0) {
7026 pkey->total_size = 0;
7027 db_msg(msg, MERROR, caller, "Cannot reallocate \"%s\" with new size %d bytes, online database full", db_get_path_locked(pheader, pkey).c_str(), data_size);
7028 return DB_FULL;
7029 }
7030
7031 pkey->data -= (POINTER_T) pheader;
7032 pkey->total_size = data_size;
7033 }
7034
7035 /* set number of values */
7036 pkey->num_values = num_values;
7037
7038 if (type == TID_STRING || type == TID_LINK)
7039 pkey->item_size = data_size / num_values;
7040 else
7041 pkey->item_size = rpc_tid_size(type);
7042
7043 if ((type == TID_STRING || type == TID_LINK) && pkey->num_values == 1) {
7044 /* copy string up to NUL termination */
7045 mstrlcpy((char *) pheader + pkey->data, (const char*)data, data_size);
7046 } else {
7047 /* copy data */
7048 memcpy((char *) pheader + pkey->data, data, data_size);
7049 }
7050
7051 /* update time */
7052 pkey->last_written = ss_time();
7053
7054 return DB_SUCCESS;
7055}
7056
7057static 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)
7058{
7059 /* increase key size if necessary */
7060 if (idx >= pkey->num_values || pkey->item_size == 0) {
7061 // FIXME: validate pkey->data
7062 pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, data_size * (idx + 1), caller);
7063
7064 if (pkey->data == 0) {
7065 pkey->total_size = 0;
7066 pkey->num_values = 0;
7067 db_msg(msg, MERROR, caller, "Cannot reallocate \"%s\" with new num_values %d and new size %d bytes, online database full", db_get_path_locked(pheader, pkey).c_str(), idx + 1, data_size * (idx + 1));
7068 return DB_FULL;
7069 }
7070
7071 pkey->data -= (POINTER_T) pheader;
7072 if (!pkey->item_size)
7073 pkey->item_size = data_size;
7074 pkey->total_size = data_size * (idx + 1);
7075 pkey->num_values = idx + 1;
7076 }
7077
7078#if 0
7079 /* cut strings which are too long */
7080 if ((type == TID_STRING || type == TID_LINK) && (int) strlen((char *) data) + 1 > pkey->item_size)
7081 *((char *) data + pkey->item_size - 1) = 0;
7082
7083 /* copy data */
7084 memcpy((char *) pheader + pkey->data + idx * pkey->item_size, data, pkey->item_size);
7085#endif
7086
7087 if ((type == TID_STRING || type == TID_LINK)) {
7088 /* cut strings which are too long */
7089 mstrlcpy((char *) pheader + pkey->data + idx * pkey->item_size, (char*)data, pkey->item_size);
7090 } else {
7091 /* copy data */
7092 memcpy((char *) pheader + pkey->data + idx * pkey->item_size, data, pkey->item_size);
7093 }
7094
7095 /* update time */
7096 pkey->last_written = ss_time();
7097
7098 return DB_SUCCESS;
7099}
7100
7101static 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)
7102{
7103 /* check for write access */
7104 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7105 return DB_NO_ACCESS;
7106 }
7107
7108 if (pkey->type != type) {
7109 db_msg(msg, MERROR, caller, "\"%s\" is of type %s, not %s", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(pkey->type), rpc_tid_name(type));
7110 return DB_TYPE_MISMATCH;
7111 }
7112
7113 /* keys cannot contain data */
7114 if (pkey->type == TID_KEY) {
7115 db_msg(msg, MERROR, caller, "\"%s\" of type TID_KEY cannot contain data", db_get_path_locked(pheader, pkey).c_str());
7116 return DB_TYPE_MISMATCH;
7117 }
7118
7119 if (type == TID_STRING || type == TID_LINK) {
7120 if (num_values > 1) {
7121 int item_size = pkey->item_size;
7122 if (data_size > 0 && num_values > 0)
7123 item_size = data_size/num_values;
7124 //printf("db_check_set_data for %s: utf8 check for odb \"%s\" string array size %d, item size %d\n", caller, db_get_path_locked(pheader, pkey).c_str(), num_values, item_size);
7125 for (int i=0; i<num_values; i++) {
7126 const char* value = ((const char*)data) + i * item_size;
7127 //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_locked(pheader, pkey).c_str(), num_values, item_size, i, value);
7128 if (!is_utf8(value)) {
7129 db_msg(msg, MERROR, caller, "\"%s\" index %d set to invalid UTF-8 Unicode string value \"%s\"", db_get_path_locked(pheader, pkey).c_str(), i, value);
7130 // just a warning for now. K.O.
7131 //return DB_TYPE_MISMATCH;
7132 }
7133 }
7134 } else {
7135 const char* value = (const char*)data;
7136 //printf("db_check_set_data for %s: utf8 check for odb \"%s\" value \"%s\" size %d\n", caller, db_get_path_locked(pheader, pkey).c_str(), value, data_size);
7137 if (!is_utf8(value)) {
7138 db_msg(msg, MERROR, caller, "\"%s\" set to invalid UTF-8 Unicode string value \"%s\"", db_get_path_locked(pheader, pkey).c_str(), value);
7139 // just a warning for now. K.O.
7140 //return DB_TYPE_MISMATCH;
7141 }
7142 }
7143 }
7144
7145 return DB_SUCCESS;
7146}
7147
7148static 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)
7149{
7150 /* check for write access */
7151 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7152 return DB_NO_ACCESS;
7153 }
7154
7155 if (pkey->type != type) {
7156 db_msg(msg, MERROR, caller, "\"%s\" is of type %s, not %s", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(pkey->type), rpc_tid_name(type));
7157 return DB_TYPE_MISMATCH;
7158 }
7159
7160 /* keys cannot contain data */
7161 if (pkey->type == TID_KEY) {
7162 db_msg(msg, MERROR, "db_set_data_index", "\"%s\" of type TID_KEY cannot contain data", db_get_path_locked(pheader, pkey).c_str());
7163 return DB_TYPE_MISMATCH;
7164 }
7165
7166 /* check utf-8 encoding */
7167 if (pkey->type == TID_STRING || pkey->type == TID_LINK) {
7168 //printf("db_check_set_data_index for %s: utf8 check for odb \"%s\" value \"%s\"\n", caller, db_get_path_locked(pheader, pkey).c_str(), data);
7169 const char* value = (const char*)data;
7170 if (!is_utf8(value)) {
7171 db_msg(msg, MERROR, "db_set_data_index", "\"%s\" index %d set to invalid UTF-8 Unicode string value \"%s\"", db_get_path_locked(pheader, pkey).c_str(), idx, value);
7172 // just a warning for now. K.O.
7173 //return DB_TYPE_MISMATCH;
7174 }
7175 }
7176
7177 /* check for valid idx */
7178 if (idx < 0) {
7179 db_msg(msg, MERROR, caller, "\%s\" given invalid index %d", db_get_path_locked(pheader, pkey).c_str(), idx);
7180 return DB_INVALID_PARAM;
7181 }
7182
7183 /* check for valid array element size: if new element size
7184 is different from existing size, ODB becomes corrupted */
7185 if (pkey->item_size != 0 && data_size != pkey->item_size) {
7186 db_msg(msg, MERROR, caller, "\"%s\" invalid element data size %d, expected %d", db_get_path_locked(pheader, pkey).c_str(), data_size, pkey->item_size);
7187 return DB_TYPE_MISMATCH;
7188 }
7189
7190 return DB_SUCCESS;
7191}
7192
7193#endif // LOCAL_ROUTINES
7194
7195/********************************************************************/
7215INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7216{
7217 if (rpc_is_remote())
7218 return rpc_call(RPC_DB_SET_DATA, hDB, hKey, data, buf_size, num_values, type);
7219
7220#ifdef LOCAL_ROUTINES
7221 {
7222 DATABASE_HEADER *pheader;
7223 KEY *pkey;
7225 int link_idx;
7226 char link_name[256];
7227 int status;
7228
7229 if (hDB > _database_entries || hDB <= 0) {
7230 cm_msg(MERROR, "db_set_data", "invalid database handle");
7231 return DB_INVALID_HANDLE;
7232 }
7233
7234 if (!_database[hDB - 1].attached) {
7235 cm_msg(MERROR, "db_set_data", "invalid database handle");
7236 return DB_INVALID_HANDLE;
7237 }
7238
7239 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7240 cm_msg(MERROR, "db_set_data", "invalid key handle");
7241 return DB_INVALID_HANDLE;
7242 }
7243
7244 if (num_values == 0)
7245 return DB_INVALID_PARAM;
7246
7248 db_err_msg* msg = NULL;
7249
7250 pheader = _database[hDB - 1].database_header;
7251
7252 /* check if hKey argument is correct */
7253 if (!db_validate_hkey(pheader, hKey)) {
7255 return DB_INVALID_HANDLE;
7256 }
7257
7258 pkey = (KEY *) ((char *) pheader + hKey);
7259
7260 /* check for write access */
7261 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7263 return DB_NO_ACCESS;
7264 }
7265
7266 /* check for link to array index */
7267 if (pkey->type == TID_LINK) {
7268 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7269 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7271 if (strchr(link_name, '[') == NULL)
7272 return DB_INVALID_LINK;
7273 link_idx = atoi(strchr(link_name, '[') + 1);
7274 *strchr(link_name, '[') = 0;
7276 return DB_INVALID_LINK;
7277 return db_set_data_index(hDB, hkeylink, data, buf_size, link_idx, type);
7278 }
7279 }
7280
7281 status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_data", &msg);
7282
7283 if (status != DB_SUCCESS) {
7285 if (msg)
7286 db_flush_msg(&msg);
7287 return status;
7288 }
7289
7290 db_allow_write_locked(&_database[hDB-1], "db_set_data");
7291
7292 status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_data", &msg);
7293
7294 if (status != DB_SUCCESS) {
7296 if (msg)
7297 db_flush_msg(&msg);
7298 return status;
7299 }
7300
7301 db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
7303 if (msg)
7304 db_flush_msg(&msg);
7305
7306
7307 }
7308#endif /* LOCAL_ROUTINES */
7309
7310 return DB_SUCCESS;
7311}
7312
7313INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7314/*
7315
7316 Same as db_set_data(), but do not notify hot-linked clients
7317
7318 */
7319{
7320 if (rpc_is_remote())
7321 return rpc_call(RPC_DB_SET_DATA1, hDB, hKey, data, buf_size, num_values, type);
7322
7323#ifdef LOCAL_ROUTINES
7324 {
7325 DATABASE_HEADER *pheader;
7326 KEY *pkey;
7328 int link_idx;
7329 char link_name[256];
7330 int status;
7331
7332 if (hDB > _database_entries || hDB <= 0) {
7333 cm_msg(MERROR, "db_set_data1", "invalid database handle");
7334 return DB_INVALID_HANDLE;
7335 }
7336
7337 if (!_database[hDB - 1].attached) {
7338 cm_msg(MERROR, "db_set_data1", "invalid database handle");
7339 return DB_INVALID_HANDLE;
7340 }
7341
7342 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7343 cm_msg(MERROR, "db_set_data1", "invalid key handle");
7344 return DB_INVALID_HANDLE;
7345 }
7346
7347 if (num_values == 0)
7348 return DB_INVALID_PARAM;
7349
7351 db_err_msg* msg = NULL;
7352
7353 pheader = _database[hDB - 1].database_header;
7354
7355 /* check if hKey argument is correct */
7356 if (!db_validate_hkey(pheader, hKey)) {
7358 return DB_INVALID_HANDLE;
7359 }
7360
7361 pkey = (KEY *) ((char *) pheader + hKey);
7362
7363 /* check for write access */
7364 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7366 return DB_NO_ACCESS;
7367 }
7368
7369 /* check for link to array index */
7370 if (pkey->type == TID_LINK) {
7371 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7372 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7374 if (strchr(link_name, '[') == NULL)
7375 return DB_INVALID_LINK;
7376 link_idx = atoi(strchr(link_name, '[') + 1);
7377 *strchr(link_name, '[') = 0;
7379 return DB_INVALID_LINK;
7380 return db_set_data_index1(hDB, hkeylink, data, buf_size, link_idx, type, FALSE);
7381 }
7382 }
7383
7384 status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_data1", &msg);
7385
7386 if (status != DB_SUCCESS) {
7388 if (msg)
7389 db_flush_msg(&msg);
7390 return status;
7391 }
7392
7393 db_allow_write_locked(&_database[hDB - 1], "db_set_data1");
7394
7395 status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_data1", &msg);
7396
7397 if (status != DB_SUCCESS) {
7399 if (msg)
7400 db_flush_msg(&msg);
7401 return status;
7402 }
7403
7405 if (msg)
7406 db_flush_msg(&msg);
7407
7408 }
7409#endif /* LOCAL_ROUTINES */
7410
7411 return DB_SUCCESS;
7412}
7413
7414/********************************************************************/
7425INT db_set_link_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7426{
7427 if (rpc_is_remote())
7428 return rpc_call(RPC_DB_SET_LINK_DATA, hDB, hKey, data, buf_size, num_values, type);
7429
7430#ifdef LOCAL_ROUTINES
7431 {
7432 DATABASE_HEADER *pheader;
7433 KEY *pkey;
7434 int status;
7435
7436 if (hDB > _database_entries || hDB <= 0) {
7437 cm_msg(MERROR, "db_set_data", "invalid database handle");
7438 return DB_INVALID_HANDLE;
7439 }
7440
7441 if (!_database[hDB - 1].attached) {
7442 cm_msg(MERROR, "db_set_data", "invalid database handle");
7443 return DB_INVALID_HANDLE;
7444 }
7445
7446 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7447 cm_msg(MERROR, "db_set_data", "invalid key handle");
7448 return DB_INVALID_HANDLE;
7449 }
7450
7451 if (num_values == 0)
7452 return DB_INVALID_PARAM;
7453
7455 db_err_msg* msg = NULL;
7456
7457 pheader = _database[hDB - 1].database_header;
7458
7459 /* check if hKey argument is correct */
7460 if (!db_validate_hkey(pheader, hKey)) {
7462 return DB_INVALID_HANDLE;
7463 }
7464
7465 pkey = (KEY *) ((char *) pheader + hKey);
7466
7467 status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_link_data", &msg);
7468
7469 if (status != DB_SUCCESS) {
7471 if (msg)
7472 db_flush_msg(&msg);
7473 return status;
7474 }
7475
7476 db_allow_write_locked(&_database[hDB - 1], "db_set_link_data");
7477
7478 status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_link_data", &msg);
7479
7480 if (status != DB_SUCCESS) {
7482 if (msg)
7483 db_flush_msg(&msg);
7484 return status;
7485 }
7486
7487 db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
7489 if (msg)
7490 db_flush_msg(&msg);
7491
7492 }
7493#endif /* LOCAL_ROUTINES */
7494
7495 return DB_SUCCESS;
7496}
7497
7499#ifndef DOXYGEN_SHOULD_SKIP_THIS
7500
7501/*------------------------------------------------------------------*/
7503/********************************************************************\
7504
7505 Routine: db_set_num_values
7506
7507 Purpose: Set number of values in a key. Extend with zeros or truncate.
7508
7509 Input:
7510 HNDLE hDB Handle to the database
7511 HNDLE hKey Handle of key
7512 INT num_values Number of data values
7513
7514 Output:
7515 none
7516
7517 Function value:
7518 DB_SUCCESS Successful completion
7519 DB_INVALID_HANDLE Database handle is invalid
7520
7521\********************************************************************/
7522{
7523 if (rpc_is_remote())
7524 return rpc_call(RPC_DB_SET_NUM_VALUES, hDB, hKey, num_values);
7525
7526#ifdef LOCAL_ROUTINES
7527 {
7528 DATABASE_HEADER *pheader;
7529 KEY *pkey;
7530 INT new_size;
7531
7532 if (hDB > _database_entries || hDB <= 0) {
7533 cm_msg(MERROR, "db_set_num_values", "invalid database handle");
7534 return DB_INVALID_HANDLE;
7535 }
7536
7537 if (!_database[hDB - 1].attached) {
7538 cm_msg(MERROR, "db_set_num_values", "invalid database handle");
7539 return DB_INVALID_HANDLE;
7540 }
7541
7542 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7543 cm_msg(MERROR, "db_set_num_values", "invalid key handle");
7544 return DB_INVALID_HANDLE;
7545 }
7546
7547 if (num_values <= 0) {
7548 cm_msg(MERROR, "db_set_num_values", "invalid num_values %d", num_values);
7549 return DB_INVALID_PARAM;
7550 }
7551
7552 if (num_values == 0)
7553 return DB_INVALID_PARAM;
7554
7556 db_err_msg* msg = NULL;
7557
7558 pheader = _database[hDB - 1].database_header;
7559
7560 /* check if hKey argument is correct */
7561 if (!db_validate_hkey(pheader, hKey)) {
7563 return DB_INVALID_HANDLE;
7564 }
7565
7566 pkey = (KEY *) ((char *) pheader + hKey);
7567
7568 /* check for write access */
7569 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7571 return DB_NO_ACCESS;
7572 }
7573
7574 /* keys cannot contain data */
7575 if (pkey->type == TID_KEY) {
7577 cm_msg(MERROR, "db_set_num_values", "Key cannot contain data");
7578 return DB_TYPE_MISMATCH;
7579 }
7580
7581 if (pkey->total_size != pkey->item_size * pkey->num_values) {
7583 cm_msg(MERROR, "db_set_num_values", "Corrupted key");
7584 return DB_CORRUPTED;
7585 }
7586
7587 if (pkey->item_size == 0) {
7589 cm_msg(MERROR, "db_set_num_values", "Cannot resize array with item_size equal to zero");
7590 return DB_INVALID_PARAM;
7591 }
7592
7593 db_allow_write_locked(&_database[hDB - 1], "db_set_num_values");
7594
7595 /* resize data size if necessary */
7596 if (pkey->num_values != num_values) {
7597 new_size = pkey->item_size * num_values;
7598
7599 pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, new_size, "db_set_num_values");
7600
7601 if (pkey->data == 0) {
7602 pkey->total_size = 0;
7603 pkey->num_values = 0;
7604 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_locked(pheader, pkey).c_str(), num_values, new_size);
7606 if (msg)
7607 db_flush_msg(&msg);
7608 return DB_FULL;
7609 }
7610
7611 pkey->data -= (POINTER_T) pheader;
7612 pkey->total_size = new_size;
7613 pkey->num_values = num_values;
7614 }
7615
7616 /* update time */
7617 pkey->last_written = ss_time();
7618
7619 db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
7621 if (msg)
7622 db_flush_msg(&msg);
7623
7624 }
7625#endif /* LOCAL_ROUTINES */
7626
7627 return DB_SUCCESS;
7628}
7629
7631#endif /* DOXYGEN_SHOULD_SKIP_THIS */
7632
7633/********************************************************************/
7649{
7650 if (rpc_is_remote())
7651 return rpc_call(RPC_DB_SET_DATA_INDEX, hDB, hKey, data, data_size, idx, type);
7652
7653#ifdef LOCAL_ROUTINES
7654 {
7655 DATABASE_HEADER *pheader;
7656 KEY *pkey;
7657 char link_name[256];
7658 int link_idx;
7660 int status;
7661
7662 if (hDB > _database_entries || hDB <= 0) {
7663 cm_msg(MERROR, "db_set_data_index", "invalid database handle");
7664 return DB_INVALID_HANDLE;
7665 }
7666
7667 if (!_database[hDB - 1].attached) {
7668 cm_msg(MERROR, "db_set_data_index", "invalid database handle");
7669 return DB_INVALID_HANDLE;
7670 }
7671
7672 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7673 cm_msg(MERROR, "db_set_data_index", "invalid key handle");
7674 return DB_INVALID_HANDLE;
7675 }
7676
7678 db_err_msg* msg = NULL;
7679
7680 pheader = _database[hDB - 1].database_header;
7681
7682 /* check if hKey argument is correct */
7683 if (!db_validate_hkey(pheader, hKey)) {
7685 return DB_INVALID_HANDLE;
7686 }
7687
7688 pkey = (KEY *) ((char *) pheader + hKey);
7689
7690 /* check for write access */
7691 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7693 return DB_NO_ACCESS;
7694 }
7695
7696 /* check for link to array index */
7697 if (pkey->type == TID_LINK) {
7698 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7699 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7701 if (strchr(link_name, '[') == NULL)
7702 return DB_INVALID_LINK;
7703 link_idx = atoi(strchr(link_name, '[') + 1);
7704 *strchr(link_name, '[') = 0;
7706 return DB_INVALID_LINK;
7707 return db_set_data_index(hDB, hkeylink, data, data_size, link_idx, type);
7708 }
7709 }
7710
7711 status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_data_index", &msg);
7712
7713 if (status != DB_SUCCESS) {
7715 if (msg)
7716 db_flush_msg(&msg);
7717 return status;
7718 }
7719
7720 db_allow_write_locked(&_database[hDB-1], "db_set_data_index");
7721
7722 status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_data_index", &msg);
7723
7724 if (status != DB_SUCCESS) {
7726 if (msg)
7727 db_flush_msg(&msg);
7728 return status;
7729 }
7730
7731 db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
7733 if (msg)
7734 db_flush_msg(&msg);
7735
7736 }
7737#endif /* LOCAL_ROUTINES */
7738
7739 return DB_SUCCESS;
7740}
7741
7742/********************************************************************/
7755{
7756 if (rpc_is_remote())
7757 return rpc_call(RPC_DB_SET_LINK_DATA_INDEX, hDB, hKey, data, data_size, idx, type);
7758
7759#ifdef LOCAL_ROUTINES
7760 {
7761 DATABASE_HEADER *pheader;
7762 KEY *pkey;
7763 int status;
7764
7765 if (hDB > _database_entries || hDB <= 0) {
7766 cm_msg(MERROR, "db_set_link_data_index", "invalid database handle");
7767 return DB_INVALID_HANDLE;
7768 }
7769
7770 if (!_database[hDB - 1].attached) {
7771 cm_msg(MERROR, "db_set_link_data_index", "invalid database handle");
7772 return DB_INVALID_HANDLE;
7773 }
7774
7775 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7776 cm_msg(MERROR, "db_set_link_data_index", "invalid key handle");
7777 return DB_INVALID_HANDLE;
7778 }
7779
7781 db_err_msg* msg = NULL;
7782
7783 pheader = _database[hDB - 1].database_header;
7784
7785 /* check if hKey argument is correct */
7786 if (!db_validate_hkey(pheader, hKey)) {
7788 return DB_INVALID_HANDLE;
7789 }
7790
7791 pkey = (KEY *) ((char *) pheader + hKey);
7792
7793 status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_link_data_index", &msg);
7794
7795 if (status != DB_SUCCESS) {
7797 if (msg)
7798 db_flush_msg(&msg);
7799 return status;
7800 }
7801
7802 db_allow_write_locked(&_database[hDB - 1], "db_set_link_data_index");
7803
7804 status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_link_data_index", &msg);
7805
7806 if (status != DB_SUCCESS) {
7808 if (msg)
7809 db_flush_msg(&msg);
7810 return status;
7811 }
7812
7813 db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
7815 if (msg)
7816 db_flush_msg(&msg);
7817
7818 }
7819#endif /* LOCAL_ROUTINES */
7820
7821 return DB_SUCCESS;
7822}
7823
7825#ifndef DOXYGEN_SHOULD_SKIP_THIS
7826
7827/*------------------------------------------------------------------*/
7829/********************************************************************\
7830
7831 Routine: db_set_data_index1
7832
7833 Purpose: Set key data for a key which contains an array of values.
7834 Optionally notify clients which have key open.
7835
7836 Input:
7837 HNDLE hDB Handle to the database
7838 HNDLE hKey Handle of key to enumerate
7839 void *data Pointer to single value of data
7840 INT data_size Size of single data element
7841 INT idx Index of array to change [0..n-1]
7842 DWORD type Type of data
7843 BOOL bNotify If TRUE, notify clients
7844
7845 Output:
7846 none
7847
7848 Function value:
7849 DB_SUCCESS Successful completion
7850 DB_INVALID_HANDLE Database handle is invalid
7851 DB_TYPE_MISMATCH Key was created with different type
7852 DB_NO_ACCESS No write access
7853
7854\********************************************************************/
7855{
7856 if (rpc_is_remote())
7857 return rpc_call(RPC_DB_SET_DATA_INDEX1, hDB, hKey, data, data_size, idx, type, bNotify);
7858
7859#ifdef LOCAL_ROUTINES
7860 {
7861 DATABASE_HEADER *pheader;
7862 KEY *pkey;
7863 int status;
7864
7865 if (hDB > _database_entries || hDB <= 0) {
7866 cm_msg(MERROR, "db_set_data_index1", "invalid database handle");
7867 return DB_INVALID_HANDLE;
7868 }
7869
7870 if (!_database[hDB - 1].attached) {
7871 cm_msg(MERROR, "db_set_data_index1", "invalid database handle");
7872 return DB_INVALID_HANDLE;
7873 }
7874
7875 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7876 cm_msg(MERROR, "db_set_data_index1", "invalid key handle");
7877 return DB_INVALID_HANDLE;
7878 }
7879
7881 db_err_msg* msg = NULL;
7882
7883 pheader = _database[hDB - 1].database_header;
7884
7885 /* check if hKey argument is correct */
7886 if (!db_validate_hkey(pheader, hKey)) {
7888 return DB_INVALID_HANDLE;
7889 }
7890
7891 pkey = (KEY *) ((char *) pheader + hKey);
7892
7893 status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_data_index1", &msg);
7894
7895 if (status != DB_SUCCESS) {
7897 if (msg)
7898 db_flush_msg(&msg);
7899 return status;
7900 }
7901
7902 db_allow_write_locked(&_database[hDB - 1], "db_set_data_index1");
7903
7904 status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_data_index1", &msg);
7905
7906 if (status != DB_SUCCESS) {
7908 if (msg)
7909 db_flush_msg(&msg);
7910 return status;
7911 }
7912
7913 if (bNotify)
7914 db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
7915
7917 if (msg)
7918 db_flush_msg(&msg);
7919
7920 }
7921#endif /* LOCAL_ROUTINES */
7922
7923 return DB_SUCCESS;
7924}
7925
7926/*----------------------------------------------------------------------------*/
7927
7928INT db_merge_data(HNDLE hDB, HNDLE hKeyRoot, const char *name, void *data, INT data_size, INT num_values, INT type)
7929/********************************************************************\
7930
7931 Routine: db_merge_data
7932
7933 Purpose: Merge an array with an ODB array. If the ODB array doesn't
7934 exist, create it and fill it with the array. If it exists,
7935 load it in the array. Adjust ODB array size if necessary.
7936
7937 Input:
7938 HNDLE hDB Handle to the database
7939 HNDLE hKeyRoot Key handle to start with, 0 for root
7940 cha *name Key name relative to hKeyRoot
7941 void *data Pointer to data array
7942 INT data_size Size of data array
7943 INT num_values Number of values in array
7944 DWORD type Type of data
7945
7946 Output:
7947 none
7948
7949 Function value:
7950 <same as db_set_data>
7951
7952\********************************************************************/
7953{
7954 HNDLE hKey;
7956
7957 if (num_values == 0)
7958 return DB_INVALID_PARAM;
7959
7961 if (status != DB_SUCCESS) {
7964 if (status != DB_SUCCESS)
7965 return status;
7966 status = db_set_data(hDB, hKey, data, data_size, num_values, type);
7967 } else {
7968 old_size = data_size;
7970 status = db_set_data(hDB, hKey, data, data_size, num_values, type);
7971 }
7972
7973 return status;
7974}
7975
7976#ifdef LOCAL_ROUTINES
7977
7978/*------------------------------------------------------------------*/
7980/********************************************************************\
7981
7982 Routine: db_set_mode_wlocked()
7983
7984 Purpose: Set access mode of key
7985
7986 Input:
7987 pheader Database
7988 pkey Key
7989 DWORD mode Access mode, any or'ed combination of
7990 MODE_READ, MODE_WRITE, MODE_EXCLUSIVE
7991 and MODE_DELETE
7992 recurse Value of 0: do not recurse subtree,
7993 value of 1: recurse subtree, becomes recurse level
7994
7995 Function value:
7996 DB_SUCCESS Successful completion
7997
7998\********************************************************************/
7999{
8000 /* resolve links */
8001 if (pkey->type == TID_LINK) {
8002 int status;
8003 pkey = (KEY*)db_resolve_link_locked(pheader, pkey, &status, msg);
8004 if (!pkey) {
8005 return status;
8006 }
8007 }
8008
8009 if (pkey->type == TID_KEY && recurse) {
8010 // drop "const" from KEY* we are permitted to write to ODB!
8011 KEY* psubkey = (KEY*)db_enum_first_locked(pheader, pkey, msg);
8012 while (psubkey) {
8013 db_set_mode_wlocked(pheader, psubkey, mode, recurse+1, msg);
8014 psubkey = (KEY*)db_enum_next_locked(pheader, pkey, psubkey, msg);
8015 }
8016 }
8017
8018 /* now set mode */
8020
8021 return DB_SUCCESS;
8022}
8023
8024#endif
8025
8026/*------------------------------------------------------------------*/
8028/********************************************************************\
8029
8030 Routine: db_set_mode
8031
8032 Purpose: Set access mode of key
8033
8034 Input:
8035 HNDLE hDB Handle to the database
8036 HNDLE hKey Key handle
8037 DWORD mode Access mode, any or'ed combination of
8038 MODE_READ, MODE_WRITE, MODE_EXCLUSIVE
8039 and MODE_DELETE
8040 BOOL recurse Value of 0 (FALSE): do not recurse subtree,
8041 value of 1 (TRUE): recurse subtree,
8042 value of 2: recurse subtree, assume database is locked by caller.
8043
8044 Output:
8045 none
8046
8047 Function value:
8048 DB_SUCCESS Successful completion
8049 DB_INVALID_HANDLE Database handle is invalid
8050
8051\********************************************************************/
8052{
8053 if (rpc_is_remote())
8055
8056#ifdef LOCAL_ROUTINES
8057 {
8058 DATABASE_HEADER *pheader;
8059 BOOL locked = FALSE;
8060
8061 if (hDB > _database_entries || hDB <= 0) {
8062 cm_msg(MERROR, "db_set_mode", "invalid database handle");
8063 return DB_INVALID_HANDLE;
8064 }
8065
8066 if (!_database[hDB - 1].attached) {
8067 cm_msg(MERROR, "db_set_mode", "invalid database handle");
8068 return DB_INVALID_HANDLE;
8069 }
8070
8071 if (recurse < 2) {
8073 locked = TRUE;
8074 }
8075
8076 pheader = _database[hDB - 1].database_header;
8077
8078 db_err_msg* msg = NULL;
8079 int status = 0;
8080
8081 KEY *pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_set_mode", &msg);
8082
8083 if (!pkey) {
8084 if (locked) {
8086 if (msg)
8087 db_flush_msg(&msg);
8088 return status;
8089 }
8090 }
8091
8092 db_allow_write_locked(&_database[hDB-1], "db_set_mode");
8093
8094 status = db_set_mode_wlocked(pheader, pkey, mode, recurse, &msg);
8095
8096 if (locked) {
8098 if (msg)
8099 db_flush_msg(&msg);
8100 }
8101
8102 return status;
8103 }
8104#endif /* LOCAL_ROUTINES */
8105
8106 return DB_SUCCESS;
8107}
8108
8110#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8111
8112/********************************************************************/
8126INT db_load(HNDLE hDB, HNDLE hKeyRoot, const char *filename, BOOL bRemote)
8127{
8128 struct stat stat_buf;
8129 INT hfile, size, n, i, status;
8130 char *buffer;
8131
8132 if (rpc_is_remote() && bRemote)
8133 return rpc_call(RPC_DB_LOAD, hDB, hKeyRoot, filename);
8134
8135 /* open file */
8136 hfile = open(filename, O_RDONLY | O_TEXT, 0644);
8137 if (hfile == -1) {
8138 cm_msg(MERROR, "db_load", "file \"%s\" not found", filename);
8139 return DB_FILE_ERROR;
8140 }
8141
8142 /* allocate buffer with file size */
8143 fstat(hfile, &stat_buf);
8144 size = stat_buf.st_size;
8145 buffer = (char *) malloc(size + 1);
8146
8147 if (buffer == NULL) {
8148 cm_msg(MERROR, "db_load", "cannot allocate ODB load buffer");
8149 close(hfile);
8150 return DB_NO_MEMORY;
8151 }
8152
8153 n = 0;
8154
8155 do {
8156 i = read(hfile, buffer + n, size - n);
8157 if (i <= 0)
8158 break;
8159 n += i;
8160 } while (TRUE);
8161
8162 buffer[n] = 0;
8163
8164 if (strncmp(buffer, "<?xml version=\"1.0\"", 19) == 0) {
8165 status = db_paste_xml(hDB, hKeyRoot, buffer);
8166 if (status != DB_SUCCESS)
8167 printf("Error in file \"%s\"\n", filename);
8168 } else if( buffer[0] == '{'){
8169 if(strrchr(buffer, '}')){
8170 status = db_paste_json(hDB, hKeyRoot, buffer);
8171 } else {
8173 }
8174 } else
8175 status = db_paste(hDB, hKeyRoot, buffer);
8176
8177 close(hfile);
8178 free(buffer);
8179
8180 return status;
8181}
8182
8183/********************************************************************/
8217INT db_copy(HNDLE hDB, HNDLE hKey, char *buffer, INT * buffer_size, const char *path)
8218{
8219 INT i, j, size, status;
8220 KEY key;
8221 HNDLE hSubkey;
8222 char full_path[MAX_ODB_PATH];
8223 char *data;
8224 char line[MAX_STRING_LENGTH * 2];
8225 BOOL bWritten;
8226
8227 mstrlcpy(full_path, path, sizeof(full_path));
8228
8229 bWritten = FALSE;
8230
8231 /* first enumerate this level */
8232 for (i = 0;; i++) {
8234
8235 if (i == 0 && !hSubkey) {
8236 /* If key has no subkeys, just write this key */
8238 if (status != DB_SUCCESS)
8239 continue;
8240 size = key.total_size;
8241 data = (char *) malloc(size);
8242 if (data == NULL) {
8243 cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
8244 return DB_NO_MEMORY;
8245 }
8246 line[0] = 0;
8247
8248 if (key.type != TID_KEY) {
8250 if (status != DB_SUCCESS)
8251 continue;
8252 if (key.num_values == 1) {
8253 sprintf(line, "%s = %s : ", key.name, rpc_tid_name(key.type));
8254
8255 if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
8256 /* multiline string */
8257 sprintf(line + strlen(line), "[====#$@$#====]\n");
8258
8259 /* copy line to buffer */
8260 if ((INT) (strlen(line) + 1) > *buffer_size) {
8261 free(data);
8262 return DB_TRUNCATED;
8263 }
8264
8265 strcpy(buffer, line);
8266 buffer += strlen(line);
8267 *buffer_size -= strlen(line);
8268
8269 /* copy multiple lines to buffer */
8270 if (key.item_size > *buffer_size) {
8271 free(data);
8272 return DB_TRUNCATED;
8273 }
8274
8275 strcpy(buffer, data);
8276 buffer += strlen(data);
8277 *buffer_size -= strlen(data);
8278
8279 strcpy(line, "\n====#$@$#====\n");
8280 } else {
8281 std::string str = db_sprintf(data, key.item_size, 0, key.type);
8282
8283 if (key.type == TID_STRING || key.type == TID_LINK)
8284 sprintf(line + strlen(line), "[%d] ", key.item_size);
8285
8286 sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8287 }
8288 } else {
8289 sprintf(line, "%s = %s[%d] :\n", key.name, rpc_tid_name(key.type), key.num_values);
8290
8291 for (j = 0; j < key.num_values; j++) {
8292 if (key.type == TID_STRING || key.type == TID_LINK)
8293 sprintf(line + strlen(line), "[%d] ", key.item_size);
8294 else
8295 sprintf(line + strlen(line), "[%d] ", j);
8296
8297 std::string str = db_sprintf(data, key.item_size, j, key.type);
8298 sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8299
8300 /* copy line to buffer */
8301 if ((INT) (strlen(line) + 1) > *buffer_size) {
8302 free(data);
8303 return DB_TRUNCATED;
8304 }
8305
8306 strcpy(buffer, line);
8307 buffer += strlen(line);
8308 *buffer_size -= strlen(line);
8309 line[0] = 0;
8310 }
8311 }
8312 }
8313
8314 /* copy line to buffer */
8315 if ((INT) (strlen(line) + 1) > *buffer_size) {
8316 free(data);
8317 return DB_TRUNCATED;
8318 }
8319
8320 strcpy(buffer, line);
8321 buffer += strlen(line);
8322 *buffer_size -= strlen(line);
8323
8324 free(data);
8325 data = NULL;
8326 }
8327
8328 if (!hSubkey)
8329 break;
8330
8332 if (status != DB_SUCCESS)
8333 continue;
8334
8335 if (strcmp(key.name, "arr2") == 0)
8336 printf("\narr2\n");
8337 size = key.total_size;
8338 data = (char *) malloc(size);
8339 if (data == NULL) {
8340 cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
8341 return DB_NO_MEMORY;
8342 }
8343
8344 line[0] = 0;
8345
8346 if (key.type == TID_KEY) {
8347 char str[MAX_ODB_PATH];
8348
8349 /* new line */
8350 if (bWritten) {
8351 if (*buffer_size < 2) {
8352 free(data);
8353 return DB_TRUNCATED;
8354 }
8355
8356 strcpy(buffer, "\n");
8357 buffer += 1;
8358 *buffer_size -= 1;
8359 }
8360
8361 strcpy(str, full_path);
8362 if (str[0] && str[strlen(str) - 1] != '/')
8363 strcat(str, "/");
8364 strcat(str, key.name);
8365
8366 /* recurse */
8367 status = db_copy(hDB, hSubkey, buffer, buffer_size, str);
8368 if (status != DB_SUCCESS) {
8369 free(data);
8370 return status;
8371 }
8372
8373 buffer += strlen(buffer);
8374 bWritten = FALSE;
8375 } else {
8377 if (status != DB_SUCCESS)
8378 continue;
8379
8380 if (!bWritten) {
8381 if (path[0] == 0)
8382 sprintf(line, "[.]\n");
8383 else
8384 sprintf(line, "[%s]\n", path);
8385 bWritten = TRUE;
8386 }
8387
8388 if (key.num_values == 1) {
8389 sprintf(line + strlen(line), "%s = %s : ", key.name, rpc_tid_name(key.type));
8390
8391 if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
8392 /* multiline string */
8393 sprintf(line + strlen(line), "[====#$@$#====]\n");
8394
8395 /* ensure string limiter */
8396 data[size - 1] = 0;
8397
8398 /* copy line to buffer */
8399 if ((INT) (strlen(line) + 1) > *buffer_size) {
8400 free(data);
8401 return DB_TRUNCATED;
8402 }
8403
8404 strcpy(buffer, line);
8405 buffer += strlen(line);
8406 *buffer_size -= strlen(line);
8407
8408 /* copy multiple lines to buffer */
8409 if (key.item_size > *buffer_size) {
8410 free(data);
8411 return DB_TRUNCATED;
8412 }
8413
8414 strcpy(buffer, data);
8415 buffer += strlen(data);
8416 *buffer_size -= strlen(data);
8417
8418 strcpy(line, "\n====#$@$#====\n");
8419 } else {
8420 std::string str = db_sprintf(data, key.item_size, 0, key.type);
8421
8422 if (key.type == TID_STRING || key.type == TID_LINK)
8423 sprintf(line + strlen(line), "[%d] ", key.item_size);
8424
8425 sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8426 }
8427 } else {
8428 sprintf(line + strlen(line), "%s = %s[%d] :\n", key.name, rpc_tid_name(key.type), key.num_values);
8429
8430 for (j = 0; j < key.num_values; j++) {
8431 if (key.type == TID_STRING || key.type == TID_LINK)
8432 sprintf(line + strlen(line), "[%d] ", key.item_size);
8433 else
8434 sprintf(line + strlen(line), "[%d] ", j);
8435
8436 std::string str = db_sprintf(data, key.item_size, j, key.type);
8437 sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8438
8439 /* copy line to buffer */
8440 if ((INT) (strlen(line) + 1) > *buffer_size) {
8441 free(data);
8442 return DB_TRUNCATED;
8443 }
8444
8445 strcpy(buffer, line);
8446 buffer += strlen(line);
8447 *buffer_size -= strlen(line);
8448 line[0] = 0;
8449 }
8450 }
8451
8452 /* copy line to buffer */
8453 if ((INT) (strlen(line) + 1) > *buffer_size) {
8454 free(data);
8455 return DB_TRUNCATED;
8456 }
8457
8458 strcpy(buffer, line);
8459 buffer += strlen(line);
8460 *buffer_size -= strlen(line);
8461 }
8462
8463 free(data);
8464 data = NULL;
8465 }
8466
8467 if (bWritten) {
8468 if (*buffer_size < 2)
8469 return DB_TRUNCATED;
8470
8471 strcpy(buffer, "\n");
8472 buffer += 1;
8473 *buffer_size -= 1;
8474 }
8475
8476 return DB_SUCCESS;
8477}
8478
8479/********************************************************************/
8487INT db_paste(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
8488{
8489 char title[MAX_STRING_LENGTH];
8490 char *data;
8491 const char *pold;
8492 INT data_size, index;
8493 INT tid, i, j, n_data, string_length, status, size;
8494 HNDLE hKey;
8495 KEY root_key;
8496
8497 title[0] = 0;
8498
8499 if (hKeyRoot == 0)
8501
8502 db_get_key(hDB, hKeyRoot, &root_key);
8503
8504 /* initial data size */
8505 data_size = 1000;
8506 data = (char *) malloc(data_size);
8507 if (data == NULL) {
8508 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8509 return DB_NO_MEMORY;
8510 }
8511
8512 do {
8513 char line[10*MAX_STRING_LENGTH];
8514
8515 if (*buffer == 0)
8516 break;
8517
8518 for (i = 0; *buffer != '\n' && *buffer && i < 10*MAX_STRING_LENGTH; i++)
8519 line[i] = *buffer++;
8520
8521 if (i == 10*MAX_STRING_LENGTH) {
8522 line[10*MAX_STRING_LENGTH-1] = 0;
8523 cm_msg(MERROR, "db_paste", "line too long: %s...", line);
8524 free(data);
8525 return DB_TRUNCATED;
8526 }
8527
8528 line[i] = 0;
8529 if (*buffer == '\n')
8530 buffer++;
8531
8532 /* check if it is a section title */
8533 if (line[0] == '[') {
8534 /* extract title and append '/' */
8535 mstrlcpy(title, line + 1, sizeof(title));
8536 if (strchr(title, ']'))
8537 *strchr(title, ']') = 0;
8538 if (title[0] && title[strlen(title) - 1] != '/')
8539 mstrlcat(title, "/", sizeof(title));
8540 } else {
8541 /* valid data line if it includes '=' and no ';' */
8542 if (strchr(line, '=') && line[0] != ';') {
8543 char key_name[MAX_ODB_PATH];
8544 char test_str[MAX_ODB_PATH];
8545 char data_str[10*MAX_STRING_LENGTH];
8546
8547 /* copy type info and data */
8548 char* pline = strrchr(line, '=') + 1;
8549 while (strstr(line, ": [") != NULL && strstr(line, ": [") < pline) {
8550 pline -= 2;
8551 while (*pline != '=' && pline > line)
8552 pline--;
8553 pline++;
8554 }
8555 while (*pline == ' ')
8556 pline++;
8557 mstrlcpy(data_str, pline, sizeof(data_str));
8558
8559 /* extract key name */
8560 *strrchr(line, '=') = 0;
8561 while (strstr(line, ": [") && strchr(line, '='))
8562 *strrchr(line, '=') = 0;
8563
8564 pline = &line[strlen(line) - 1];
8565 while (*pline == ' ')
8566 *pline-- = 0;
8567
8568 key_name[0] = 0;
8569 if (title[0] != '.')
8570 mstrlcpy(key_name, title, sizeof(key_name));
8571
8572 mstrlcat(key_name, line, sizeof(key_name));
8573
8574 /* evaluate type info */
8575 mstrlcpy(line, data_str, sizeof(line));
8576 if (strchr(line, ' '))
8577 *strchr(line, ' ') = 0;
8578
8579 n_data = 1;
8580 if (strchr(line, '[')) {
8581 n_data = atol(strchr(line, '[') + 1);
8582 *strchr(line, '[') = 0;
8583 }
8584
8585 for (tid = 0; tid < TID_LAST; tid++)
8586 if (strcmp(rpc_tid_name(tid), line) == 0)
8587 break;
8588 if (tid == TID_LAST) {
8589 for (tid = 0; tid < TID_LAST; tid++)
8590 if (strcmp(rpc_tid_name_old(tid), line) == 0)
8591 break;
8592 }
8593
8594 string_length = 0;
8595
8596 if (tid == TID_LAST)
8597 cm_msg(MERROR, "db_paste", "found unknown data type \"%s\" in ODB file", line);
8598 else {
8599 /* skip type info */
8600 char* pc = data_str;
8601 while (*pc != ' ' && *pc)
8602 pc++;
8603 while ((*pc == ' ' || *pc == ':') && *pc)
8604 pc++;
8605
8606 //mstrlcpy(data_str, pc, sizeof(data_str)); // MacOS 10.9 does not permit mstrlcpy() of overlapping strings
8607 assert(strlen(pc) < sizeof(data_str)); // "pc" points at a substring inside "data_str"
8608 memmove(data_str, pc, strlen(pc)+1);
8609
8610 if (n_data > 1) {
8611 data_str[0] = 0;
8612 if (!*buffer)
8613 break;
8614
8615 for (j = 0; *buffer != '\n' && *buffer; j++)
8616 data_str[j] = *buffer++;
8617 data_str[j] = 0;
8618 if (*buffer == '\n')
8619 buffer++;
8620 }
8621
8622 for (i = 0; i < n_data; i++) {
8623 /* strip trailing \n */
8624 char* pc = &data_str[strlen(data_str) - 1];
8625 while (*pc == '\n' || *pc == '\r')
8626 *pc-- = 0;
8627
8628 if (tid == TID_STRING || tid == TID_LINK) {
8629 if (!string_length) {
8630 if (data_str[1] == '=')
8631 string_length = -1;
8632 else
8636 cm_msg(MERROR, "db_paste", "found string exceeding MAX_STRING_LENGTH, odb path \"%s\"", key_name);
8637 }
8638 if (string_length == 0) {
8639 string_length = 32;
8640 cm_msg(MERROR, "db_paste", "found string length of zero, set to 32, odb path \"%s\"", key_name);
8641 }
8642 }
8643
8644 if (string_length == -1) {
8645 /* multi-line string */
8646 if (strstr(buffer, "\n====#$@$#====\n") != NULL) {
8647 string_length = (POINTER_T) strstr(buffer, "\n====#$@$#====\n") - (POINTER_T) buffer + 1;
8648
8649 if (string_length >= data_size) {
8650 data_size += string_length + 100;
8651 data = (char *) realloc(data, data_size);
8652 if (data == NULL) {
8653 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8654 return DB_NO_MEMORY;
8655 }
8656 }
8657
8658 memset(data, 0, data_size);
8659 strncpy(data, buffer, string_length);
8660 data[string_length - 1] = 0;
8661 buffer = strstr(buffer, "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
8662 } else
8663 cm_msg(MERROR, "db_paste", "found multi-line string without termination sequence");
8664 } else {
8665 char* pc = data_str + 2;
8666 while (*pc && *pc != ' ')
8667 pc++;
8668
8669 // skip one space (needed for strings starting with spaces)
8670 if (*pc)
8671 pc++;
8672
8673 /* limit string size */
8674 *(pc + string_length - 1) = 0;
8675
8676 /* increase data buffer if necessary */
8677 if (string_length * (i + 1) >= data_size) {
8678 data_size += 1000;
8679 data = (char *) realloc(data, data_size);
8680 if (data == NULL) {
8681 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8682 return DB_NO_MEMORY;
8683 }
8684 }
8685
8687 }
8688 } else {
8689 char* pc = data_str;
8690
8691 if (n_data > 1 && data_str[0] == '[') {
8692 index = atoi(data_str+1);
8693 pc = strchr(data_str, ']') + 1;
8694 while (*pc && *pc == ' ')
8695 pc++;
8696 } else
8697 index = 0;
8698
8699 /* increase data buffer if necessary */
8700 if (rpc_tid_size(tid) * (index + 1) >= data_size) {
8701 data_size += 1000;
8702 data = (char *) realloc(data, data_size);
8703 if (data == NULL) {
8704 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8705 return DB_NO_MEMORY;
8706 }
8707 }
8708
8709 db_sscanf(pc, data, &size, index, tid);
8710 }
8711
8712 if (i < n_data - 1) {
8713 data_str[0] = 0;
8714 if (!*buffer)
8715 break;
8716
8717 pold = buffer;
8718
8719 for (j = 0; *buffer != '\n' && *buffer; j++)
8720 data_str[j] = *buffer++;
8721 data_str[j] = 0;
8722 if (*buffer == '\n')
8723 buffer++;
8724
8725 /* test if valid data */
8726 if (tid != TID_STRING && tid != TID_LINK) {
8727 if (data_str[0] == 0 || (strchr(data_str, '=')
8728 && strchr(data_str, ':')))
8729 buffer = pold;
8730 }
8731 }
8732 }
8733
8734 /* skip system client entries */
8735 mstrlcpy(test_str, key_name, sizeof(test_str));
8736 test_str[15] = 0;
8737
8738 if (!equal_ustring(test_str, "/System/Clients")) {
8739 if (root_key.type != TID_KEY) {
8740 /* root key is destination key */
8741 hKey = hKeyRoot;
8742 } else {
8743 /* create key and set value */
8744 if (key_name[0] == '/') {
8745 status = db_find_link(hDB, 0, key_name, &hKey);
8746 if (status == DB_NO_KEY) {
8747 db_create_key(hDB, 0, key_name, tid);
8748 status = db_find_link(hDB, 0, key_name, &hKey);
8749 }
8750 } else {
8751 status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
8752 if (status == DB_NO_KEY) {
8753 db_create_key(hDB, hKeyRoot, key_name, tid);
8754 status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
8755 }
8756 }
8757 }
8758
8759 /* set key data if created successfully */
8760 if (hKey) {
8761 if (tid == TID_STRING || tid == TID_LINK)
8762 db_set_link_data(hDB, hKey, data, string_length * n_data, n_data, tid);
8763 else
8764 db_set_link_data(hDB, hKey, data, rpc_tid_size(tid) * n_data, n_data, tid);
8765 }
8766 }
8767 }
8768 }
8769 }
8770 } while (TRUE);
8771
8772 free(data);
8773 return DB_SUCCESS;
8774}
8775
8776/********************************************************************/
8777/*
8778 Only internally used by db_paste_xml
8779*/
8781{
8782 int status;
8783
8784 if (strcmp(mxml_get_name(node), "odb") == 0) {
8785 for (int i = 0; i < mxml_get_number_of_children(node); i++) {
8787 if (status != DB_SUCCESS)
8788 return status;
8789 }
8790 } else if (strcmp(mxml_get_name(node), "dir") == 0) {
8791 const char* name = mxml_get_attribute(node, "name");
8792
8793 if (name == NULL) {
8794 cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no name in XML data", mxml_get_name(node));
8795 return DB_TYPE_MISMATCH;
8796 }
8797
8798 HNDLE hKey;
8800
8801 if (status == DB_NO_KEY) {
8803 if (status == DB_NO_ACCESS) {
8804 cm_msg(MINFO, "db_paste_node", "cannot load key \"%s\": write protected", name);
8805 return DB_SUCCESS; /* key or tree is locked, just skip it */
8806 }
8807
8808 if (status != DB_SUCCESS && status != DB_KEY_EXIST) {
8809 cm_msg(MERROR, "db_paste_node", "cannot create key \"%s\" in ODB, status = %d", name, status);
8810 return status;
8811 }
8813 if (status != DB_SUCCESS) {
8814 cm_msg(MERROR, "db_paste_node", "cannot find key \"%s\" in ODB", name);
8815 return status;
8816 }
8817 }
8818
8819 std::string path = db_get_path(hDB, hKey);
8820 if (!equal_ustring(path.c_str(), "/System/Clients")) {
8821 for (int i = 0; i < mxml_get_number_of_children(node); i++) {
8823 if (status != DB_SUCCESS)
8824 return status;
8825 }
8826 }
8827 } else if (strcmp(mxml_get_name(node), "key") == 0 || strcmp(mxml_get_name(node), "keyarray") == 0) {
8828
8829 const char* name = mxml_get_attribute(node, "name");
8830
8831 if (name == NULL) {
8832 cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no name in XML data", mxml_get_name(node));
8833 return DB_TYPE_MISMATCH;
8834 }
8835
8836 int num_values;
8837 if (strcmp(mxml_get_name(node), "keyarray") == 0)
8838 num_values = atoi(mxml_get_attribute(node, "num_values"));
8839 else
8840 num_values = 0;
8841
8842 const char* type = mxml_get_attribute(node, "type");
8843
8844 if (type == NULL) {
8845 cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no type in XML data", mxml_get_name(node));
8846 return DB_TYPE_MISMATCH;
8847 }
8848
8849 int tid = rpc_name_tid(type);
8850 if (tid == 0) {
8851 cm_msg(MERROR, "db_paste_node", "found unknown data type \"%s\" in XML data", type);
8852 return DB_TYPE_MISMATCH;
8853 }
8854
8855 HNDLE hKey;
8857 if (status == DB_NO_KEY) {
8859 if (status == DB_NO_ACCESS) {
8860 cm_msg(MINFO, "db_paste_node", "cannot load key \"%s\": write protected", name);
8861 return DB_SUCCESS; /* key or tree is locked, just skip it */
8862 }
8863
8864 if (status != DB_SUCCESS) {
8865 cm_msg(MERROR, "db_paste_node", "cannot create key \"%s\" in ODB, status = %d", name, status);
8866 return status;
8867 }
8869 if (status != DB_SUCCESS) {
8870 cm_msg(MERROR, "db_paste_node", "cannot find key \"%s\" in ODB, status = %d", name, status);
8871 return status;
8872 }
8873 } else {
8874 KEY key;
8876 if (status != DB_SUCCESS) {
8877 cm_msg(MERROR, "db_paste_node", "cannot get key \"%s\" in ODB, status = %d", name, status);
8878 return status;
8879 }
8880
8881 if (num_values > 0 && num_values != key.num_values && key.item_size > 0) {
8882 status = db_set_num_values(hDB, hKey, num_values);
8883 if (status != DB_SUCCESS) {
8884 cm_msg(MERROR, "db_paste_node", "cannot resize key \"%s\" in ODB, status = %d", name, status);
8885 return status;
8886 }
8887 }
8888 }
8889
8890 int size = 0;
8891 char *buf = NULL;
8892
8893 if (tid == TID_STRING || tid == TID_LINK) {
8894 size = atoi(mxml_get_attribute(node, "size"));
8895 buf = (char *)malloc(size);
8896 assert(buf);
8897 buf[0] = 0;
8898 }
8899
8900 if (num_values) {
8901 /* evaluate array */
8902 for (int i = 0; i < mxml_get_number_of_children(node); i++) {
8904 int idx;
8905 if (mxml_get_attribute(child, "index"))
8906 idx = atoi(mxml_get_attribute(child, "index"));
8907 else
8908 idx = i;
8909 if (tid == TID_STRING || tid == TID_LINK) {
8910 if (mxml_get_value(child) == NULL) {
8911 status = db_set_data_index(hDB, hKey, "", size, i, tid);
8912 if (status == DB_NO_ACCESS) {
8913 cm_msg(MINFO, "db_paste_node", "cannot load string or link \"%s\": write protected", mxml_get_attribute(node, "name"));
8914 return DB_SUCCESS; /* key or tree is locked, just skip it */
8915 } else if (status != DB_SUCCESS) {
8916 cm_msg(MERROR, "db_paste_node", "cannot load string or link \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
8917 return status;
8918 }
8919 } else {
8920 mstrlcpy(buf, mxml_get_value(child), size);
8921 status = db_set_data_index(hDB, hKey, buf, size, idx, tid);
8922 if (status == DB_NO_ACCESS) {
8923 cm_msg(MINFO, "db_paste_node", "cannot load array element \"%s\": write protected", mxml_get_attribute(node, "name"));
8924 return DB_SUCCESS; /* key or tree is locked, just skip it */
8925 } else if (status != DB_SUCCESS) {
8926 cm_msg(MERROR, "db_paste_node", "cannot load array element \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
8927 return status;
8928 }
8929 }
8930 } else {
8931 char data[256];
8932 db_sscanf(mxml_get_value(child), data, &size, 0, tid);
8934 if (status == DB_NO_ACCESS) {
8935 cm_msg(MINFO, "db_paste_node", "cannot load array element \"%s\": write protected", mxml_get_attribute(node, "name"));
8936 return DB_SUCCESS; /* key or tree is locked, just skip it */
8937 } else if (status != DB_SUCCESS) {
8938 cm_msg(MERROR, "db_paste_node", "cannot load array element \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
8939 return status;
8940 }
8941 }
8942 }
8943
8944 } else { /* single value */
8945 if (tid == TID_STRING || tid == TID_LINK) {
8946 size = atoi(mxml_get_attribute(node, "size"));
8947 if (mxml_get_value(node) == NULL) {
8948 status = db_set_data(hDB, hKey, "", size, 1, tid);
8949 if (status == DB_NO_ACCESS) {
8950 cm_msg(MINFO, "db_paste_node", "cannot load string or link \"%s\": write protected", mxml_get_attribute(node, "name"));
8951 return DB_SUCCESS; /* key or tree is locked, just skip it */
8952 } else if (status != DB_SUCCESS) {
8953 cm_msg(MERROR, "db_paste_node", "cannot load string or link \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
8954 return status;
8955 }
8956 } else {
8957 mstrlcpy(buf, mxml_get_value(node), size);
8958 status = db_set_data(hDB, hKey, buf, size, 1, tid);
8959 if (status == DB_NO_ACCESS) {
8960 cm_msg(MINFO, "db_paste_node", "cannot load value \"%s\": write protected", mxml_get_attribute(node, "name"));
8961 return DB_SUCCESS; /* key or tree is locked, just skip it */
8962 } else if (status != DB_SUCCESS) {
8963 cm_msg(MERROR, "db_paste_node", "cannot load value \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
8964 return status;
8965 }
8966 }
8967 } else {
8968 char data[256];
8969 db_sscanf(mxml_get_value(node), data, &size, 0, tid);
8970 status = db_set_data(hDB, hKey, data, rpc_tid_size(tid), 1, tid);
8971 if (status == DB_NO_ACCESS) {
8972 cm_msg(MINFO, "db_paste_node", "cannot load value \"%s\": write protected", mxml_get_attribute(node, "name"));
8973 return DB_SUCCESS; /* key or tree is locked, just skip it */
8974 } else if (status != DB_SUCCESS) {
8975 cm_msg(MERROR, "db_paste_node", "cannot load value \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
8976 return status;
8977 }
8978 }
8979 }
8980
8981 if (buf) {
8982 free(buf);
8983 buf = NULL;
8984 }
8985 }
8986
8987 return DB_SUCCESS;
8988}
8989
8990/********************************************************************/
8999{
9000 char error[256];
9001 INT status;
9003
9004 if (hKeyRoot == 0)
9006
9007 /* parse XML buffer */
9008 tree = mxml_parse_buffer(buffer, error, sizeof(error), NULL);
9009 if (tree == NULL) {
9010 puts(error);
9011 return DB_TYPE_MISMATCH;
9012 }
9013
9014 node = mxml_find_node(tree, "odb");
9015 if (node == NULL) {
9016 puts("Cannot find element \"odb\" in XML data");
9017 return DB_TYPE_MISMATCH;
9018 }
9019
9021
9023
9024 return status;
9025}
9026
9027/********************************************************************/
9037INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, int *buffer_size, bool header)
9038{
9039
9040 if (rpc_is_remote())
9041 return rpc_call(RPC_DB_COPY_XML, hDB, hKey, buffer, buffer_size, header);
9042
9043#ifdef LOCAL_ROUTINES
9044 {
9045 INT len;
9046 char *p;
9047 MXML_WRITER *writer;
9048
9049 /* open file */
9050 writer = mxml_open_buffer();
9051 if (writer == NULL) {
9052 cm_msg(MERROR, "db_copy_xml", "Cannot allocate buffer");
9053 return DB_NO_MEMORY;
9054 }
9055
9056 if (header) {
9057 std::string path = db_get_path(hDB, hKey);
9058
9059 /* write XML header */
9060 mxml_start_element(writer, "odb");
9061 mxml_write_attribute(writer, "root", path.c_str());
9062 mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
9063 mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", "http://midas.psi.ch/odb.xsd");
9064
9065 db_save_xml_key(hDB, hKey, 0, writer);
9066 mxml_end_element(writer); // "odb"
9067 } else {
9068 KEY key;
9069 int status = db_get_key(hDB, hKey, &key);
9070 if (status != DB_SUCCESS)
9071 return status;
9072
9073 db_save_xml_key(hDB, hKey, 1, writer);
9074 }
9075
9076 p = mxml_close_buffer(writer);
9077 len = strlen(p) + 1;
9078 if (len > *buffer_size) {
9079 free(p);
9080 *buffer_size = 0;
9081 return DB_TRUNCATED;
9082 }
9083
9084 mstrlcpy(buffer, p, len);
9085 free(p);
9086 *buffer_size = len;
9087 }
9088#endif /* LOCAL_ROUTINES */
9089
9090 return DB_SUCCESS;
9091}
9092
9094#ifndef DOXYGEN_SHOULD_SKIP_THIS
9095
9096/*------------------------------------------------------------------*/
9097void name2c(char *str)
9098/********************************************************************\
9099
9100 Routine: name2c
9101
9102 Purpose: Convert key name to C name. Internal use only.
9103
9104\********************************************************************/
9105{
9106 if (*str >= '0' && *str <= '9')
9107 *str = '_';
9108
9109 while (*str) {
9110 if (!(*str >= 'a' && *str <= 'z') && !(*str >= 'A' && *str <= 'Z') && !(*str >= '0' && *str <= '9'))
9111 *str = '_';
9112 *str = (char) tolower(*str);
9113 str++;
9114 }
9115}
9116
9117/*------------------------------------------------------------------*/
9119/********************************************************************\
9120
9121 Routine: db_save_tree_struct
9122
9123 Purpose: Save database tree as a C structure. Gets called by
9124 db_save_struct(). Internal use only.
9125
9126\********************************************************************/
9127{
9128 INT i, idx;
9129 KEY key;
9130 HNDLE hSubkey;
9131 int wr;
9132
9133 /* first enumerate this level */
9134 for (idx = 0;; idx++) {
9135 char name[MAX_ODB_PATH];
9136
9138 if (!hSubkey)
9139 break;
9140
9141 /* first get the name of the link, than the type of the link target */
9143 mstrlcpy(name, key.name, sizeof(name));
9145
9147
9148 if (key.type != TID_KEY) {
9149 char line[MAX_ODB_PATH];
9150 char str[MAX_ODB_PATH];
9151
9152 for (i = 0; i <= level; i++) {
9153 wr = write(hfile, " ", 2);
9154 assert(wr == 2);
9155 }
9156
9157 switch (key.type) {
9158 case TID_INT8:
9159 case TID_CHAR:
9160 strcpy(line, "char");
9161 break;
9162 case TID_INT16:
9163 strcpy(line, "short");
9164 break;
9165 case TID_FLOAT:
9166 strcpy(line, "float");
9167 break;
9168 case TID_DOUBLE:
9169 strcpy(line, "double");
9170 break;
9171 case TID_BITFIELD:
9172 strcpy(line, "unsigned char");
9173 break;
9174 case TID_STRING:
9175 strcpy(line, "char");
9176 break;
9177 case TID_LINK:
9178 strcpy(line, "char");
9179 break;
9180 default:
9181 strcpy(line, rpc_tid_name(key.type));
9182 break;
9183 }
9184
9185 mstrlcat(line, " ", sizeof(line));
9186 mstrlcpy(str, name, sizeof(str));
9187 name2c(str);
9188
9189 if (key.num_values > 1)
9190 sprintf(str + strlen(str), "[%d]", key.num_values);
9191 if (key.type == TID_STRING || key.type == TID_LINK)
9192 sprintf(str + strlen(str), "[%d]", key.item_size);
9193
9194 mstrlcpy(line + 10, str, sizeof(line) - 10);
9195 mstrlcat(line, ";\n", sizeof(line));
9196
9197 wr = write(hfile, line, strlen(line));
9198 assert(wr > 0);
9199 } else {
9200 char line[10+MAX_ODB_PATH];
9201 char str[MAX_ODB_PATH];
9202
9203 /* recurse subtree */
9204 for (i = 0; i <= level; i++) {
9205 wr = write(hfile, " ", 2);
9206 assert(wr == 2);
9207 }
9208
9209 sprintf(line, "struct {\n");
9210 wr = write(hfile, line, strlen(line));
9211 assert(wr > 0);
9213
9214 for (i = 0; i <= level; i++) {
9215 wr = write(hfile, " ", 2);
9216 assert(wr == 2);
9217 }
9218
9219 strcpy(str, name);
9220 name2c(str);
9221
9222 sprintf(line, "} %s;\n", str);
9223 wr = write(hfile, line, strlen(line));
9224 assert(wr > 0);
9225 }
9226 }
9227}
9228
9230#endif /* DOXYGEN_SHOULD_SKIP_THIS */
9231
9232/********************************************************************/
9245INT db_save(HNDLE hDB, HNDLE hKey, const char *filename, BOOL bRemote)
9246{
9247 if (rpc_is_remote() && bRemote)
9248 return rpc_call(RPC_DB_SAVE, hDB, hKey, filename, bRemote);
9249
9250#ifdef LOCAL_ROUTINES
9251 {
9252 INT hfile, size, buffer_size, n, status;
9253 char *buffer;
9254
9255 /* open file */
9256 hfile = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0644);
9257 if (hfile == -1) {
9258 cm_msg(MERROR, "db_save", "Cannot open file \"%s\"", filename);
9259 return DB_FILE_ERROR;
9260 }
9261
9262 std::string path = db_get_path(hDB, hKey);
9263
9264 buffer_size = 10000;
9265 do {
9266 buffer = (char *) malloc(buffer_size);
9267 if (buffer == NULL) {
9268 cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
9269 break;
9270 }
9271
9272 size = buffer_size;
9273 status = db_copy(hDB, hKey, buffer, &size, path.c_str());
9274 if (status != DB_TRUNCATED) {
9275 n = write(hfile, buffer, buffer_size - size);
9276 free(buffer);
9277 buffer = NULL;
9278
9279 if (n != buffer_size - size) {
9280 cm_msg(MERROR, "db_save", "cannot save .ODB file");
9281 close(hfile);
9282 return DB_FILE_ERROR;
9283 }
9284 break;
9285 }
9286
9287 /* increase buffer size if truncated */
9288 free(buffer);
9289 buffer = NULL;
9290 buffer_size *= 2;
9291 } while (1);
9292
9293 close(hfile);
9294
9295 }
9296#endif /* LOCAL_ROUTINES */
9297
9298 return DB_SUCCESS;
9299}
9300
9301/*------------------------------------------------------------------*/
9302
9303void xml_encode(char *src, int size)
9304{
9305 int i;
9306 char *dst, *p;
9307
9308 dst = (char *) malloc(size);
9309 if (dst == NULL)
9310 return;
9311
9312 *dst = 0;
9313 for (i = 0; i < (int) strlen(src); i++) {
9314 switch (src[i]) {
9315 case '<':
9316 mstrlcat(dst, "&lt;", size);
9317 break;
9318 case '>':
9319 mstrlcat(dst, "&gt;", size);
9320 break;
9321 case '&':
9322 mstrlcat(dst, "&amp;", size);
9323 break;
9324 case '\"':
9325 mstrlcat(dst, "&quot;", size);
9326 break;
9327 case '\'':
9328 mstrlcat(dst, "&apos;", size);
9329 break;
9330 default:
9331 if ((int) strlen(dst) >= size) {
9332 free(dst);
9333 return;
9334 }
9335 p = dst + strlen(dst);
9336 *p = src[i];
9337 *(p + 1) = 0;
9338 }
9339 }
9340
9341 mstrlcpy(src, dst, size);
9342}
9343
9344/*------------------------------------------------------------------*/
9345
9347{
9348 INT i, idx, size, status;
9349 char *data;
9350 HNDLE hSubkey;
9351 KEY key;
9352
9354 if (status != DB_SUCCESS)
9355 return status;
9356
9357 if (key.type == TID_KEY) {
9358
9359 /* save opening tag for subtree */
9360
9361 if (level > 0) {
9362 mxml_start_element(writer, "dir");
9363 mxml_write_attribute(writer, "name", key.name);
9364 mxml_write_attribute(writer, "handle", std::to_string(hKey).c_str());
9365 }
9366
9367 for (idx = 0;; idx++) {
9369
9370 if (!hSubkey)
9371 break;
9372
9373 /* save subtree */
9374 status = db_save_xml_key(hDB, hSubkey, level + 1, writer);
9375 if (status != DB_SUCCESS)
9376 return status;
9377 }
9378
9379 /* save closing tag for subtree */
9380 if (level > 0)
9381 mxml_end_element(writer);
9382
9383 } else {
9384 /* save key value */
9385
9386 if (key.num_values > 1)
9387 mxml_start_element(writer, "keyarray");
9388 else
9389 mxml_start_element(writer, "key");
9390 mxml_write_attribute(writer, "name", key.name);
9391 mxml_write_attribute(writer, "type", rpc_tid_name(key.type));
9392 mxml_write_attribute(writer, "handle", std::to_string(hKey).c_str());
9393
9394 if (key.type == TID_STRING || key.type == TID_LINK) {
9395 char str[256];
9396 sprintf(str, "%d", key.item_size);
9397 mxml_write_attribute(writer, "size", str);
9398 }
9399
9400 if (key.num_values > 1) {
9401 char str[256];
9402 sprintf(str, "%d", key.num_values);
9403 mxml_write_attribute(writer, "num_values", str);
9404 }
9405
9406 size = key.total_size;
9407 data = (char *) malloc(size+1); // an extra byte to zero-terminate strings
9408 if (data == NULL) {
9409 cm_msg(MERROR, "db_save_xml_key", "cannot allocate data buffer");
9410 return DB_NO_MEMORY;
9411 }
9412
9413 db_get_link_data(hDB, hKey, data, &size, key.type);
9414
9415 if (key.num_values == 1) {
9416 if (key.type == TID_STRING) {
9417 data[size] = 0; // make sure strings are NUL-terminated
9418 mxml_write_value(writer, data);
9419 } else {
9420 std::string str = db_sprintf(data, key.item_size, 0, key.type);
9422 std::string path = db_get_path(hDB, hKey);
9423 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());
9424 }
9425 mxml_write_value(writer, str.c_str());
9426 }
9427 mxml_end_element(writer);
9428
9429 } else { /* array of values */
9430
9431 for (i = 0; i < key.num_values; i++) {
9432
9433 mxml_start_element(writer, "value");
9434
9435 {
9436 char str[256];
9437 sprintf(str, "%d", i);
9438 mxml_write_attribute(writer, "index", str);
9439 }
9440
9441 if (key.type == TID_STRING) {
9442 char* p = data + i * key.item_size;
9443 p[key.item_size - 1] = 0; // make sure string is NUL-terminated
9444 //cm_msg(MINFO, "db_save_xml_key", "odb string array item_size %d, index %d length %d", key.item_size, i, (int)strlen(p));
9445 mxml_write_value(writer, p);
9446 } else {
9447 std::string str = db_sprintf(data, key.item_size, i, key.type);
9448 if ((key.type == TID_STRING) && (str.length() >= MAX_STRING_LENGTH-1)) {
9449 std::string path = db_get_path(hDB, hKey);
9450 cm_msg(MERROR, "db_save_xml_key", "Long odb string array probably truncated, odb path \"%s\"[%d]", path.c_str(), i);
9451 }
9452 mxml_write_value(writer, str.c_str());
9453 }
9454
9455 mxml_end_element(writer);
9456 }
9457
9458 mxml_end_element(writer); /* keyarray */
9459 }
9460
9461 free(data);
9462 data = NULL;
9463 }
9464
9465 return DB_SUCCESS;
9466}
9467
9468/********************************************************************/
9480INT db_save_xml(HNDLE hDB, HNDLE hKey, const char *filename)
9481{
9482#ifdef LOCAL_ROUTINES
9483 {
9484 INT status;
9485 MXML_WRITER *writer;
9486
9487 /* open file */
9488 writer = mxml_open_file(filename);
9489 if (writer == NULL) {
9490 cm_msg(MERROR, "db_save_xml", "Cannot open file \"%s\"", filename);
9491 return DB_FILE_ERROR;
9492 }
9493
9494 std::string path = db_get_path(hDB, hKey);
9495
9496 /* write XML header */
9497 mxml_start_element(writer, "odb");
9498 mxml_write_attribute(writer, "root", path.c_str());
9499 mxml_write_attribute(writer, "filename", filename);
9500 mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
9501
9502 std::string xsd_path;
9503
9504 if (getenv("MIDASSYS"))
9505 xsd_path = getenv("MIDASSYS");
9506 else
9507 xsd_path = ".";
9508
9510 xsd_path += "odb.xsd";
9511 mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", xsd_path.c_str());
9512
9513 status = db_save_xml_key(hDB, hKey, 0, writer);
9514
9515 mxml_end_element(writer); // "odb"
9516 mxml_close_file(writer);
9517
9518 return status;
9519 }
9520#endif /* LOCAL_ROUTINES */
9521
9522 return DB_SUCCESS;
9523}
9524
9525/*------------------------------------------------------------------*/
9526
9527void json_write(char **buffer, int* buffer_size, int* buffer_end, int level, const char* s, int quoted)
9528{
9529 int len, remain, xlevel;
9530
9531 len = strlen(s);
9532 remain = *buffer_size - *buffer_end;
9533 assert(remain >= 0);
9534
9535 xlevel = 2*level;
9536
9537 while (10 + xlevel + 3*len > remain) {
9538 // reallocate the buffer
9539 int new_buffer_size = 2*(*buffer_size);
9540 if (new_buffer_size < 4*1024)
9541 new_buffer_size = 4*1024;
9542 //printf("reallocate: len %d, size %d, remain %d, allocate %d\n", len, *buffer_size, remain, new_buffer_size);
9543 assert(new_buffer_size > *buffer_size);
9544 *buffer = (char *)realloc(*buffer, new_buffer_size);
9545 assert(*buffer);
9546 *buffer_size = new_buffer_size;
9547 remain = *buffer_size - *buffer_end;
9548 assert(remain >= 0);
9549 }
9550
9551 if (xlevel) {
9552 int i;
9553 for (i=0; i<xlevel; i++)
9554 (*buffer)[(*buffer_end)++] = ' ';
9555 }
9556
9557 if (!quoted) {
9558 memcpy(*buffer + *buffer_end, s, len);
9559 *buffer_end += len;
9560 (*buffer)[*buffer_end] = 0; // NUL-terminate the buffer
9561 return;
9562 }
9563
9564 char *bufptr = *buffer;
9565 int bufend = *buffer_end;
9566
9567 bufptr[bufend++] = '"';
9568
9569 while (*s) {
9570 switch (*s) {
9571 case '\"':
9572 bufptr[bufend++] = '\\';
9573 bufptr[bufend++] = '\"';
9574 s++;
9575 break;
9576 case '\\':
9577 bufptr[bufend++] = '\\';
9578 bufptr[bufend++] = '\\';
9579 s++;
9580 break;
9581#if 0
9582 case '/':
9583 bufptr[bufend++] = '\\';
9584 bufptr[bufend++] = '/';
9585 s++;
9586 break;
9587#endif
9588 case '\b':
9589 bufptr[bufend++] = '\\';
9590 bufptr[bufend++] = 'b';
9591 s++;
9592 break;
9593 case '\f':
9594 bufptr[bufend++] = '\\';
9595 bufptr[bufend++] = 'f';
9596 s++;
9597 break;
9598 case '\n':
9599 bufptr[bufend++] = '\\';
9600 bufptr[bufend++] = 'n';
9601 s++;
9602 break;
9603 case '\r':
9604 bufptr[bufend++] = '\\';
9605 bufptr[bufend++] = 'r';
9606 s++;
9607 break;
9608 case '\t':
9609 bufptr[bufend++] = '\\';
9610 bufptr[bufend++] = 't';
9611 s++;
9612 break;
9613 default:
9614 bufptr[bufend++] = *s++;
9615 }
9616 }
9617
9618 bufptr[bufend++] = '"';
9619 bufptr[bufend] = 0; // NUL-terminate the buffer
9620
9621 *buffer_end = bufend;
9622
9623 remain = *buffer_size - *buffer_end;
9624 assert(remain > 0);
9625}
9626
9628{
9629 // sprintf "%.2e" can result in strings like "1,23e6" if
9630 // the user's locale has a comma for the decimal spearator.
9631 // However the JSON RFC requires a dot for the decimal.
9632 // This approach of "sprintf-then-replace" is much safer than
9633 // changing the user's locale, and much faster than the C++
9634 // approach of using a stringstream that we "imbue" with a C locale.
9635 char* comma = strchr(str, ',');
9636
9637 if (comma != nullptr) {
9638 *comma = '.';
9639 }
9640}
9641
9642static void json_write_data(char **buffer, int* buffer_size, int* buffer_end, int level, const KEY* key, const char* p)
9643{
9644 char str[256];
9645 switch (key->type) {
9646 case TID_UINT8:
9647 sprintf(str, "%u", *(unsigned char*)p);
9648 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9649 break;
9650 case TID_INT8:
9651 sprintf(str, "%d", *(char*)p);
9652 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9653 break;
9654 case TID_CHAR:
9655 sprintf(str, "%c", *(char*)p);
9656 json_write(buffer, buffer_size, buffer_end, 0, str, 1);
9657 break;
9658 case TID_UINT16:
9659 sprintf(str, "\"0x%04x\"", *(WORD*)p);
9660 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9661 break;
9662 case TID_INT16:
9663 sprintf(str, "%d", *(short*)p);
9664 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9665 break;
9666 case TID_UINT32:
9667 sprintf(str, "\"0x%08x\"", *(DWORD*)p);
9668 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9669 break;
9670 case TID_INT32:
9671 sprintf(str, "%d", *(int*)p);
9672 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9673 break;
9674 case TID_UINT64:
9675 // encode as hex string
9676 sprintf(str, "\"0x%016llx\"", *(UINT64*)p);
9677 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9678 break;
9679 case TID_INT64:
9680 // encode as decimal string
9681 sprintf(str, "\"%lld\"", *(INT64*)p);
9682 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9683 break;
9684 case TID_BOOL:
9685 if (*(int*)p)
9686 json_write(buffer, buffer_size, buffer_end, 0, "true", 0);
9687 else
9688 json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
9689 break;
9690 case TID_FLOAT: {
9691 float flt = (*(float*)p);
9692 if (isnan(flt))
9693 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
9694 else if (isinf(flt)) {
9695 if (flt > 0)
9696 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
9697 else
9698 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
9699 } else if (flt == 0)
9700 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
9701 else if (flt == (int)flt) {
9702 sprintf(str, "%.0f", flt);
9704 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9705 } else {
9706 sprintf(str, "%.7e", flt);
9708 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9709 }
9710 break;
9711 }
9712 case TID_DOUBLE: {
9713 double dbl = (*(double*)p);
9714 if (isnan(dbl))
9715 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
9716 else if (isinf(dbl)) {
9717 if (dbl > 0)
9718 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
9719 else
9720 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
9721 } else if (dbl == 0)
9722 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
9723 else if (dbl == (int)dbl) {
9724 sprintf(str, "%.0f", dbl);
9726 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9727 } else {
9728 sprintf(str, "%.16e", dbl);
9730 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9731 }
9732 break;
9733 }
9734 case TID_BITFIELD:
9735 json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
9736 break;
9737 case TID_STRING:
9738 // data is already NUL terminated // p[key.item_size-1] = 0; // make sure string is NUL terminated!
9739 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
9740 break;
9741 case TID_ARRAY:
9742 json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
9743 break;
9744 case TID_STRUCT:
9745 json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
9746 break;
9747 case TID_KEY:
9748 json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
9749 break;
9750 case TID_LINK:
9751 // data is already NUL terminated // p[key.item_size-1] = 0; // make sure string is NUL terminated!
9752 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
9753 break;
9754 default:
9755 json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
9756 }
9757}
9758
9759static void json_write_key(HNDLE hDB, HNDLE hKey, const KEY* key, const char* link_path, char **buffer, int* buffer_size, int* buffer_end)
9760{
9761 char str[256]; // not used to store anything long, only numeric values like: "item_size: 100"
9762
9763 json_write(buffer, buffer_size, buffer_end, 0, "{ ", 0);
9764
9765 sprintf(str, "\"type\" : %d", key->type);
9766 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9767
9768 if (link_path) {
9769 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9770 json_write(buffer, buffer_size, buffer_end, 0, "link", 1);
9771 json_write(buffer, buffer_size, buffer_end, 0, ": ", 0);
9772 json_write(buffer, buffer_size, buffer_end, 0, link_path, 1);
9773 }
9774
9775 if (key->num_values > 1) {
9776 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9777
9778 sprintf(str, "\"num_values\" : %d", key->num_values);
9779 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9780 }
9781
9782 if (key->type == TID_STRING) {
9783 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9784
9785 sprintf(str, "\"item_size\" : %d", key->item_size);
9786 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9787 }
9788
9789 if (key->notify_count > 0) {
9790 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9791
9792 sprintf(str, "\"notify_count\" : %d", key->notify_count);
9793 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9794 }
9795
9796 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9797
9798 sprintf(str, "\"access_mode\" : %d", key->access_mode);
9799 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9800
9801 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9802
9803 sprintf(str, "\"last_written\" : %d", key->last_written);
9804 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9805
9806 json_write(buffer, buffer_size, buffer_end, 0, " ", 0);
9807
9808 json_write(buffer, buffer_size, buffer_end, 0, "}", 0);
9809}
9810
9811static 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)
9812{
9813 INT i, size, status;
9814 char *data;
9815 KEY key;
9816 KEY link_key;
9817 char link_path[MAX_ODB_PATH];
9818 int omit_top_level_braces = 0;
9819
9820 //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);
9821
9822 if (level < 0) {
9823 level = 0;
9825 }
9826
9828
9829 if (status != DB_SUCCESS)
9830 return status;
9831
9832 link_key = key;
9833
9834 if (key.type == TID_LINK) {
9835 size = sizeof(link_path);
9837
9838 if (status != DB_SUCCESS)
9839 return status;
9840
9841 if (follow_links) {
9843
9844 if (status != DB_SUCCESS)
9845 return status;
9846
9848
9849 if (status != DB_SUCCESS)
9850 return status;
9851 }
9852 }
9853
9854 //printf("key [%s] link [%s], type %d, link %d\n", key.name, link_key.name, key.type, link_key.type);
9855
9856 if (key.type == TID_KEY && (recurse || level<=0)) {
9857 int idx = 0;
9858 int do_close_curly_bracket = 0;
9859
9860 if (level == 0 && !omit_top_level_braces) {
9861 json_write(buffer, buffer_size, buffer_end, 0, "{\n", 0);
9863 }
9864 else if (level > 0) {
9865 json_write(buffer, buffer_size, buffer_end, level, link_key.name, 1);
9866 json_write(buffer, buffer_size, buffer_end, 0, " : {\n", 0);
9868 }
9869
9870 if (level > 100) {
9871 std::string path = db_get_path(hDB, hKey);
9872
9873 json_write(buffer, buffer_size, buffer_end, 0, "/error", 1);
9874 json_write(buffer, buffer_size, buffer_end, 0, " : ", 0);
9875 json_write(buffer, buffer_size, buffer_end, 0, "max nesting level exceed", 1);
9876
9877 cm_msg(MERROR, "db_save_json_key", "max nesting level exceeded at \"%s\", check for symlink loops in this subtree", path.c_str());
9878
9879 } else {
9880 HNDLE hSubkey;
9881
9882 for (;; idx++) {
9884
9885 if (!hSubkey)
9886 break;
9887
9888 if (idx != 0) {
9889 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
9890 }
9891
9892 /* save subtree */
9894 if (status != DB_SUCCESS)
9895 return status;
9896 }
9897 }
9898
9900 if (idx > 0)
9901 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
9902 json_write(buffer, buffer_size, buffer_end, level, "}", 0);
9903 }
9904
9905 } else {
9906
9907 if (save_keys && level == 0) {
9908 json_write(buffer, buffer_size, buffer_end, 0, "{\n", 0);
9909 }
9910
9911 /* save key value */
9912
9913 if (save_keys == 1) {
9914 char str[NAME_LENGTH+15];
9915 sprintf(str, "%s/key", link_key.name);
9916
9917 json_write(buffer, buffer_size, buffer_end, level, str, 1);
9918 json_write(buffer, buffer_size, buffer_end, 0, " : { ", 0);
9919
9920 sprintf(str, "\"type\" : %d", key.type);
9921 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9922
9923 if (link_key.type == TID_LINK && follow_links) {
9924 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9925 json_write(buffer, buffer_size, buffer_end, 0, "link", 1);
9926 json_write(buffer, buffer_size, buffer_end, 0, ": ", 0);
9927 json_write(buffer, buffer_size, buffer_end, 0, link_path, 1);
9928 }
9929
9930 if (key.num_values > 1) {
9931 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9932
9933 sprintf(str, "\"num_values\" : %d", key.num_values);
9934 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9935 }
9936
9937 if (key.type == TID_STRING || key.type == TID_LINK) {
9938 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9939
9940 sprintf(str, "\"item_size\" : %d", key.item_size);
9941 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9942 }
9943
9944 if (key.notify_count > 0) {
9945 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9946
9947 sprintf(str, "\"notify_count\" : %d", key.notify_count);
9948 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9949 }
9950
9951 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9952
9953 sprintf(str, "\"access_mode\" : %d", key.access_mode);
9954 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9955
9956 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9957
9958 sprintf(str, "\"last_written\" : %d", key.last_written);
9959 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9960
9961 json_write(buffer, buffer_size, buffer_end, 0, " ", 0);
9962
9963 json_write(buffer, buffer_size, buffer_end, 0, "}", 0);
9964
9965 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
9966 }
9967
9968 if (save_keys == 2) {
9969 char str[NAME_LENGTH+15];
9970 sprintf(str, "%s/last_written", link_key.name);
9971
9972 json_write(buffer, buffer_size, buffer_end, level, str, 1);
9973
9974 sprintf(str, " : %d", key.last_written);
9975 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9976
9977 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
9978 }
9979
9980 if (save_keys) {
9981 json_write(buffer, buffer_size, buffer_end, level, link_key.name, 1);
9982 json_write(buffer, buffer_size, buffer_end, 0, " : ", 0);
9983 }
9984
9985 if (key.num_values > 1) {
9986 json_write(buffer, buffer_size, buffer_end, 0, "[ ", 0);
9987 }
9988
9989 size = key.total_size;
9990 data = (char *) malloc(size);
9991 if (data == NULL) {
9992 cm_msg(MERROR, "db_save_json_key", "cannot allocate data buffer for %d bytes", size);
9993 return DB_NO_MEMORY;
9994 }
9995
9996 if (key.type != TID_KEY) {
9997 if (follow_links)
9998 status = db_get_data(hDB, hKey, data, &size, key.type);
9999 else
10001
10002 if (status != DB_SUCCESS)
10003 return status;
10004 }
10005
10006 for (i = 0; i < key.num_values; i++) {
10007 char str[256];
10008 char *p = data + key.item_size*i;
10009
10010 if (i != 0)
10011 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10012
10013 switch (key.type) {
10014 case TID_UINT8:
10015 sprintf(str, "%u", *(unsigned char*)p);
10016 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10017 break;
10018 case TID_INT8:
10019 sprintf(str, "%d", *(char*)p);
10020 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10021 break;
10022 case TID_CHAR:
10023 sprintf(str, "%c", *(char*)p);
10024 json_write(buffer, buffer_size, buffer_end, 0, str, 1);
10025 break;
10026 case TID_UINT16:
10027 sprintf(str, "\"0x%04x\"", *(WORD*)p);
10028 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10029 break;
10030 case TID_INT16:
10031 sprintf(str, "%d", *(short*)p);
10032 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10033 break;
10034 case TID_UINT32:
10035 sprintf(str, "\"0x%08x\"", *(DWORD*)p);
10036 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10037 break;
10038 case TID_INT32:
10039 sprintf(str, "%d", *(int*)p);
10040 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10041 break;
10042 case TID_UINT64:
10043 sprintf(str, "\"0x%08llx\"", *(UINT64*)p);
10044 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10045 break;
10046 case TID_INT64:
10047 sprintf(str, "%lld", *(INT64*)p);
10048 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10049 break;
10050 case TID_BOOL:
10051 if (*(int*)p)
10052 json_write(buffer, buffer_size, buffer_end, 0, "true", 0);
10053 else
10054 json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
10055 break;
10056 case TID_FLOAT: {
10057 float flt = (*(float*)p);
10058 if (isnan(flt))
10059 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
10060 else if (isinf(flt)) {
10061 if (flt > 0)
10062 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
10063 else
10064 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
10065 } else if (flt == 0)
10066 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10067 else if (flt == (int)flt) {
10068 sprintf(str, "%.0f", flt);
10069 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10070 } else {
10071 sprintf(str, "%.7e", flt);
10072 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10073 }
10074 break;
10075 }
10076 case TID_DOUBLE: {
10077 double dbl = (*(double*)p);
10078 if (isnan(dbl))
10079 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
10080 else if (isinf(dbl)) {
10081 if (dbl > 0)
10082 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
10083 else
10084 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
10085 } else if (dbl == 0)
10086 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10087 else if (dbl == (int)dbl) {
10088 sprintf(str, "%.0f", dbl);
10089 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10090 } else {
10091 sprintf(str, "%.16e", dbl);
10092 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10093 }
10094 break;
10095 }
10096 case TID_BITFIELD:
10097 json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
10098 break;
10099 case TID_STRING:
10100 p[key.item_size-1] = 0; // make sure string is NUL terminated!
10101 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
10102 break;
10103 case TID_ARRAY:
10104 json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
10105 break;
10106 case TID_STRUCT:
10107 json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
10108 break;
10109 case TID_KEY:
10110 json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
10111 break;
10112 case TID_LINK:
10113 p[key.item_size-1] = 0; // make sure string is NUL terminated!
10114 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
10115 break;
10116 default:
10117 json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
10118 }
10119
10120 }
10121
10122 if (key.num_values > 1) {
10123 json_write(buffer, buffer_size, buffer_end, 0, " ]", 0);
10124 } else {
10125 json_write(buffer, buffer_size, buffer_end, 0, "", 0);
10126 }
10127
10128 free(data);
10129 data = NULL;
10130
10131 if (save_keys && level == 0) {
10132 json_write(buffer, buffer_size, buffer_end, 0, "\n}", 0);
10133 }
10134 }
10135
10136 return DB_SUCCESS;
10137}
10138
10139/********************************************************************/
10150INT db_copy_json_array(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
10151{
10152 int size, asize;
10153 int status;
10154 char* data;
10155 int i;
10156 KEY key;
10157
10159 if (status != DB_SUCCESS)
10160 return status;
10161
10162 assert(key.type != TID_KEY);
10163
10164 if (key.num_values > 1) {
10165 json_write(buffer, buffer_size, buffer_end, 0, "[ ", 0);
10166 }
10167
10168 size = key.total_size;
10169
10170 asize = size;
10171 if (asize < 1024)
10172 asize = 1024;
10173
10174 data = (char *) malloc(asize);
10175 if (data == NULL) {
10176 cm_msg(MERROR, "db_save_json_key_data", "cannot allocate data buffer for %d bytes", asize);
10177 return DB_NO_MEMORY;
10178 }
10179
10180 data[0] = 0; // protect against TID_STRING that has key.total_size == 0.
10181
10182 status = db_get_data(hDB, hKey, data, &size, key.type);
10183 if (status != DB_SUCCESS) {
10184 free(data);
10185 return status;
10186 }
10187
10188 for (i = 0; i < key.num_values; i++) {
10189 char *p = data + key.item_size*i;
10190
10191 if (i != 0)
10192 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10193
10194 json_write_data(buffer, buffer_size, buffer_end, 0, &key, p);
10195 }
10196
10197 if (key.num_values > 1) {
10198 json_write(buffer, buffer_size, buffer_end, 0, " ]", 0);
10199 }
10200
10201 free(data);
10202 data = NULL;
10203
10204 return DB_SUCCESS;
10205}
10206
10207/********************************************************************/
10219INT db_copy_json_index(HNDLE hDB, HNDLE hKey, int index, char **buffer, int* buffer_size, int* buffer_end)
10220{
10221 int status;
10222 KEY key;
10223
10225
10226 if (status != DB_SUCCESS)
10227 return status;
10228
10229 int size = key.item_size;
10230 char* data = (char*)malloc(size + 1); // extra byte for string NUL termination
10231 assert(data != NULL);
10232
10234
10235 if (status != DB_SUCCESS) {
10236 free(data);
10237 return status;
10238 }
10239
10240 assert(size <= key.item_size);
10241 data[key.item_size] = 0; // make sure data is NUL terminated, in case of strings.
10242
10243 json_write_data(buffer, buffer_size, buffer_end, 0, &key, data);
10244
10245 free(data);
10246
10247 return DB_SUCCESS;
10248}
10249
10250static 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)
10251{
10252 int status;
10253 char link_buf[MAX_ODB_PATH]; // link_path points to link_buf
10254 char* link_path = NULL;
10255
10257
10258 if (link.type == TID_LINK) {
10259 int size = sizeof(link_buf);
10261 if (status != DB_SUCCESS)
10262 return status;
10263
10264 //printf("link size %d, len %d, data [%s]\n", size, (int)strlen(link_buf), link_buf);
10265
10266 if ((size > 0) && (strlen(link_buf) > 0)) {
10268
10269 // resolve the link, unless it is a link to an array element
10270 if ((flags & JSFLAG_FOLLOW_LINKS) && strchr(link_path, '[') == NULL) {
10272 if (status != DB_SUCCESS) {
10273 // dangling link to nowhere
10275 }
10276 }
10277 }
10278 }
10279
10282 if (status != DB_SUCCESS)
10283 return status;
10284
10285 if (flags & JSFLAG_OMIT_OLD) {
10286 if (link_target.last_written) {
10287 if (link_target.last_written < timestamp) {
10288 // omit old data
10289 return DB_SUCCESS;
10290 }
10291 }
10292 }
10293
10294 if (need_comma) {
10295 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10296 } else {
10297 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10298 }
10299
10300 char link_name[MAX_ODB_PATH];
10301
10302 mstrlcpy(link_name, link.name, sizeof(link_name));
10303
10304 if (flags & JSFLAG_LOWERCASE) {
10305 for (int i=0; (i<(int)sizeof(link.name)) && link.name[i]; i++)
10306 link_name[i] = tolower(link.name[i]);
10307 }
10308
10309 if ((flags & JSFLAG_LOWERCASE) && !(flags & JSFLAG_OMIT_NAMES)) {
10310 char buf[MAX_ODB_PATH];
10311 mstrlcpy(buf, link_name, sizeof(buf));
10312 mstrlcat(buf, "/name", sizeof(buf));
10313 json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10314 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10315 json_write(buffer, buffer_size, buffer_end, 0, link.name, 1);
10316 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10317 }
10318
10319 if (link.type != TID_KEY && (flags & JSFLAG_SAVE_KEYS)) {
10320 char buf[MAX_ODB_PATH];
10321 mstrlcpy(buf, link_name, sizeof(buf));
10322 mstrlcat(buf, "/key", sizeof(buf));
10323 json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10324 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10325 json_write_key(hDB, hLink, &link_target, link_path, buffer, buffer_size, buffer_end);
10326 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10327 } else if ((link_target.type != TID_KEY) && !(flags & JSFLAG_OMIT_LAST_WRITTEN)) {
10328 char buf[MAX_ODB_PATH];
10329 mstrlcpy(buf, link_name, sizeof(buf));
10330 mstrlcat(buf, "/last_written", sizeof(buf));
10331 json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10332 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10333 sprintf(buf, "%d", link_target.last_written);
10334 json_write(buffer, buffer_size, buffer_end, 0, buf, 0);
10335 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10336 }
10337
10338 json_write(buffer, buffer_size, buffer_end, level, link_name, 1);
10339 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10340
10341 if (link_target.type == TID_KEY && !(flags & JSFLAG_RECURSE)) {
10342 json_write(buffer, buffer_size, buffer_end, 0, "{ }" , 0);
10343 } else {
10344 status = json_write_anything(hDB, hLinkTarget, buffer, buffer_size, buffer_end, level, 0, flags, timestamp);
10345 if (status != DB_SUCCESS)
10346 return status;
10347 }
10348
10349 return DB_SUCCESS;
10350}
10351
10352int json_write_bare_subdir(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int level, int flags, time_t timestamp)
10353{
10354 if (level > MAX_ODB_PATH/2) {
10355 // max nesting level is limited by max odb path, where each subdirectory takes
10356 // at least 2 bytes - 1 byte directory name and 1 byte for "/"
10357 cm_msg(MERROR, "json_write_bare_subdir", "Max ODB subdirectory nesting level exceeded %d", level);
10358 return DB_TRUNCATED;
10359 }
10360
10361 for (int i=0; ; i++) {
10362 int status;
10363
10364 HNDLE hLink;
10366 if (status != DB_SUCCESS && !hLink)
10367 break;
10368
10369 KEY link;
10371 if (status != DB_SUCCESS)
10372 return status;
10373
10374 bool need_comma = (i!=0);
10375
10376 status = json_write_bare_key(hDB, hLink, link, buffer, buffer_size, buffer_end, level, flags, timestamp, need_comma);
10377 if (status != DB_SUCCESS)
10378 return status;
10379 }
10380
10381 return DB_SUCCESS;
10382}
10383
10384int 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)
10385{
10386 int status;
10387 KEY key;
10388
10390 if (status != DB_SUCCESS)
10391 return status;
10392
10393 if (key.type == TID_KEY) {
10394
10395 json_write(buffer, buffer_size, buffer_end, 0, "{", 0);
10396
10397 status = json_write_bare_subdir(hDB, hKey, buffer, buffer_size, buffer_end, level+1, flags, timestamp);
10398 if (status != DB_SUCCESS)
10399 return status;
10400
10401 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10402 json_write(buffer, buffer_size, buffer_end, level, "}", 0);
10403
10404 } else if (must_be_subdir) {
10405 json_write(buffer, buffer_size, buffer_end, 0, "{", 0);
10406 status = json_write_bare_key(hDB, hKey, key, buffer, buffer_size, buffer_end, level+1, flags, timestamp, false);
10407 if (status != DB_SUCCESS)
10408 return status;
10409 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10410 json_write(buffer, buffer_size, buffer_end, level, "}", 0);
10411 } else {
10412 status = db_copy_json_array(hDB, hKey, buffer, buffer_size, buffer_end);
10413
10414 if (status != DB_SUCCESS)
10415 return status;
10416 }
10417
10418 return DB_SUCCESS;
10419}
10420
10421INT db_copy_json_ls(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
10422{
10423 int status;
10424 bool unlock = false;
10425
10426 if (!rpc_is_remote()) {
10428 if (status != DB_SUCCESS) {
10429 return status;
10430 }
10431 unlock = true;
10432 }
10433
10435
10436 if (unlock) {
10438 }
10439
10440 return status;
10441}
10442
10444{
10445 int status;
10447 if (omit_names)
10448 flags |= JSFLAG_OMIT_NAMES;
10450 flags |= JSFLAG_OMIT_LAST_WRITTEN;
10452 flags |= JSFLAG_OMIT_OLD;
10453 if (!preserve_case)
10454 flags |= JSFLAG_LOWERCASE;
10455
10456 bool unlock = false;
10457
10458 if (!rpc_is_remote()) {
10460 if (status != DB_SUCCESS) {
10461 return status;
10462 }
10463 unlock = true;
10464 }
10465
10466 status = json_write_anything(hDB, hKey, buffer, buffer_size, buffer_end, JS_LEVEL_0, 0, flags, omit_old_timestamp);
10467
10468 if (unlock) {
10470 }
10471
10472 return status;
10473}
10474
10475INT db_copy_json_save(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
10476{
10477 int status;
10478 bool unlock = false;
10479
10480 if (!rpc_is_remote()) {
10482 if (status != DB_SUCCESS) {
10483 return status;
10484 }
10485 unlock = true;
10486 }
10487
10489
10490 if (unlock) {
10492 }
10493
10494 return status;
10495}
10496
10497/********************************************************************/
10508INT db_copy_json_obsolete(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end, int save_keys, int follow_links, int recurse)
10509{
10511 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10512 return DB_SUCCESS;
10513}
10514
10515/********************************************************************/
10527INT db_save_json(HNDLE hDB, HNDLE hKey, const char *filename, int flags)
10528{
10529 INT status;
10530
10531 /* open file */
10532 FILE *fp = fopen(filename, "w");
10533 if (fp == NULL) {
10534 cm_msg(MERROR, "db_save_json", "Cannot open file \"%s\", fopen() errno %d (%s)", filename, errno, strerror(errno));
10535 return DB_FILE_ERROR;
10536 }
10537
10538 bool unlock = false;
10539
10540 if (!rpc_is_remote()) {
10542 if (status != DB_SUCCESS) {
10543 return status;
10544 }
10545 unlock = true;
10546 }
10547
10548 std::string path = db_get_path(hDB, hKey);
10549
10550 bool emptySubdir = false;
10551 HNDLE hSubKey;
10554 emptySubdir = true;
10555
10556 char* buffer = NULL;
10557 int buffer_size = 0;
10558 int buffer_end = 0;
10559
10560 json_write(&buffer, &buffer_size, &buffer_end, 0, "{\n", 0);
10561
10562 json_write(&buffer, &buffer_size, &buffer_end, 1, "/MIDAS version", 1);
10563 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10564 json_write(&buffer, &buffer_size, &buffer_end, 0, MIDAS_VERSION, 1);
10565 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10566
10567 json_write(&buffer, &buffer_size, &buffer_end, 1, "/MIDAS git revision", 1);
10568 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10569 json_write(&buffer, &buffer_size, &buffer_end, 0, GIT_REVISION, 1);
10570 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10571
10572 json_write(&buffer, &buffer_size, &buffer_end, 1, "/filename", 1);
10573 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10574 json_write(&buffer, &buffer_size, &buffer_end, 0, filename, 1);
10575 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10576
10577 json_write(&buffer, &buffer_size, &buffer_end, 1, "/ODB path", 1);
10578 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10579 json_write(&buffer, &buffer_size, &buffer_end, 0, path.c_str(), 1);
10580
10581 if (emptySubdir)
10582 json_write(&buffer, &buffer_size, &buffer_end, 0, "", 0);
10583 else
10584 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10585
10586 //status = db_save_json_key_obsolete(hDB, hKey, -1, &buffer, &buffer_size, &buffer_end, 1, 0, 1);
10587 status = json_write_bare_subdir(hDB, hKey, &buffer, &buffer_size, &buffer_end, JS_LEVEL_1, flags, 0);
10588
10589 json_write(&buffer, &buffer_size, &buffer_end, 0, "\n}\n", 0);
10590
10591 if (unlock) {
10593 }
10594
10595 if (status == DB_SUCCESS) {
10596 if (buffer) {
10597 size_t wr = fwrite(buffer, 1, buffer_end, fp);
10598 if (wr != (size_t)buffer_end) {
10599 cm_msg(MERROR, "db_save_json", "Cannot write to file \"%s\", fwrite() errno %d (%s)", filename, errno, strerror(errno));
10600 free(buffer);
10601 fclose(fp);
10602 return DB_FILE_ERROR;
10603 }
10604 }
10605 }
10606
10607 if (buffer)
10608 free(buffer);
10609
10610 fclose(fp);
10611
10612 return DB_SUCCESS;
10613}
10614
10615/********************************************************************/
10627{
10628 KEY key;
10629 char str[100], line[10+100];
10630 INT status, i, fh;
10631 int wr, size;
10632
10633 /* open file */
10634 fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
10635
10636 if (fh == -1) {
10637 cm_msg(MERROR, "db_save_struct", "Cannot open file\"%s\"", file_name);
10638 return DB_FILE_ERROR;
10639 }
10640
10642 if (status != DB_SUCCESS) {
10643 cm_msg(MERROR, "db_save_struct", "cannot find key");
10644 return DB_INVALID_HANDLE;
10645 }
10646
10647 sprintf(line, "typedef struct {\n");
10648
10649 size = strlen(line);
10650 wr = write(fh, line, size);
10651 if (wr != size) {
10652 cm_msg(MERROR, "db_save_struct", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10653 close(fh);
10654 return DB_FILE_ERROR;
10655 }
10656
10658
10659 if (struct_name && struct_name[0])
10660 mstrlcpy(str, struct_name, sizeof(str));
10661 else
10662 mstrlcpy(str, key.name, sizeof(str));
10663
10664 name2c(str);
10665 for (i = 0; i < (int) strlen(str); i++)
10666 str[i] = (char) toupper(str[i]);
10667
10668 sprintf(line, "} %s;\n\n", str);
10669
10670 size = strlen(line);
10671 wr = write(fh, line, size);
10672 if (wr != size) {
10673 cm_msg(MERROR, "db_save_struct", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10674 close(fh);
10675 return DB_FILE_ERROR;
10676 }
10677
10678 close(fh);
10679
10680 return DB_SUCCESS;
10681}
10682
10684#ifndef DOXYGEN_SHOULD_SKIP_THIS
10685
10686/*------------------------------------------------------------------*/
10688/********************************************************************\
10689
10690 Routine: db_save_string
10691
10692 Purpose: Save a branch of a database as a string which can be used
10693 by db_create_record.
10694
10695 Input:
10696 HNDLE hDB Handle to the database
10697 HNDLE hKey Handle of key to start, 0 for root
10698 int fh File handle to write to
10699 char string_name Name of string. If struct_name == NULL,
10700 the name of the key is used.
10701
10702 Output:
10703 none
10704
10705 Function value:
10706 DB_SUCCESS Successful completion
10707 DB_INVALID_HANDLE Database handle is invalid
10708
10709\********************************************************************/
10710{
10711 KEY key;
10712 char str[256], line[50+256];
10713 INT status, i, size, fh, buffer_size;
10714 char *buffer = NULL, *pc;
10715 int wr;
10716
10717
10718 /* open file */
10719 fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
10720
10721 if (fh == -1) {
10722 cm_msg(MERROR, "db_save_string", "Cannot open file\"%s\"", file_name);
10723 return DB_FILE_ERROR;
10724 }
10725
10727 if (status != DB_SUCCESS) {
10728 cm_msg(MERROR, "db_save_string", "cannot find key");
10729 return DB_INVALID_HANDLE;
10730 }
10731
10732 if (string_name && string_name[0])
10733 strcpy(str, string_name);
10734 else
10735 strcpy(str, key.name);
10736
10737 name2c(str);
10738 for (i = 0; i < (int) strlen(str); i++)
10739 str[i] = (char) toupper(str[i]);
10740
10741 sprintf(line, "#define %s(_name) const char *_name[] = {\\\n", str);
10742 size = strlen(line);
10743 wr = write(fh, line, size);
10744 if (wr != size) {
10745 cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10746 close(fh);
10747 if (buffer)
10748 free(buffer);
10749 return DB_FILE_ERROR;
10750 }
10751
10752 buffer_size = 10000;
10753 do {
10754 buffer = (char *) malloc(buffer_size);
10755 if (buffer == NULL) {
10756 cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
10757 break;
10758 }
10759
10760 size = buffer_size;
10761 status = db_copy(hDB, hKey, buffer, &size, "");
10762 if (status != DB_TRUNCATED)
10763 break;
10764
10765 /* increase buffer size if truncated */
10766 free(buffer);
10767 buffer = NULL;
10768 buffer_size *= 2;
10769 } while (1);
10770
10771
10772 pc = buffer;
10773
10774 do {
10775 i = 0;
10776 line[i++] = '"';
10777 while (*pc != '\n' && *pc != 0) {
10778 if (*pc == '\"' || *pc == '\'')
10779 line[i++] = '\\';
10780 line[i++] = *pc++;
10781 }
10782 strcpy(&line[i], "\",\\\n");
10783 if (i > 0) {
10784 size = strlen(line);
10785 wr = write(fh, line, size);
10786 if (wr != size) {
10787 cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10788 close(fh);
10789 if (buffer)
10790 free(buffer);
10791 return DB_FILE_ERROR;
10792 }
10793 }
10794
10795 if (*pc == '\n')
10796 pc++;
10797
10798 } while (*pc);
10799
10800 sprintf(line, "NULL }\n\n");
10801 size = strlen(line);
10802 wr = write(fh, line, size);
10803 if (wr != size) {
10804 cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10805 close(fh);
10806 if (buffer)
10807 free(buffer);
10808 return DB_FILE_ERROR;
10809 }
10810
10811 close(fh);
10812 free(buffer);
10813
10814 return DB_SUCCESS;
10815}
10816
10818#endif /* DOXYGEN_SHOULD_SKIP_THIS */
10819
10820/********************************************************************/
10843INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
10844{
10845 if (data_size == 0)
10846 sprintf(string, "<NULL>");
10847 else
10848 switch (type) {
10849 case TID_UINT8:
10850 sprintf(string, "%d", *(((BYTE *) data) + idx));
10851 break;
10852 case TID_INT8:
10853 sprintf(string, "%d", *(((char *) data) + idx));
10854 break;
10855 case TID_CHAR:
10856 sprintf(string, "%c", *(((char *) data) + idx));
10857 break;
10858 case TID_UINT16:
10859 sprintf(string, "%u", *(((WORD *) data) + idx));
10860 break;
10861 case TID_INT16:
10862 sprintf(string, "%d", *(((short *) data) + idx));
10863 break;
10864 case TID_UINT32:
10865 sprintf(string, "%u", *(((DWORD *) data) + idx));
10866 break;
10867 case TID_INT32:
10868 sprintf(string, "%d", *(((INT *) data) + idx));
10869 break;
10870 case TID_UINT64:
10871 sprintf(string, "%llu", *(((UINT64 *) data) + idx));
10872 break;
10873 case TID_INT64:
10874 sprintf(string, "%lld", *(((INT64 *) data) + idx));
10875 break;
10876 case TID_BOOL:
10877 sprintf(string, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
10878 break;
10879 case TID_FLOAT:
10880 if (ss_isnan(*(((float *) data) + idx)))
10881 sprintf(string, "NAN");
10882 else
10883 sprintf(string, "%.7g", *(((float *) data) + idx));
10884 break;
10885 case TID_DOUBLE:
10886 if (ss_isnan(*(((double *) data) + idx)))
10887 sprintf(string, "NAN");
10888 else
10889 sprintf(string, "%.16lg", *(((double *) data) + idx));
10890 break;
10891 case TID_BITFIELD:
10892 sprintf(string, "%u", *(((DWORD *) data) + idx));
10893 break;
10894 case TID_STRING:
10895 case TID_LINK:
10896 mstrlcpy(string, ((char *) data) + data_size * idx, MAX_STRING_LENGTH);
10897 break;
10898 default:
10899 sprintf(string, "<unknown>");
10900 break;
10901 }
10902
10903 return DB_SUCCESS;
10904}
10905
10906/********************************************************************/
10919INT db_sprintff(char *string, const char *format, const void *data, INT data_size, INT idx, DWORD type)
10920{
10921 if (data_size == 0)
10922 sprintf(string, "<NULL>");
10923 else
10924 switch (type) {
10925 case TID_UINT8:
10926 sprintf(string, format, *(((BYTE *) data) + idx));
10927 break;
10928 case TID_INT8:
10929 sprintf(string, format, *(((char *) data) + idx));
10930 break;
10931 case TID_CHAR:
10932 sprintf(string, format, *(((char *) data) + idx));
10933 break;
10934 case TID_UINT16:
10935 sprintf(string, format, *(((WORD *) data) + idx));
10936 break;
10937 case TID_INT16:
10938 sprintf(string, format, *(((short *) data) + idx));
10939 break;
10940 case TID_UINT32:
10941 sprintf(string, format, *(((DWORD *) data) + idx));
10942 break;
10943 case TID_INT32:
10944 sprintf(string, format, *(((INT *) data) + idx));
10945 break;
10946 case TID_UINT64:
10947 sprintf(string, format, *(((UINT64 *) data) + idx));
10948 break;
10949 case TID_INT64:
10950 sprintf(string, format, *(((INT64 *) data) + idx));
10951 break;
10952 case TID_BOOL:
10953 sprintf(string, format, *(((BOOL *) data) + idx) ? 'y' : 'n');
10954 break;
10955 case TID_FLOAT:
10956 if (ss_isnan(*(((float *) data) + idx)))
10957 sprintf(string, "NAN");
10958 else
10959 sprintf(string, format, *(((float *) data) + idx));
10960 break;
10961 case TID_DOUBLE:
10962 if (ss_isnan(*(((double *) data) + idx)))
10963 sprintf(string, "NAN");
10964 else
10965 sprintf(string, format, *(((double *) data) + idx));
10966 break;
10967 case TID_BITFIELD:
10968 sprintf(string, format, *(((DWORD *) data) + idx));
10969 break;
10970 case TID_STRING:
10971 case TID_LINK:
10972 mstrlcpy(string, ((char *) data) + data_size * idx, MAX_STRING_LENGTH);
10973 break;
10974 default:
10975 sprintf(string, "<unknown>");
10976 break;
10977 }
10978
10979 return DB_SUCCESS;
10980}
10981
10982/*------------------------------------------------------------------*/
10983INT db_sprintfh(char *string, const void *data, INT data_size, INT idx, DWORD type)
10984/********************************************************************\
10985
10986 Routine: db_sprintfh
10987
10988 Purpose: Convert a database value to a string according to its type
10989 in hex format
10990
10991 Input:
10992 void *data Value data
10993 INT idx Index for array data
10994 INT data_size Size of single data element
10995 DWORD type Valye type, one of TID_xxx
10996
10997 Output:
10998 char *string ASCII string of data
10999
11000 Function value:
11001 DB_SUCCESS Successful completion
11002
11003\********************************************************************/
11004{
11005 if (data_size == 0)
11006 sprintf(string, "<NULL>");
11007 else
11008 switch (type) {
11009 case TID_UINT8:
11010 sprintf(string, "0x%X", *(((BYTE *) data) + idx));
11011 break;
11012 case TID_INT8:
11013 sprintf(string, "0x%X", *(((char *) data) + idx));
11014 break;
11015 case TID_CHAR:
11016 sprintf(string, "%c", *(((char *) data) + idx));
11017 break;
11018 case TID_UINT16:
11019 sprintf(string, "0x%X", *(((WORD *) data) + idx));
11020 break;
11021 case TID_INT16:
11022 sprintf(string, "0x%hX", *(((short *) data) + idx));
11023 break;
11024 case TID_UINT32:
11025 sprintf(string, "0x%X", *(((DWORD *) data) + idx));
11026 break;
11027 case TID_INT32:
11028 sprintf(string, "0x%X", *(((INT *) data) + idx));
11029 break;
11030 case TID_UINT64:
11031 sprintf(string, "0x%llX", *(((UINT64 *) data) + idx));
11032 break;
11033 case TID_INT64:
11034 sprintf(string, "0x%llX", *(((INT64 *) data) + idx));
11035 break;
11036 case TID_BOOL:
11037 sprintf(string, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11038 break;
11039 case TID_FLOAT:
11040 if (ss_isnan(*(((float *) data) + idx)))
11041 sprintf(string, "NAN");
11042 else
11043 sprintf(string, "%.7g", *(((float *) data) + idx));
11044 break;
11045 case TID_DOUBLE:
11046 if (ss_isnan(*(((double *) data) + idx)))
11047 sprintf(string, "NAN");
11048 else
11049 sprintf(string, "%.16lg", *(((double *) data) + idx));
11050 break;
11051 case TID_BITFIELD:
11052 sprintf(string, "0x%X", *(((DWORD *) data) + idx));
11053 break;
11054 case TID_STRING:
11055 case TID_LINK:
11056 sprintf(string, "%s", ((char *) data) + data_size * idx);
11057 break;
11058 default:
11059 sprintf(string, "<unknown>");
11060 break;
11061 }
11062
11063 return DB_SUCCESS;
11064}
11065
11066/********************************************************************/
11087std::string db_sprintf(const void *data, INT data_size, INT idx, DWORD type)
11088{
11089 if (data_size == 0) {
11090 return "<NULL>";
11091 } else {
11092 char buf[256];
11093 switch (type) {
11094 case TID_UINT8:
11095 sprintf(buf, "%d", *(((BYTE *) data) + idx));
11096 return buf;
11097 case TID_INT8:
11098 sprintf(buf, "%d", *(((char *) data) + idx));
11099 return buf;
11100 case TID_CHAR:
11101 sprintf(buf, "%c", *(((char *) data) + idx));
11102 return buf;
11103 case TID_UINT16:
11104 sprintf(buf, "%u", *(((WORD *) data) + idx));
11105 return buf;
11106 case TID_INT16:
11107 sprintf(buf, "%d", *(((short *) data) + idx));
11108 return buf;
11109 case TID_UINT32:
11110 sprintf(buf, "%u", *(((DWORD *) data) + idx));
11111 return buf;
11112 case TID_INT32:
11113 sprintf(buf, "%d", *(((INT *) data) + idx));
11114 return buf;
11115 case TID_UINT64:
11116 sprintf(buf, "%llu", *(((UINT64 *) data) + idx));
11117 return buf;
11118 case TID_INT64:
11119 sprintf(buf, "%lld", *(((INT64 *) data) + idx));
11120 return buf;
11121 case TID_BOOL:
11122 sprintf(buf, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11123 return buf;
11124 case TID_FLOAT:
11125 if (ss_isnan(*(((float *) data) + idx))) {
11126 return "NAN";
11127 } else {
11128 sprintf(buf, "%.7g", *(((float *) data) + idx));
11129 return buf;
11130 }
11131 case TID_DOUBLE:
11132 if (ss_isnan(*(((double *) data) + idx))) {
11133 return "NAN";
11134 } else {
11135 sprintf(buf, "%.16lg", *(((double *) data) + idx));
11136 return buf;
11137 }
11138 case TID_BITFIELD:
11139 sprintf(buf, "%u", *(((DWORD *) data) + idx));
11140 return buf;
11141 case TID_STRING:
11142 case TID_LINK:
11143 return (((char *) data) + data_size * idx);
11144 default:
11145 return "<unknown>";
11146 }
11147 }
11148}
11149
11150/********************************************************************/
11163std::string db_sprintff(const char *format, const void *data, INT data_size, INT idx, DWORD type)
11164{
11165 if (data_size == 0) {
11166 return "<NULL>";
11167 } else {
11168 char buf[256];
11169 switch (type) {
11170 case TID_UINT8:
11171 sprintf(buf, format, *(((BYTE *) data) + idx));
11172 return buf;
11173 case TID_INT8:
11174 sprintf(buf, format, *(((char *) data) + idx));
11175 return buf;
11176 case TID_CHAR:
11177 sprintf(buf, format, *(((char *) data) + idx));
11178 return buf;
11179 case TID_UINT16:
11180 sprintf(buf, format, *(((WORD *) data) + idx));
11181 return buf;
11182 case TID_INT16:
11183 sprintf(buf, format, *(((short *) data) + idx));
11184 return buf;
11185 case TID_UINT32:
11186 sprintf(buf, format, *(((DWORD *) data) + idx));
11187 return buf;
11188 case TID_INT32:
11189 sprintf(buf, format, *(((INT *) data) + idx));
11190 return buf;
11191 case TID_UINT64:
11192 sprintf(buf, format, *(((UINT64 *) data) + idx));
11193 return buf;
11194 case TID_INT64:
11195 sprintf(buf, format, *(((INT64 *) data) + idx));
11196 return buf;
11197 case TID_BOOL:
11198 sprintf(buf, format, *(((BOOL *) data) + idx) ? 'y' : 'n');
11199 return buf;
11200 case TID_FLOAT:
11201 if (ss_isnan(*(((float *) data) + idx))) {
11202 return "NAN";
11203 } else {
11204 sprintf(buf, format, *(((float *) data) + idx));
11205 return buf;
11206 }
11207 case TID_DOUBLE:
11208 if (ss_isnan(*(((double *) data) + idx))) {
11209 return "NAN";
11210 } else {
11211 sprintf(buf, format, *(((double *) data) + idx));
11212 return buf;
11213 }
11214 case TID_BITFIELD:
11215 sprintf(buf, format, *(((DWORD *) data) + idx));
11216 return buf;
11217 case TID_STRING:
11218 case TID_LINK:
11219 return (((char *) data) + data_size * idx);
11220 default:
11221 return "<unknown>";
11222 }
11223 }
11224}
11225
11227#ifndef DOXYGEN_SHOULD_SKIP_THIS
11228
11229/*------------------------------------------------------------------*/
11230std::string db_sprintfh(const void *data, INT data_size, INT idx, DWORD type)
11231/********************************************************************\
11232
11233 Routine: db_sprintfh
11234
11235 Purpose: Convert a database value to a string according to its type
11236 in hex format
11237
11238 Input:
11239 void *data Value data
11240 INT idx Index for array data
11241 INT data_size Size of single data element
11242 DWORD type Valye type, one of TID_xxx
11243
11244 Output:
11245 char *string ASCII string of data
11246
11247 Function value:
11248 DB_SUCCESS Successful completion
11249
11250\********************************************************************/
11251{
11252 if (data_size == 0) {
11253 return "<NULL>";
11254 } else {
11255 char buf[256];
11256 switch (type) {
11257 case TID_UINT8:
11258 sprintf(buf, "0x%X", *(((BYTE *) data) + idx));
11259 return buf;
11260 case TID_INT8:
11261 sprintf(buf, "0x%X", *(((char *) data) + idx));
11262 return buf;
11263 case TID_CHAR:
11264 sprintf(buf, "%c", *(((char *) data) + idx));
11265 return buf;
11266 case TID_UINT16:
11267 sprintf(buf, "0x%X", *(((WORD *) data) + idx));
11268 return buf;
11269 case TID_INT16:
11270 sprintf(buf, "0x%hX", *(((short *) data) + idx));
11271 return buf;
11272 case TID_UINT32:
11273 sprintf(buf, "0x%X", *(((DWORD *) data) + idx));
11274 return buf;
11275 case TID_INT32:
11276 sprintf(buf, "0x%X", *(((INT *) data) + idx));
11277 return buf;
11278 case TID_UINT64:
11279 sprintf(buf, "0x%llX", *(((UINT64 *) data) + idx));
11280 return buf;
11281 case TID_INT64:
11282 sprintf(buf, "0x%llX", *(((INT64 *) data) + idx));
11283 return buf;
11284 case TID_BOOL:
11285 sprintf(buf, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11286 return buf;
11287 case TID_FLOAT:
11288 if (ss_isnan(*(((float *) data) + idx))) {
11289 return "NAN";
11290 } else {
11291 sprintf(buf, "%.7g", *(((float *) data) + idx));
11292 return buf;
11293 }
11294 case TID_DOUBLE:
11295 if (ss_isnan(*(((double *) data) + idx))) {
11296 return "NAN";
11297 } else {
11298 sprintf(buf, "%.16lg", *(((double *) data) + idx));
11299 return buf;
11300 }
11301 case TID_BITFIELD:
11302 sprintf(buf, "0x%X", *(((DWORD *) data) + idx));
11303 return buf;
11304 case TID_STRING:
11305 case TID_LINK:
11306 return (((char *) data) + data_size * idx);
11307 default:
11308 return "<unknown>";
11309 }
11310 }
11311}
11312
11313/*------------------------------------------------------------------*/
11314INT db_sscanf(const char *data_str, void *data, INT * data_size, INT i, DWORD tid)
11315/********************************************************************\
11316
11317 Routine: db_sscanf
11318
11319 Purpose: Convert a string to a database value according to its type
11320
11321 Input:
11322 char *data_str ASCII string of data
11323 INT i Index for array data
11324 DWORD tid Value type, one of TID_xxx
11325
11326 Output:
11327 void *data Value data
11328 INT *data_size Size of single data element
11329
11330 Function value:
11331 DB_SUCCESS Successful completion
11332
11333\********************************************************************/
11334{
11335 DWORD value = 0;
11336 BOOL hex = FALSE;
11337
11338 if (data_str == NULL)
11339 return 0;
11340
11341 *data_size = rpc_tid_size(tid);
11342 if (strncmp(data_str, "0x", 2) == 0) {
11343 hex = TRUE;
11344 sscanf(data_str + 2, "%x", &value);
11345 }
11346
11347 switch (tid) {
11348 case TID_UINT8:
11349 case TID_INT8:
11350 if (hex)
11351 *((char *) data + i) = (char) value;
11352 else
11353 *((char *) data + i) = (char) atoi(data_str);
11354 break;
11355 case TID_CHAR:
11356 *((char *) data + i) = data_str[0];
11357 break;
11358 case TID_UINT16:
11359 if (hex)
11360 *((WORD *) data + i) = (WORD) value;
11361 else
11362 *((WORD *) data + i) = (WORD) atoi(data_str);
11363 break;
11364 case TID_INT16:
11365 if (hex)
11366 *((short int *) data + i) = (short int) value;
11367 else
11368 *((short int *) data + i) = (short int) atoi(data_str);
11369 break;
11370 case TID_UINT32:
11371 if (hex)
11372 value = strtoul(data_str, nullptr, 16);
11373 else
11374 value = strtoul(data_str, nullptr, 10);
11375
11376 *((DWORD *) data + i) = value;
11377 break;
11378 case TID_INT32:
11379 if (hex)
11380 value = strtol(data_str, nullptr, 16);
11381 else
11382 value = strtol(data_str, nullptr, 10);
11383
11384 *((INT *) data + i) = value;
11385 break;
11386 case TID_UINT64:
11387 if (hex)
11388 *((UINT64 *) data + i) = strtoull(data_str, nullptr, 16);
11389 else
11390 *((UINT64 *) data + i) = strtoull(data_str, nullptr, 10);
11391 break;
11392 case TID_INT64:
11393 if (hex)
11394 *((UINT64 *) data + i) = strtoll(data_str, nullptr, 16);
11395 else
11396 *((UINT64 *) data + i) = strtoll(data_str, nullptr, 10);
11397 break;
11398 case TID_BOOL:
11399 if (data_str[0] == 'y' || data_str[0] == 'Y' ||
11400 data_str[0] == 't' || data_str[0] == 'T' || atoi(data_str) > 0)
11401 *((BOOL *) data + i) = 1;
11402 else
11403 *((BOOL *) data + i) = 0;
11404 break;
11405 case TID_FLOAT:
11406 if (data_str[0] == 'n' || data_str[0] == 'N')
11407 *((float *) data + i) = (float) ss_nan();
11408 else
11409 *((float *) data + i) = (float) atof(data_str);
11410 break;
11411 case TID_DOUBLE:
11412 if (data_str[0] == 'n' || data_str[0] == 'N')
11413 *((double *) data + i) = ss_nan();
11414 else
11415 *((double *) data + i) = atof(data_str);
11416 break;
11417 case TID_BITFIELD:
11418 if (hex)
11419 value = strtoul(data_str, nullptr, 16);
11420 else
11421 value = strtoul(data_str, nullptr, 10);
11422
11423 *((DWORD *) data + i) = value;
11424 break;
11425 case TID_STRING:
11426 case TID_LINK:
11427 strcpy((char *) data, data_str);
11428 *data_size = strlen(data_str) + 1;
11429 break;
11430 }
11431
11432 return DB_SUCCESS;
11433}
11434
11435/*------------------------------------------------------------------*/
11436
11437#ifdef LOCAL_ROUTINES
11438
11439static void db_recurse_record_tree_locked(HNDLE hDB, const DATABASE_HEADER* pheader, const KEY* pkey, void **data, INT * total_size, INT base_align, INT * max_align, BOOL bSet, INT convert_flags, db_err_msg** msg)
11440/********************************************************************\
11441
11442 Routine: db_recurse_record_tree_locked
11443
11444 Purpose: Recurse a database tree and calculate its size or copy
11445 data. Internal use only.
11446
11447\********************************************************************/
11448{
11449 const KEY *pold;
11450 INT size, align, corr, total_size_tmp;
11451
11452 KEYLIST *pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
11453 if (!pkeylist->first_key)
11454 return;
11455 // FIXME: validate pkeylist->first_key
11456 pkey = (const KEY *) ((char *) pheader + pkeylist->first_key);
11457
11458 /* first browse through this level */
11459 do {
11460 pold = NULL;
11461
11462 if (pkey->type == TID_LINK) {
11463 const KEY *plink = db_resolve_link_locked(pheader, pkey, NULL, msg);
11464
11465 if (!plink)
11466 return;
11467
11468 if (plink->type == TID_KEY) {
11469 db_recurse_record_tree_locked(hDB, pheader, plink, data, total_size, base_align, NULL, bSet, convert_flags, msg);
11470 } else {
11471 pold = pkey;
11472 pkey = plink;
11473 }
11474 }
11475
11476 if (pkey->type != TID_KEY) {
11477 /* correct for alignment */
11478 align = 1;
11479
11480 if (rpc_tid_size(pkey->type))
11481 align = rpc_tid_size(pkey->type) < base_align ? rpc_tid_size(pkey->type) : base_align;
11482
11483 if (max_align && align > *max_align)
11484 *max_align = align;
11485
11486 corr = VALIGN(*total_size, align) - *total_size;
11487 *total_size += corr;
11488 if (data)
11489 *data = (void *) ((char *) (*data) + corr);
11490
11491 /* calculate data size */
11492 size = pkey->item_size * pkey->num_values;
11493
11494 if (data) {
11495 if (bSet) {
11496 KEY* wpkey = (KEY*)pkey;
11497 /* copy data if there is write access */
11498 if (pkey->access_mode & MODE_WRITE) {
11499 memcpy((char *) pheader + pkey->data, *data, pkey->item_size * pkey->num_values);
11500
11501 /* convert data */
11502 if (convert_flags) {
11503 if (pkey->num_values > 1)
11504 rpc_convert_data((char *) pheader + pkey->data,
11505 pkey->type, RPC_FIXARRAY, pkey->item_size * pkey->num_values, convert_flags);
11506 else
11507 rpc_convert_single((char *) pheader + pkey->data, pkey->type, 0, convert_flags);
11508 }
11509
11510 /* update time */
11511 wpkey->last_written = ss_time();
11512
11513 /* notify clients which have key open */
11514 db_notify_clients_locked(pheader, hDB, db_pkey_to_hkey(pheader, pkey), -1, TRUE, msg);
11515 }
11516 } else {
11517 /* copy key data if there is read access */
11518 if (pkey->access_mode & MODE_READ) {
11519 memcpy(*data, (char *) pheader + pkey->data, pkey->item_size * pkey->num_values);
11520
11521 /* convert data */
11522 if (convert_flags) {
11523 if (pkey->num_values > 1)
11524 rpc_convert_data(*data, pkey->type,
11526 pkey->item_size * pkey->num_values, convert_flags);
11527 else
11528 rpc_convert_single(*data, pkey->type, RPC_OUTGOING, convert_flags);
11529 }
11530 }
11531 }
11532
11533 *data = (char *) (*data) + size;
11534 }
11535
11536 *total_size += size;
11537 } else {
11538 /* align new substructure according to the maximum
11539 align value in this structure */
11540 align = 1;
11541
11542 total_size_tmp = *total_size;
11543 db_recurse_record_tree_locked(hDB, pheader, pkey, NULL, &total_size_tmp, base_align, &align, bSet, convert_flags, msg);
11544
11545 if (max_align && align > *max_align)
11546 *max_align = align;
11547
11548 corr = VALIGN(*total_size, align) - *total_size;
11549 *total_size += corr;
11550 if (data)
11551 *data = (void *) ((char *) (*data) + corr);
11552
11553 /* now recurse subtree */
11554 db_recurse_record_tree_locked(hDB, pheader, pkey, data, total_size, base_align, NULL, bSet, convert_flags, msg);
11555
11556 corr = VALIGN(*total_size, align) - *total_size;
11557 *total_size += corr;
11558 if (data)
11559 *data = (void *) ((char *) (*data) + corr);
11560 }
11561
11562 if (pold) {
11563 pkey = pold;
11564 pold = NULL;
11565 }
11566
11567 if (!pkey->next_key)
11568 break;
11569
11570 // FIXME: validate pkey->next_key
11571 pkey = (KEY *) ((char *) pheader + pkey->next_key);
11572 } while (TRUE);
11573}
11574
11575static void db_recurse_record_tree_locked(HNDLE hDB, HNDLE hKey, void **data, INT * total_size, INT base_align, INT * max_align, BOOL bSet, INT convert_flags, db_err_msg** msg)
11576/********************************************************************\
11577
11578 Routine: db_recurse_record_tree_locked
11579
11580 Purpose: Recurse a database tree and calculate its size or copy
11581 data. Internal use only.
11582
11583\********************************************************************/
11584{
11585 /* get first subkey of hKey */
11587
11588 const KEY* pkey = db_get_pkey(pheader, hKey, NULL, "db_recurse_record_tree", msg);
11589
11590 if (!pkey) {
11591 return;
11592 }
11593
11594 db_recurse_record_tree_locked(hDB, pheader, pkey, data, total_size, base_align, max_align, bSet, convert_flags, msg);
11595}
11596
11597#endif /* LOCAL_ROUTINES */
11598
11600#endif /* DOXYGEN_SHOULD_SKIP_THIS */
11601
11602/********************************************************************/
11616{
11617 if (rpc_is_remote()) {
11618 align = ss_get_struct_align();
11619 return rpc_call(RPC_DB_GET_RECORD_SIZE, hDB, hKey, align, buf_size);
11620 }
11621#ifdef LOCAL_ROUTINES
11622 {
11623 KEY key;
11625
11626 if (!align)
11627 align = ss_get_struct_align();
11628
11629 /* check if key has subkeys */
11631 if (status != DB_SUCCESS)
11632 return status;
11633
11634 if (key.type != TID_KEY) {
11635 /* just a single key */
11636 *buf_size = key.item_size * key.num_values;
11637 return DB_SUCCESS;
11638 }
11639
11640 db_err_msg* msg = NULL;
11642
11643 /* determine record size */
11644 *buf_size = max_align = 0;
11645 db_recurse_record_tree_locked(hDB, hKey, NULL, buf_size, align, &max_align, 0, 0, &msg);
11646
11647 //int tmp = *buf_size;
11648 /* correct for byte padding */
11649 *buf_size = VALIGN(*buf_size, max_align);
11650
11651 //if (tmp != *buf_size) {
11652 // 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);
11653 //}
11654
11656 if (msg)
11657 db_flush_msg(&msg);
11658 }
11659#endif /* LOCAL_ROUTINES */
11660
11661 return DB_SUCCESS;
11662}
11663
11664/********************************************************************/
11709INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align)
11710{
11711 if (rpc_is_remote()) {
11712 align = ss_get_struct_align();
11713 return rpc_call(RPC_DB_GET_RECORD, hDB, hKey, data, buf_size, align);
11714 }
11715#ifdef LOCAL_ROUTINES
11716 {
11717 KEY key;
11718 INT convert_flags, status;
11719 INT total_size;
11720 void *pdata;
11721
11722 if (data && buf_size) {
11723 memset(data, 0x00, *buf_size);
11724 }
11725
11726 convert_flags = 0;
11727
11728 if (!align)
11729 align = ss_get_struct_align();
11730 else {
11731 /* only convert data if called remotely, as indicated by align != 0 */
11732 if (rpc_is_mserver()) {
11733 convert_flags = rpc_get_convert_flags();
11734 }
11735 }
11736
11737 /* check if key has subkeys */
11739 if (status != DB_SUCCESS)
11740 return status;
11741
11742 if (key.type != TID_KEY) {
11743 /* copy single key */
11744 if (key.item_size * key.num_values != *buf_size) {
11745 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, key.item_size * key.num_values);
11747 }
11748
11749 db_get_data(hDB, hKey, data, buf_size, key.type);
11750
11751 if (convert_flags) {
11752 if (key.num_values > 1)
11754 else
11755 rpc_convert_single(data, key.type, RPC_OUTGOING, convert_flags);
11756 }
11757
11758 return DB_SUCCESS;
11759 }
11760
11761 /* check record size */
11762 db_get_record_size(hDB, hKey, align, &total_size);
11763 if (total_size != *buf_size) {
11764 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);
11766 }
11767
11768 /* get subkey data */
11769 pdata = data;
11770 total_size = 0;
11771
11772 db_err_msg* msg = NULL;
11774 db_recurse_record_tree_locked(hDB, hKey, &pdata, &total_size, align, NULL, FALSE, convert_flags, &msg);
11776 if (msg)
11777 db_flush_msg(&msg);
11778
11779 //if (total_size != *buf_size) {
11780 // 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);
11781 // //return DB_STRUCT_SIZE_MISMATCH;
11782 //}
11783 }
11784#endif /* LOCAL_ROUTINES */
11785
11786 return DB_SUCCESS;
11787}
11788
11789/********************************************************************/
11805INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align, const char *rec_str)
11806{
11807 int size = *buf_size;
11808 int odb_size = 0;
11809 int status;
11810 //char path[MAX_ODB_PATH];
11811
11812 /* check record size first */
11813
11815 if (status != DB_SUCCESS)
11816 return status;
11817
11818 /* if size mismatch, call repair function */
11819
11820 if (odb_size != size) {
11821 std::string path = db_get_path(hDB, hKey);
11822 cm_msg(MINFO, "db_get_record1", "Fixing ODB \"%s\" struct size mismatch (expected %d, odb size %d)", path.c_str(), size, odb_size);
11824 if (status != DB_SUCCESS)
11825 return status;
11826 }
11827
11828 /* run db_get_record(), if success, we are done */
11829
11830 status = db_get_record(hDB, hKey, data, buf_size, align);
11831 if (status == DB_SUCCESS)
11832 return status;
11833
11834 /* try repair with db_check_record() */
11835
11837 if (status != DB_SUCCESS)
11838 return status;
11839
11840 /* verify struct size again, because there can still be a mismatch if there
11841 * are extra odb entries at the end of the record as db_check_record()
11842 * seems to ignore all odb entries past the end of "rec_str". K.O.
11843 */
11844
11846 if (status != DB_SUCCESS)
11847 return status;
11848
11849 std::string path = db_get_path(hDB, hKey);
11850
11851 if (odb_size != size) {
11852 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());
11854 if (status != DB_SUCCESS)
11855 return status;
11856 }
11857
11858 cm_msg(MERROR, "db_get_record1", "repaired struct size mismatch of \"%s\"", path.c_str());
11859
11860 *buf_size = size;
11861 status = db_get_record(hDB, hKey, data, buf_size, align);
11862
11863 return status;
11864}
11865
11866static 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)
11867{
11868 title[0] = 0;
11869 key_name[0] = 0;
11870 *tid = 0;
11871 *n_data = 0;
11872 *string_length = 0;
11873 *out_rec_str = NULL;
11874
11875 //
11876 // expected format of rec_str:
11877 //
11878 // title: "[.]",
11879 // numeric value: "example_int = INT : 3",
11880 // string value: "example_string = STRING : [20] /Runinfo/Run number",
11881 // array: "aaa = INT[10] : ...",
11882 // string array: "sarr = STRING[10] : [32] ",
11883 //
11884
11885 //printf("parse_rec_str: [%s]\n", rec_str);
11886
11887 while (*rec_str == '\n')
11888 rec_str++;
11889
11890 /* check if it is a section title */
11891 if (rec_str[0] == '[') {
11892 rec_str++;
11893
11894 title[0] = 0;
11895
11896 /* extract title and append '/' */
11897 mstrlcpy(title, rec_str, title_size);
11898 char* p = strchr(title, ']');
11899 if (p)
11900 *p = 0;
11901
11902 int len = strlen(title);
11903 if (len > 0) {
11904 if (title[len - 1] != '/')
11905 mstrlcat(title, "/", title_size);
11906 }
11907
11908 // skip to the next end-of-line
11909 const char* pend = strchr(rec_str, '\n');
11910 if (pend)
11911 rec_str = pend;
11912 else
11914
11915 while (*rec_str == '\n')
11916 rec_str++;
11917
11919 return DB_SUCCESS;
11920 }
11921
11922 if (rec_str[0] == ';') {
11923 // skip to the next end-of-line
11924 const char* pend = strchr(rec_str, '\n');
11925 if (pend)
11926 rec_str = pend;
11927 else
11929
11930 while (*rec_str == '\n')
11931 rec_str++;
11932
11934 return DB_SUCCESS;
11935 }
11936
11937 const char* peq = strchr(rec_str, '=');
11938 if (!peq) {
11939 cm_msg(MERROR, "db_parse_record", "do not see \'=\'");
11940 return DB_INVALID_PARAM;
11941 }
11942
11943 int key_name_len = peq - rec_str;
11944
11945 // remove trailing equals sign and trailing spaces
11946 while (key_name_len > 1) {
11947 if (rec_str[key_name_len-1] == '=') {
11948 key_name_len--;
11949 continue;
11950 }
11951 if (rec_str[key_name_len-1] == ' ') {
11952 key_name_len--;
11953 continue;
11954 }
11955 break;
11956 }
11957
11958 memcpy(key_name, rec_str, key_name_len);
11959 key_name[key_name_len] = 0;
11960
11961 rec_str = peq + 1; // consume the "=" sign
11962
11963 while (*rec_str == ' ') // consume spaces
11964 rec_str++;
11965
11966 // extract type id
11967 char stid[256];
11968 int i;
11969 for (i=0; i<(int)sizeof(stid)-1; i++) {
11970 char s = *rec_str;
11971 if (s == 0) break;
11972 if (s == ' ') break;
11973 if (s == '\n') break;
11974 if (s == '[') break;
11975 stid[i] = s;
11976 rec_str++;
11977 }
11978 stid[i] = 0;
11979
11980 DWORD xtid = 0;
11981 for (xtid = 0; xtid < TID_LAST; xtid++) {
11982 if (strcmp(rpc_tid_name(xtid), stid) == 0) {
11983 *tid = xtid;
11984 break;
11985 }
11986 }
11987
11988 //printf("tid [%s], tid %d\n", stid, *tid);
11989
11990 if (xtid == TID_LAST) {
11991 cm_msg(MERROR, "db_parse_record", "do not see \':\'");
11992 return DB_INVALID_PARAM;
11993 }
11994
11995 while (*rec_str == ' ') // consume spaces
11996 rec_str++;
11997
11998 *n_data = 1;
11999
12000 if (*rec_str == '[') {
12001 // decode array size
12002 rec_str++; // cosume the '['
12003 *n_data = atoi(rec_str);
12004 const char *pbr = strchr(rec_str, ']');
12005 if (!pbr) {
12006 cm_msg(MERROR, "db_parse_record", "do not see closing bracket \']\'");
12007 return DB_INVALID_PARAM;
12008 }
12009 rec_str = pbr + 1; // skip the closing bracket
12010 }
12011
12012 while (*rec_str == ' ') // consume spaces
12013 rec_str++;
12014
12015 const char* pcol = strchr(rec_str, ':');
12016 if (!pcol) {
12017 cm_msg(MERROR, "db_parse_record", "do not see \':\'");
12018 return DB_INVALID_PARAM;
12019 }
12020
12021 rec_str = pcol + 1; // skip the ":"
12022
12023 while (*rec_str == ' ') // consume spaces
12024 rec_str++;
12025
12026 *string_length = 0;
12027 if (xtid == TID_LINK || xtid == TID_STRING) {
12028 // extract string length
12029 const char* pbr = strchr(rec_str, '[');
12030 if (pbr) {
12031 *string_length = atoi(pbr+1);
12032 }
12033 }
12034
12035 // skip to the next end-of-line
12036 const char* pend = strchr(rec_str, '\n');
12037 if (pend)
12038 rec_str = pend;
12039 else
12041
12042 while (*rec_str == '\n')
12043 rec_str++;
12044
12046 return DB_SUCCESS;
12047}
12048
12049static 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)
12050{
12051 assert(tid > 0);
12052 assert(n_data > 0);
12053 int tsize = rpc_tid_size(tid);
12054 int offset = *buf_ptr - buf_start;
12055 int align = 0;
12056 if (tsize && (offset%tsize != 0)) {
12057 while (offset%tsize != 0) {
12058 align++;
12059 *(*buf_ptr) = 0xFF; // pad bytes for correct data alignement
12060 (*buf_ptr)++;
12061 (*buf_remain)--;
12062 offset++;
12063 }
12064 }
12065 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);
12066 if (tsize > 0) {
12067 int xsize = tsize*n_data;
12068 if (xsize > *buf_remain) {
12069 cm_msg(MERROR, "db_get_record2", "buffer overrun at key \"%s\", size %d, buffer remaining %d", key_name, xsize, *buf_remain);
12070 return DB_INVALID_PARAM;
12071 }
12072 int ysize = xsize;
12073 int status = db_get_value(hDB, hKey, key_name, *buf_ptr, &ysize, tid, FALSE);
12074 //printf("status %d, xsize %d\n", status, xsize);
12075 if (status != DB_SUCCESS) {
12076 cm_msg(MERROR, "db_get_record2", "cannot read \"%s\", db_get_value() status %d", key_name, status);
12077 memset(*buf_ptr, 0, xsize);
12078 *buf_ptr += xsize;
12079 *buf_remain -= xsize;
12080 return status;
12081 }
12082 *buf_ptr += xsize;
12083 *buf_remain -= xsize;
12084 } else if (tid == TID_STRING) {
12085 int xstatus = 0;
12086 int i;
12087 for (i=0; i<n_data; i++) {
12088 int xsize = string_length;
12089 if (xsize > *buf_remain) {
12090 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);
12091 return DB_INVALID_PARAM;
12092 }
12093 char xkey_name[MAX_ODB_PATH+100];
12094 sprintf(xkey_name, "%s[%d]", key_name, i);
12096 //printf("status %d, string length %d, xsize %d, actual len %d\n", status, string_length, xsize, (int)strlen(*buf_ptr));
12097 if (status == DB_TRUNCATED) {
12098 // make sure string is NUL terminated
12099 (*buf_ptr)[string_length-1] = 0;
12100 cm_msg(MERROR, "db_get_record2", "string key \"%s\" index %d, string value was truncated", key_name, i);
12101 } else if (status != DB_SUCCESS) {
12102 cm_msg(MERROR, "db_get_record2", "cannot read string \"%s\"[%d], db_get_value() status %d", key_name, i, status);
12104 xstatus = status;
12105 }
12108 }
12109 if (xstatus != 0) {
12110 return xstatus;
12111 }
12112 } else {
12113 cm_msg(MERROR, "db_get_record2", "cannot read key \"%s\" of unsupported type %d", key_name, tid);
12114 return DB_INVALID_PARAM;
12115 }
12116 return DB_SUCCESS;
12117}
12118
12119/********************************************************************/
12170{
12171 int status = DB_SUCCESS;
12172
12173 printf("db_get_record2!\n");
12174
12175 assert(data != NULL);
12176 assert(xbuf_size != NULL);
12177 assert(*xbuf_size > 0);
12178 assert(correct == 0);
12179
12180 int truncated = 0;
12181#if 1
12182 char* r1 = NULL;
12183 int rs = *xbuf_size;
12184 if (1) {
12185 r1 = (char*)malloc(rs);
12186 memset(data, 0xFF, *xbuf_size);
12187 memset(r1, 0xFF, rs);
12188 //status = db_get_record1(hDB, hKey, r1, &rs, 0, rec_str);
12189 status = db_get_record(hDB, hKey, r1, &rs, 0);
12190 printf("db_get_record status %d\n", status);
12191 }
12192#endif
12193
12194 char* buf_start = (char*)data;
12195 int buf_size = *xbuf_size;
12196
12197 char* buf_ptr = buf_start;
12198 int buf_remain = buf_size;
12199
12200 while (rec_str && *rec_str != 0) {
12201 char title[256];
12202 char key_name[MAX_ODB_PATH];
12203 int tid = 0;
12204 int n_data = 0;
12205 int string_length = 0;
12206 const char* rec_str_next = NULL;
12207
12208 status = db_parse_record(rec_str, &rec_str_next, title, sizeof(title), key_name, sizeof(key_name), &tid, &n_data, &string_length);
12209
12210 //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);
12211
12213
12214 if (status != DB_SUCCESS) {
12215 return status;
12216 }
12217
12218 if (key_name[0] == 0) {
12219 // skip title strings, comments, etc
12220 continue;
12221 }
12222
12224 if (status == DB_INVALID_PARAM) {
12225 cm_msg(MERROR, "db_get_record2", "error: cannot continue reading odb record because of previous fatal error, status %d", status);
12226 return DB_INVALID_PARAM;
12227 } if (status != DB_SUCCESS) {
12228 truncated = 1;
12229 }
12230
12232 }
12233
12234 if (r1) {
12235 int ok = -1;
12236 int i;
12237 for (i=0; i<rs; i++) {
12238 if (r1[i] != buf_start[i]) {
12239 ok = i;
12240 break;
12241 }
12242 }
12243 if (ok>=0 || buf_remain>0) {
12244 printf("db_get_record2: miscompare at %d out of %d, buf_remain %d\n", ok, rs, buf_remain);
12245 } else {
12246 printf("db_get_record2: check ok\n");
12247 }
12248 }
12249
12250 if (buf_remain > 0) {
12251 // FIXME: we finished processing the data definition string, but unused space remains in the buffer
12252 return DB_TRUNCATED;
12253 }
12254
12255 if (truncated)
12256 return DB_TRUNCATED;
12257 else
12258 return DB_SUCCESS;
12259}
12260
12261/********************************************************************/
12291INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
12292{
12293 if (rpc_is_remote()) {
12294 align = ss_get_struct_align();
12295 return rpc_call(RPC_DB_SET_RECORD, hDB, hKey, data, buf_size, align);
12296 }
12297#ifdef LOCAL_ROUTINES
12298 {
12299 KEY key;
12300 INT convert_flags;
12301 INT total_size;
12302 void *pdata;
12303
12304 convert_flags = 0;
12305
12306 if (!align)
12307 align = ss_get_struct_align();
12308 else {
12309 /* only convert data if called remotely, as indicated by align != 0 */
12310 if (rpc_is_mserver()) {
12311 convert_flags = rpc_get_convert_flags();
12312 }
12313 }
12314
12315 /* check if key has subkeys */
12316 db_get_key(hDB, hKey, &key);
12317 if (key.type != TID_KEY) {
12318 /* copy single key */
12319 if (key.item_size * key.num_values != buf_size) {
12320 cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
12322 }
12323
12324 if (convert_flags) {
12325 if (key.num_values > 1)
12327 else
12328 rpc_convert_single(data, key.type, 0, convert_flags);
12329 }
12330
12332 return DB_SUCCESS;
12333 }
12334
12335 /* check record size */
12336 db_get_record_size(hDB, hKey, align, &total_size);
12337 if (total_size != buf_size) {
12338 cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
12340 }
12341
12342 /* set subkey data */
12343 pdata = data;
12344 total_size = 0;
12345
12347 db_err_msg* msg = NULL;
12348 db_allow_write_locked(&_database[hDB-1], "db_set_record");
12349 db_recurse_record_tree_locked(hDB, hKey, &pdata, &total_size, align, NULL, TRUE, convert_flags, &msg);
12351 if (msg)
12352 db_flush_msg(&msg);
12353 }
12354#endif /* LOCAL_ROUTINES */
12355
12356 return DB_SUCCESS;
12357}
12358
12360#ifndef DOXYGEN_SHOULD_SKIP_THIS
12361
12362/*------------------------------------------------------------------*/
12364/********************************************************************\
12365
12366 Routine: db_add_open_record
12367
12368 Purpose: Server part of db_open_record. Internal use only.
12369
12370\********************************************************************/
12371{
12372 if (rpc_is_remote())
12373 return rpc_call(RPC_DB_ADD_OPEN_RECORD, hDB, hKey, access_mode);
12374
12375#ifdef LOCAL_ROUTINES
12376 {
12377 INT i;
12378 int status;
12379
12380 if (hDB > _database_entries || hDB <= 0) {
12381 cm_msg(MERROR, "db_add_open_record", "invalid database handle");
12382 return DB_INVALID_HANDLE;
12383 }
12384
12385 db_err_msg* msg = NULL;
12386
12387 /* lock database */
12389
12390 DATABASE *pdb = &_database[hDB - 1];
12391 DATABASE_HEADER *pheader = pdb->database_header;
12393
12394 /* check if key is already open */
12395 for (i = 0; i < pclient->max_index; i++) {
12396 if (pclient->open_record[i].handle == hKey)
12397 break;
12398 }
12399
12400 if (i < pclient->max_index) {
12402 return DB_SUCCESS;
12403 }
12404
12405 /* not found, search free entry */
12406 for (i = 0; i < pclient->max_index; i++) {
12407 if (pclient->open_record[i].handle == 0)
12408 break;
12409 }
12410
12411 /* check if maximum number reached */
12412 if (i == MAX_OPEN_RECORDS) {
12414 return DB_NO_MEMORY;
12415 }
12416
12417 db_allow_write_locked(pdb, "db_add_open_record");
12418
12419 KEY *pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_add_open_record", &msg);
12420
12421 if (!pkey) {
12423 if (msg)
12424 db_flush_msg(&msg);
12425 return status;
12426 }
12427
12428 if (i == pclient->max_index)
12429 pclient->max_index++;
12430
12431 pclient->open_record[i].handle = hKey;
12432 pclient->open_record[i].access_mode = access_mode;
12433
12434 /* increment notify_count */
12435 pkey->notify_count++;
12436
12437 pclient->num_open_records++;
12438
12439 /* set exclusive bit if requested */
12440 if (access_mode & MODE_WRITE)
12441 db_set_mode(hDB, hKey, (WORD) (pkey->access_mode | MODE_EXCLUSIVE), 2);
12442
12444 }
12445#endif /* LOCAL_ROUTINES */
12446
12447 return DB_SUCCESS;
12448}
12449
12450#ifdef LOCAL_ROUTINES
12451
12453{
12454 int status = DB_SUCCESS;
12455
12457
12458 /* search key */
12459 int idx;
12460 for (idx = 0; idx < pclient->max_index; idx++)
12461 if (pclient->open_record[idx].handle == hKey)
12462 break;
12463
12464 if (idx == pclient->max_index) {
12465 return DB_INVALID_HANDLE;
12466 }
12467
12468 KEY* pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_remove_open_record_wlocked", NULL);
12469
12470 if (!pkey)
12471 return status;
12472
12473 /* decrement notify_count */
12474
12475 if (pkey->notify_count > 0)
12476 pkey->notify_count--;
12477
12478 pclient->num_open_records--;
12479
12480 /* remove exclusive flag */
12481 if (pclient->open_record[idx].access_mode & MODE_WRITE)
12482 db_set_mode_wlocked(pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2, NULL);
12483
12484 memset(&pclient->open_record[idx], 0, sizeof(OPEN_RECORD));
12485
12486 /* calculate new max_index entry */
12487 int i;
12488 for (i = pclient->max_index - 1; i >= 0; i--)
12489 if (pclient->open_record[i].handle != 0)
12490 break;
12491 pclient->max_index = i + 1;
12492
12493 return DB_SUCCESS;
12494}
12495#endif // LOCAL_ROUTINES
12496
12497
12499/********************************************************************\
12500
12501 Routine: db_remove_open_record
12502
12503 Purpose: Gets called by db_close_record. Internal use only.
12504
12505\********************************************************************/
12506{
12507 if (rpc_is_remote())
12509
12510 int status = DB_SUCCESS;
12511
12512#ifdef LOCAL_ROUTINES
12513 {
12514 if (hDB > _database_entries || hDB <= 0) {
12515 cm_msg(MERROR, "db_remove_open_record", "invalid database handle %d", hDB);
12516 return DB_INVALID_HANDLE;
12517 }
12518
12520
12521 DATABASE *pdb = &_database[hDB - 1];
12522 DATABASE_HEADER *pheader = pdb->database_header;
12523
12524 db_allow_write_locked(pdb, "db_remove_open_record");
12525
12527
12529 }
12530#endif /* LOCAL_ROUTINES */
12531
12532 return status;
12533}
12534
12535/*------------------------------------------------------------------*/
12536
12537#ifdef LOCAL_ROUTINES
12538
12540/********************************************************************\
12541
12542 Routine: db_notify_clients
12543
12544 Purpose: Gets called by db_set_xxx functions. Internal use only.
12545
12546\********************************************************************/
12547{
12548 HNDLE hKey;
12550 INT i, j;
12551 char str[80];
12552 int status;
12553
12554 hKey = hKeyMod;
12555
12556 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_notify_clients", msg);
12557
12558 if (!pkey) {
12559 return status;
12560 }
12561
12562 do {
12563
12564 /* check which client has record open */
12565 if (pkey->notify_count)
12566 for (i = 0; i < pheader->max_client_index; i++) {
12567 const DATABASE_CLIENT* pclient = &pheader->client[i];
12568 for (j = 0; j < pclient->max_index; j++)
12569 if (pclient->open_record[j].handle == hKey) {
12570 /* send notification to remote process */
12571 sprintf(str, "O %d %d %d %d", hDB, hKey, hKeyMod, index);
12572 ss_resume(pclient->port, str);
12573 }
12574 }
12575
12576 if (pkey->parent_keylist == 0 || !bWalk)
12577 return DB_SUCCESS;
12578
12579 // FIXME: validate pkey->parent_keylist
12580 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
12581 pkey = db_get_pkey(pheader, pkeylist->parent, &status, "db_notify_clients", msg);
12582 if (!pkey) {
12583 return status;
12584 }
12585 hKey = db_pkey_to_hkey(pheader, pkey);
12586 } while (TRUE);
12587
12588}
12589
12590#endif /* LOCAL_ROUTINES */
12591/*------------------------------------------------------------------*/
12592
12593
12595/********************************************************************\
12596
12597 Routine: db_notify_clients
12598
12599 Purpose: Gets called by db_set_xxx functions. Internal use only.
12600 \********************************************************************/
12601{
12602 if (rpc_is_remote()) {
12603 cm_msg(MERROR, "db_notify_clients", "db_notify_clients() does not work in remotely connected MIDAS clients");
12604 return DB_INVALID_HANDLE;
12605 }
12606
12607#ifdef LOCAL_ROUTINES
12608 {
12609 db_err_msg* msg = NULL;
12611 if (status != DB_SUCCESS)
12612 return status;
12614 db_notify_clients_locked(pheader, hDB, hKeyMod, index, bWalk, &msg);
12616 if (msg)
12617 db_flush_msg(&msg);
12618 }
12619#endif
12620 return DB_SUCCESS;
12621}
12622
12624/********************************************************************\
12625
12626 Routine: db_notify_clients_array
12627
12628 Purpose: This function is typically called after a set of calls
12629 to db_set_data1 which omits hot-link notification to
12630 programs. After several ODB values are modified in a set,
12631 this function has to be called to trigger the hot-links
12632 of the whole set.
12633
12634 \********************************************************************/
12635{
12636 if (rpc_is_remote())
12638
12639#ifdef LOCAL_ROUTINES
12640 {
12642 if (status != DB_SUCCESS)
12643 return status;
12644 db_err_msg* msg = NULL;
12646 int count = size/sizeof(INT);
12647 for (int i=0 ; i<count; i++) {
12648 db_notify_clients_locked(pheader, hDB, hKeys[i], -1, TRUE, &msg);
12649 }
12651 if (msg)
12652 db_flush_msg(&msg);
12653 }
12654#endif
12655 return DB_SUCCESS;
12656}
12657
12658/*------------------------------------------------------------------*/
12660{
12661 char full_name[MAX_ODB_PATH];
12662 INT status, size;
12664 KEY initkey, key;
12665
12666 /* avoid compiler warnings */
12667 status = level;
12668
12669 /* compose name of init key */
12670 std::string s = db_get_path(hDB, hKey);
12671 mstrlcpy(full_name, s.c_str(), sizeof(full_name));
12672 *strchr(full_name, 'O') = 'I';
12673
12674 /* if key in init record found, copy original data to init data */
12676 if (status == DB_SUCCESS) {
12678 if (status != DB_SUCCESS) {
12679 cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', db_get_key() status %d", full_name, status);
12680 return;
12681 }
12683 if (status != DB_SUCCESS) {
12684 cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', second db_get_key() status %d", full_name, status);
12685 return;
12686 }
12687
12688 if (initkey.type != TID_KEY && initkey.type == key.type) {
12689 char* allocbuffer = NULL;
12690 char stackbuffer[10000];
12691 char* buffer = stackbuffer;
12692 size = sizeof(stackbuffer);
12693 while (1) {
12694 /* copy data from original key to new key */
12695 status = db_get_data(hDB, hKey, buffer, &size, initkey.type);
12696 if (status == DB_SUCCESS) {
12697 status = db_set_data(hDB, hKeyInit, buffer, initkey.total_size, initkey.num_values, initkey.type);
12698 if (status != DB_SUCCESS) {
12699 cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', db_set_data() status %d", full_name, status);
12700 return;
12701 }
12702 break;
12703 }
12704 if (status == DB_TRUNCATED) {
12705 size *= 2;
12706 allocbuffer = (char *)realloc(allocbuffer, size);
12707 assert(allocbuffer != NULL);
12708 buffer = allocbuffer;
12709 continue;
12710 }
12711 cm_msg(MERROR, "merge_records", "aborting on unexpected failure of db_get_data(%s), status %d", full_name, status);
12712 abort();
12713 }
12714 if (allocbuffer)
12715 free(allocbuffer);
12716 }
12717 } else if (status == DB_NO_KEY) {
12718 /* do nothing */
12719 } else if (status == DB_INVALID_LINK) {
12721 if (status == DB_SUCCESS) {
12722 size = sizeof(full_name);
12724 }
12725 cm_msg(MERROR, "merge_records", "Invalid link \"%s\"", full_name);
12726 } else {
12727 cm_msg(MERROR, "merge_records", "aborting on unexpected failure of db_find_key(%s), status %d", full_name, status);
12728 abort();
12729 }
12730}
12731
12732static int _global_open_count; // FIXME: this is not thread-safe
12733
12735{
12736 if (pkey->notify_count)
12738}
12739
12741#endif /* DOXYGEN_SHOULD_SKIP_THIS */
12742
12743/********************************************************************/
12800INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
12801{
12802 char str[256], key_name[256], *buffer;
12803 INT status, size, buffer_size;
12805
12806 if (rpc_is_remote())
12807 return rpc_call(RPC_DB_CREATE_RECORD, hDB, hKey, orig_key_name, init_str);
12808
12809 /* make this function atomic */
12811
12812 /* strip trailing '/' */
12813 mstrlcpy(key_name, orig_key_name, sizeof(key_name));
12814 if (strlen(key_name) > 1 && key_name[strlen(key_name) - 1] == '/')
12815 key_name[strlen(key_name) - 1] = 0;
12816
12817 /* merge temporay record and original record */
12818 status = db_find_key(hDB, hKey, key_name, &hKeyOrig);
12819 if (status == DB_SUCCESS) {
12820 assert(hKeyOrig != 0);
12821#ifdef CHECK_OPEN_RECORD
12822 /* check if key or subkey is opened */
12823 _global_open_count = 0; // FIXME: this is not thread safe
12825 if (_global_open_count) {
12827 return DB_OPEN_RECORD;
12828 }
12829#endif
12830 /* create temporary records */
12831 sprintf(str, "/System/Tmp/%sI", ss_tid_to_string(ss_gettid()).c_str());
12832 //printf("db_create_record str [%s]\n", str);
12833 db_find_key(hDB, 0, str, &hKeyTmp);
12834 if (hKeyTmp)
12837 status = db_find_key(hDB, 0, str, &hKeyTmp);
12838 if (status != DB_SUCCESS) {
12840 return status;
12841 }
12842
12843 sprintf(str, "/System/Tmp/%sO", ss_tid_to_string(ss_gettid()).c_str());
12844 //printf("db_create_record str [%s]\n", str);
12845 db_find_key(hDB, 0, str, &hKeyTmpO);
12846 if (hKeyTmpO)
12850 if (status != DB_SUCCESS) {
12852 return status;
12853 }
12854
12855 status = db_paste(hDB, hKeyTmp, init_str);
12856 if (status != DB_SUCCESS) {
12858 return status;
12859 }
12860
12861 buffer_size = 10000;
12862 buffer = (char *) malloc(buffer_size);
12863 do {
12864 size = buffer_size;
12865 status = db_copy(hDB, hKeyOrig, buffer, &size, "");
12866 if (status == DB_TRUNCATED) {
12867 buffer_size += 10000;
12868 buffer = (char *) realloc(buffer, buffer_size);
12869 continue;
12870 }
12871 if (status != DB_SUCCESS) {
12873 return status;
12874 }
12875
12876 } while (status != DB_SUCCESS);
12877
12878 status = db_paste(hDB, hKeyTmpO, buffer);
12879 if (status != DB_SUCCESS) {
12880 free(buffer);
12882 return status;
12883 }
12884
12885 /* merge temporay record and original record */
12887
12888 /* delete original record */
12889 while (1) {
12891 if (!hSubkey)
12892 break;
12893
12895 if (status != DB_SUCCESS) {
12896 free(buffer);
12898 return status;
12899 }
12900 }
12901
12902 /* copy merged record to original record */
12903 do {
12904 size = buffer_size;
12905 status = db_copy(hDB, hKeyTmp, buffer, &size, "");
12906 if (status == DB_TRUNCATED) {
12907 buffer_size += 10000;
12908 buffer = (char *) realloc(buffer, buffer_size);
12909 continue;
12910 }
12911 if (status != DB_SUCCESS) {
12912 free(buffer);
12914 return status;
12915 }
12916
12917 } while (status != DB_SUCCESS);
12918
12919 status = db_paste(hDB, hKeyOrig, buffer);
12920 if (status != DB_SUCCESS) {
12921 free(buffer);
12923 return status;
12924 }
12925
12926 /* delete temporary records */
12929
12930 free(buffer);
12931 buffer = NULL;
12932 } else if (status == DB_NO_KEY) {
12933 /* create fresh record */
12934 db_create_key(hDB, hKey, key_name, TID_KEY);
12935 status = db_find_key(hDB, hKey, key_name, &hKeyTmp);
12936 if (status != DB_SUCCESS) {
12938 return status;
12939 }
12940
12941 status = db_paste(hDB, hKeyTmp, init_str);
12942 if (status != DB_SUCCESS) {
12944 return status;
12945 }
12946 } else {
12947 cm_msg(MERROR, "db_create_record", "aborting on unexpected failure of db_find_key(%s), status %d", key_name, status);
12948 abort();
12949 }
12950
12952
12953 return DB_SUCCESS;
12954}
12955
12956/********************************************************************/
12973{
12974 char line[MAX_STRING_LENGTH];
12975 char title[MAX_STRING_LENGTH];
12976 char key_name[MAX_STRING_LENGTH];
12977 char info_str[MAX_STRING_LENGTH + 50];
12978 char *pc;
12979 const char *pold, *rec_str_orig;
12980 DWORD tid;
12981 INT i, j, n_data, string_length, status;
12983 KEY key;
12985
12986 if (rpc_is_remote())
12988
12989 /* check if record exists */
12991
12992 /* create record if not */
12993 if (status == DB_NO_KEY) {
12994 if (correct)
12996 return DB_NO_KEY;
12997 }
12998
12999 assert(hKeyRoot);
13000
13001 title[0] = 0;
13003
13005 if (key.type == TID_KEY)
13006 /* get next subkey which is not of type TID_KEY */
13008 else
13009 /* test root key itself */
13011
13012 if (hKeyTest == 0 && *rec_str != 0) {
13013 if (correct)
13015
13016 return DB_STRUCT_MISMATCH;
13017 }
13018
13019 do {
13020 if (*rec_str == 0)
13021 break;
13022
13023 for (i = 0; *rec_str != '\n' && *rec_str && i < MAX_STRING_LENGTH; i++)
13024 line[i] = *rec_str++;
13025
13026 if (i == MAX_STRING_LENGTH) {
13027 cm_msg(MERROR, "db_check_record", "line too long");
13028 return DB_TRUNCATED;
13029 }
13030
13031 line[i] = 0;
13032 if (*rec_str == '\n')
13033 rec_str++;
13034
13035 /* check if it is a section title */
13036 if (line[0] == '[') {
13037 /* extract title and append '/' */
13038 strcpy(title, line + 1);
13039 if (strchr(title, ']'))
13040 *strchr(title, ']') = 0;
13041 if (title[0] && title[strlen(title) - 1] != '/')
13042 strcat(title, "/");
13043 } else {
13044 /* valid data line if it includes '=' and no ';' */
13045 if (strchr(line, '=') && line[0] != ';') {
13046 /* copy type info and data */
13047 pc = strchr(line, '=') + 1;
13048 while (*pc == ' ')
13049 pc++;
13050 strcpy(info_str, pc);
13051
13052 /* extract key name */
13053 *strchr(line, '=') = 0;
13054
13055 pc = &line[strlen(line) - 1];
13056 while (*pc == ' ')
13057 *pc-- = 0;
13058
13059 mstrlcpy(key_name, line, sizeof(key_name));
13060
13061 /* evaluate type info */
13062 strcpy(line, info_str);
13063 if (strchr(line, ' '))
13064 *strchr(line, ' ') = 0;
13065
13066 n_data = 1;
13067 if (strchr(line, '[')) {
13068 n_data = atol(strchr(line, '[') + 1);
13069 *strchr(line, '[') = 0;
13070 }
13071
13072 for (tid = 0; tid < TID_LAST; tid++)
13073 if (strcmp(rpc_tid_name(tid), line) == 0)
13074 break;
13075 if (tid == TID_LAST) {
13076 for (tid = 0; tid < TID_LAST; tid++)
13077 if (strcmp(rpc_tid_name_old(tid), line) == 0)
13078 break;
13079 }
13080
13081 string_length = 0;
13082
13083 if (tid == TID_LAST)
13084 cm_msg(MERROR, "db_check_record", "found unknown data type \"%s\" in ODB file", line);
13085 else {
13086 /* skip type info */
13087 pc = info_str;
13088 while (*pc != ' ' && *pc)
13089 pc++;
13090 while ((*pc == ' ' || *pc == ':') && *pc)
13091 pc++;
13092
13093 if (n_data > 1) {
13094 info_str[0] = 0;
13095 if (!*rec_str)
13096 break;
13097
13098 for (j = 0; *rec_str != '\n' && *rec_str; j++)
13099 info_str[j] = *rec_str++;
13100 info_str[j] = 0;
13101 if (*rec_str == '\n')
13102 rec_str++;
13103 }
13104
13105 for (i = 0; i < n_data; i++) {
13106 /* strip trailing \n */
13107 pc = &info_str[strlen(info_str) - 1];
13108 while (*pc == '\n' || *pc == '\r')
13109 *pc-- = 0;
13110
13111 if (tid == TID_STRING || tid == TID_LINK) {
13112 if (!string_length) {
13113 if (info_str[1] == '=')
13114 string_length = -1;
13115 else {
13116 pc = strchr(info_str, '[');
13117 if (pc != NULL)
13118 string_length = atoi(pc + 1);
13119 else
13120 string_length = -1;
13121 }
13124 cm_msg(MERROR, "db_check_record", "found string exceeding MAX_STRING_LENGTH");
13125 }
13126 }
13127
13128 if (string_length == -1) {
13129 /* multi-line string */
13130 if (strstr(rec_str, "\n====#$@$#====\n") != NULL) {
13131 string_length = (POINTER_T) strstr(rec_str, "\n====#$@$#====\n") - (POINTER_T) rec_str + 1;
13132
13133 rec_str = strstr(rec_str, "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
13134 } else
13135 cm_msg(MERROR, "db_check_record", "found multi-line string without termination sequence");
13136 } else {
13137 if (strchr(info_str, ']'))
13138 pc = strchr(info_str, ']') + 1;
13139 else
13140 pc = info_str + 2;
13141 while (*pc && *pc != ' ')
13142 pc++;
13143 while (*pc && *pc == ' ')
13144 pc++;
13145
13146 /* limit string size */
13147 *(pc + string_length - 1) = 0;
13148 }
13149 } else {
13150 pc = info_str;
13151
13152 if (n_data > 1 && info_str[0] == '[') {
13153 pc = strchr(info_str, ']') + 1;
13154 while (*pc && *pc == ' ')
13155 pc++;
13156 }
13157 }
13158
13159 if (i < n_data - 1) {
13160 info_str[0] = 0;
13161 if (!*rec_str)
13162 break;
13163
13164 pold = rec_str;
13165
13166 for (j = 0; *rec_str != '\n' && *rec_str; j++)
13167 info_str[j] = *rec_str++;
13168 info_str[j] = 0;
13169 if (*rec_str == '\n')
13170 rec_str++;
13171
13172 /* test if valid data */
13173 if (tid != TID_STRING && tid != TID_LINK) {
13174 if (info_str[0] == 0 || (strchr(info_str, '=')
13175 && strchr(info_str, ':')))
13176 rec_str = pold;
13177 }
13178 }
13179 }
13180
13181 /* if rec_str contains key, but not ODB, return mismatch */
13182 if (!hKeyTest) {
13183 if (correct)
13185
13186 return DB_STRUCT_MISMATCH;
13187 }
13188
13190 assert(status == DB_SUCCESS);
13191
13193
13194 if (key.type == TID_STRING) {
13195 //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);
13196 if (string_length > 0 && string_length != key.item_size) {
13198 }
13199 }
13200
13201 /* check rec_str vs. ODB key */
13202 if (!equal_ustring(key.name, key_name) || key.type != tid || key.num_values != n_data || bad_string_length) {
13203 //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);
13204 if (correct)
13206
13207 return DB_STRUCT_MISMATCH;
13208 }
13209
13210 /* get next key in ODB */
13212 }
13213 }
13214 }
13215 } while (TRUE);
13216
13217 return DB_SUCCESS;
13218}
13219
13220/********************************************************************/
13292 WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info)
13293{
13294 INT idx, status, size;
13295 KEY key;
13296 void *data;
13297
13298 /* allocate new space for the local record list */
13299 if (_record_list_entries == 0) {
13301 memset(_record_list, 0, sizeof(RECORD_LIST));
13302 if (_record_list == NULL) {
13303 cm_msg(MERROR, "db_open_record", "not enough memory");
13304 return DB_NO_MEMORY;
13305 }
13306
13308 idx = 0;
13309 } else {
13310 /* check for a deleted entry */
13311 for (idx = 0; idx < _record_list_entries; idx++)
13312 if (!_record_list[idx].handle)
13313 break;
13314
13315 /* if not found, create new one */
13316 if (idx == _record_list_entries) {
13318 if (_record_list == NULL) {
13319 cm_msg(MERROR, "db_open_record", "not enough memory");
13320 return DB_NO_MEMORY;
13321 }
13322
13324
13326 }
13327 }
13328
13329 db_get_key(hDB, hKey, &key);
13330
13331 /* check record size */
13332 status = db_get_record_size(hDB, hKey, 0, &size);
13333 if (status != DB_SUCCESS) {
13335 cm_msg(MERROR, "db_open_record", "cannot get record size, db_get_record_size() status %d", status);
13336 return DB_NO_MEMORY;
13337 }
13338
13339 if (size != rec_size && ptr != NULL) {
13341 std::string path = db_get_path(hDB, hKey);
13342 cm_msg(MERROR, "db_open_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d)", path.c_str(), rec_size, size);
13344 }
13345
13346 /* check for read access */
13347 if (((key.access_mode & MODE_EXCLUSIVE) && (access_mode & MODE_WRITE))
13348 || (!(key.access_mode & MODE_WRITE) && (access_mode & MODE_WRITE))
13349 || (!(key.access_mode & MODE_READ) && (access_mode & MODE_READ))) {
13351 return DB_NO_ACCESS;
13352 }
13353
13354 if (access_mode & MODE_ALLOC) {
13355 data = malloc(size);
13356
13357 if (data == NULL) {
13359 cm_msg(MERROR, "db_open_record", "not enough memory, malloc(%d) returned NULL", size);
13360 return DB_NO_MEMORY;
13361 }
13362
13363 memset(data, 0, size);
13364
13365 *((void **) ptr) = data;
13366 } else {
13367 data = ptr;
13368 }
13369
13370 /* copy record to local memory */
13371 if (access_mode & MODE_READ && data != NULL) {
13372 status = db_get_record(hDB, hKey, data, &size, 0);
13373 if (status != DB_SUCCESS) {
13375 cm_msg(MERROR, "db_open_record", "cannot get record, db_get_record() status %d", status);
13376 return DB_NO_MEMORY;
13377 }
13378 }
13379
13380 /* copy local record to ODB */
13381 if (access_mode & MODE_WRITE) {
13382 /* only write to ODB if not in MODE_ALLOC */
13383 if ((access_mode & MODE_ALLOC) == 0) {
13384 status = db_set_record(hDB, hKey, data, size, 0);
13385 if (status != DB_SUCCESS) {
13387 cm_msg(MERROR, "db_open_record", "cannot set record, db_set_record() status %d", status);
13388 return DB_NO_MEMORY;
13389 }
13390 }
13391
13392 /* init a local copy of the record */
13393 _record_list[idx].copy = malloc(size);
13394 if (_record_list[idx].copy == NULL) {
13395 cm_msg(MERROR, "db_open_record", "not enough memory");
13396 return DB_NO_MEMORY;
13397 }
13398
13399 memcpy(_record_list[idx].copy, data, size);
13400 }
13401
13402 /* initialize record list */
13405 _record_list[idx].access_mode = access_mode;
13407 _record_list[idx].buf_size = size;
13408 _record_list[idx].dispatcher = dispatcher;
13410
13411 /* add record entry in database structure */
13412 return db_add_open_record(hDB, hKey, (WORD) (access_mode & ~MODE_ALLOC));
13413}
13414
13415/********************************************************************/
13442 WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info,
13443 const char *rec_str)
13444{
13445 if (rec_str) {
13446 int status;
13447 if (rec_size) {
13448 char* pbuf;
13449 int size = rec_size;
13450 pbuf = (char*)malloc(size);
13451 assert(pbuf != NULL);
13452 status = db_get_record1(hDB, hKey, pbuf, &size, 0, rec_str);
13453 free(pbuf);
13454 if (status != DB_SUCCESS)
13455 return status;
13456 }
13457
13459 if (status != DB_SUCCESS)
13460 return status;
13461 }
13462
13463 return db_open_record(hDB, hKey, ptr, rec_size, access_mode, dispatcher, info);
13464}
13465
13466/********************************************************************/
13474{
13475#ifdef LOCAL_ROUTINES
13476 {
13477 INT i;
13478
13479 for (i = 0; i < _record_list_entries; i++)
13480 if (_record_list[i].handle == hKey && _record_list[i].hDB == hDB)
13481 break;
13482
13483 if (i == _record_list_entries)
13484 return DB_INVALID_HANDLE;
13485
13486 /* remove record entry from database structure */
13488
13489 /* free local memory */
13490 if (_record_list[i].access_mode & MODE_ALLOC) {
13491 free(_record_list[i].data);
13493 }
13494
13495 if (_record_list[i].access_mode & MODE_WRITE) {
13496 free(_record_list[i].copy);
13498 }
13499
13500 memset(&_record_list[i], 0, sizeof(RECORD_LIST));
13501 }
13502#endif /* LOCAL_ROUTINES */
13503
13504 return DB_SUCCESS;
13505}
13506
13507/********************************************************************/
13515{
13516 INT i;
13517
13518 for (i = 0; i < _record_list_entries; i++) {
13519 if (_record_list[i].handle) {
13520 if (_record_list[i].access_mode & MODE_WRITE) {
13521 free(_record_list[i].copy);
13523 }
13524
13525 if (_record_list[i].access_mode & MODE_ALLOC) {
13526 free(_record_list[i].data);
13528 }
13529
13530 memset(&_record_list[i], 0, sizeof(RECORD_LIST));
13531 }
13532 }
13533
13534 if (_record_list_entries > 0) {
13536 free(_record_list);
13538 }
13539
13540 return DB_SUCCESS;
13541}
13542
13543/********************************************************************/
13553{
13554 INT i, size, status;
13555
13557
13558 /* check all record entries for matching key */
13559 for (i = 0; i < _record_list_entries; i++)
13560 if (_record_list[i].handle == hKeyRoot) {
13562
13563 /* get updated data if record not opened in write mode */
13564 if ((_record_list[i].access_mode & MODE_WRITE) == 0) {
13565 size = _record_list[i].buf_size;
13566 if (_record_list[i].data != NULL) {
13567 status = db_get_record(hDB, hKeyRoot, _record_list[i].data, &size, 0); // db_open_record() update
13568 //printf("db_open_record update status %d, size %d %d\n", status, _record_list[i].buf_size, size);
13569 }
13570
13571 /* call dispatcher if requested */
13572 if (_record_list[i].dispatcher)
13574 }
13575 }
13576
13577 /* check all watch entries for matching key */
13578 for (i = 0; i < _watch_list_entries; i++)
13579 if (_watch_list[i].handle == hKeyRoot) {
13581
13582 /* call dispatcher if requested */
13583 if (_watch_list[i].dispatcher)
13585 }
13586
13587 return status;
13588}
13589
13590/********************************************************************/
13600{
13601 char buffer[32];
13602
13603 int convert_flags = rpc_get_convert_flags();
13604
13605 NET_COMMAND* nc = (NET_COMMAND *) buffer;
13606
13608 nc->header.param_size = 4 * sizeof(INT);
13609 *((INT *) nc->param) = hDB;
13610 *((INT *) nc->param + 1) = hKeyRoot;
13611 *((INT *) nc->param + 2) = hKey;
13612 *((INT *) nc->param + 3) = index;
13613
13614 if (convert_flags) {
13617 rpc_convert_single(&nc->param[0], TID_UINT32, RPC_OUTGOING, convert_flags);
13618 rpc_convert_single(&nc->param[4], TID_UINT32, RPC_OUTGOING, convert_flags);
13619 rpc_convert_single(&nc->param[8], TID_UINT32, RPC_OUTGOING, convert_flags);
13620 rpc_convert_single(&nc->param[12], TID_UINT32, RPC_OUTGOING, convert_flags);
13621 }
13622
13623 /* send the update notification to the client */
13624 send_tcp(client_socket, buffer, sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT), 0);
13625
13626 return DB_SUCCESS;
13627}
13628
13629/********************************************************************/
13778{
13779 INT i;
13780
13781 for (i = 0; i < _record_list_entries; i++)
13782 if (_record_list[i].access_mode & MODE_WRITE) {
13783 if (memcmp(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size) != 0) {
13784 if (rpc_is_remote()) {
13785 int align = ss_get_struct_align();
13787 } else {
13789 }
13790 memcpy(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size);
13791 }
13792 }
13793
13794 return DB_SUCCESS;
13795}
13796
13797/*------------------------------------------------------------------*/
13798
13799/********************************************************************/
13813INT db_watch(HNDLE hDB, HNDLE hKey, void (*dispatcher) (INT, INT, INT, void*), void* info)
13814{
13815 INT idx, status;
13816 KEY key;
13817
13818 /* check for valid key */
13819 assert(hKey);
13820
13821 /* allocate new space for the local record list */
13822 if (_watch_list_entries == 0) {
13823 _watch_list = (WATCH_LIST *) malloc(sizeof(WATCH_LIST));
13824 memset(_watch_list, 0, sizeof(WATCH_LIST));
13825 if (_watch_list == NULL) {
13826 cm_msg(MERROR, "db_watch", "not enough memory");
13827 return DB_NO_MEMORY;
13828 }
13829
13831 idx = 0;
13832 } else {
13833 /* check for a deleted entry */
13834 for (idx = 0; idx < _watch_list_entries; idx++)
13835 if (!_watch_list[idx].handle)
13836 break;
13837
13838 /* if not found, create new one */
13839 if (idx == _watch_list_entries) {
13841 if (_watch_list == NULL) {
13842 cm_msg(MERROR, "db_watch", "not enough memory");
13843 return DB_NO_MEMORY;
13844 }
13845
13847
13849 }
13850 }
13851
13852 /* check key */
13854 if (status != DB_SUCCESS) {
13856 std::string path = db_get_path(hDB, hKey);
13857 cm_msg(MERROR, "db_watch", "cannot get key \"%s\"", path.c_str());
13858 return DB_NO_MEMORY;
13859 }
13860
13861 /* check for read access */
13862 if (!(key.access_mode & MODE_READ)) {
13864 std::string path = db_get_path(hDB, hKey);
13865 cm_msg(MERROR, "db_watch", "cannot get key \"%s\"", path.c_str());
13866 return DB_NO_ACCESS;
13867 }
13868
13869 /* initialize record list */
13872 _watch_list[idx].dispatcher = dispatcher;
13874
13875 /* add record entry in database structure */
13877}
13878
13879
13880/********************************************************************/
13888{
13889#ifdef LOCAL_ROUTINES
13890 {
13891 INT i;
13892
13893 for (i = 0; i < _watch_list_entries; i++)
13894 if (_watch_list[i].handle == hKey && _watch_list[i].hDB == hDB)
13895 break;
13896
13897 if (i == _watch_list_entries)
13898 return DB_INVALID_HANDLE;
13899
13900 /* remove record entry from database structure */
13902
13903 memset(&_watch_list[i], 0, sizeof(WATCH_LIST));
13904 }
13905#endif /* LOCAL_ROUTINES */
13906
13907 return DB_SUCCESS;
13908}
13909
13910/********************************************************************/
13918{
13919 for (int i = _watch_list_entries-1; i >= 0 ; i--) {
13920 if ((_watch_list[i].hDB == 0) && (_watch_list[i].handle == 0)) {
13921 // empty or deleted watch list entry
13922 continue;
13923 }
13925 }
13926
13927 return DB_SUCCESS;
13928}
13929
13930/*------------------------------------------------------------------*/
13931
13932/* C++ wrapper for db_get_value */
13933
13934INT EXPRT db_get_value_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int index, std::string* s, BOOL create, int create_string_length)
13935{
13936 int status;
13937 int hkey;
13938
13939 //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);
13940
13941 if (index > 0 && create) {
13942 cm_msg(MERROR, "db_get_value_string", "cannot resize odb string arrays, please use db_resize_string() instead");
13943 return DB_OUT_OF_RANGE;
13944 }
13945
13946 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
13947 if (status == DB_SUCCESS) {
13948 KEY key;
13950 if (status != DB_SUCCESS)
13951 return status;
13953 return DB_OUT_OF_RANGE;
13954 int size = key.item_size;
13955 if (size == 0) {
13956 if (s)
13957 *s = "";
13958 //printf("db_get_value_string: return empty string, item_size %d\n", key.item_size);
13959 return DB_SUCCESS;
13960 }
13961 char* buf = (char*)malloc(size);
13962 assert(buf != NULL);
13963 status = db_get_data_index(hdb, hkey, buf, &size, index, TID_STRING);
13964 if (status != DB_SUCCESS) {
13965 free(buf);
13966 return status;
13967 }
13968 if (s)
13969 *s = buf;
13970 free(buf);
13971 //printf("db_get_value_string: return [%s], len %d, item_size %d, size %d\n", s->c_str(), s->length(), key.item_size, size);
13972 return DB_SUCCESS;
13973 } else if (!create) {
13974 // does not exist and not asked to create it
13975 return status;
13976 } else {
13977 //printf("db_get_value_string: creating [%s]\n", key_name);
13979 if (status != DB_SUCCESS)
13980 return status;
13981 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
13982 if (status != DB_SUCCESS)
13983 return status;
13984 assert(s != NULL);
13985 if (create_string_length == 0) {
13986 int size = s->length() + 1; // 1 byte for final \0
13987 status = db_set_data_index(hdb, hkey, s->c_str(), size, index, TID_STRING);
13988 } else {
13989 char* buf = (char*)malloc(create_string_length);
13990 assert(buf);
13991 mstrlcpy(buf, s->c_str(), create_string_length);
13993 free(buf);
13994 }
13995 if (status != DB_SUCCESS)
13996 return status;
13997 //printf("db_get_value_string: created with value [%s]\n", s->c_str());
13998 return DB_SUCCESS;
13999 }
14000 // NOT REACHED
14001}
14002
14003/* C++ wrapper for db_set_value */
14004
14005INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string* s)
14006{
14007 assert(s != NULL);
14008 int size = s->length() + 1; // 1 byte for final \0
14009 //printf("db_set_value_string: key_name [%s], string [%s], size %d\n", key_name, s->c_str(), size);
14010 return db_set_value(hDB, hKeyRoot, key_name, s->c_str(), size, 1, TID_STRING);
14011}
14012
14013/********************************************************************/
14025INT EXPRT db_resize_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int num_values, int max_string_length)
14026{
14027 int status;
14028 int hkey;
14029
14030 //printf("db_resize_string: key_name [%s], num_values %d, max_string_length %d\n", key_name, num_values, max_string_length);
14031
14032 int old_num_values = 0;
14033 int old_item_size = 0;
14034 int old_size = 0;
14035 char* old_data = NULL;
14036
14037 if (key_name) {
14038 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
14039 } else {
14040 hkey = hKeyRoot;
14042 }
14043 if (status == DB_SUCCESS) {
14044 KEY key;
14046 if (status != DB_SUCCESS)
14047 return status;
14051 if (old_size > 0) {
14052 old_data = (char*)malloc(old_size);
14053 assert(old_data != NULL);
14054 int size = old_size;
14056 if (status != DB_SUCCESS) {
14057 free(old_data);
14058 return status;
14059 }
14060 assert(size == old_size);
14061 }
14062 } else {
14064 if (status != DB_SUCCESS)
14065 return status;
14066 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
14067 if (status != DB_SUCCESS)
14068 return status;
14069 }
14070
14071 //printf("old_num_values %d, old_item_size %d, old_size %d\n", old_num_values, old_item_size, old_size);
14072
14073 int item_size = max_string_length;
14074
14075 if (item_size < 1)
14076 item_size = old_item_size;
14077
14078 if (num_values < 1)
14079 num_values = old_num_values;
14080
14081 int new_size = num_values * item_size;
14082 char* new_data = (char*)malloc(new_size);
14083 assert(new_data);
14084
14086
14087 if (old_data) {
14088 int num = old_num_values;
14089 if (num > num_values)
14090 num = num_values;
14091
14092 //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);
14093
14094 for (int i=0; i<num; i++) {
14095 const char* old_ptr = old_data + i*old_item_size;
14096 char* new_ptr = new_data + i*item_size;
14097 mstrlcpy(new_ptr, old_ptr, item_size);
14098 }
14099 }
14100
14102
14103 if (old_data)
14104 free(old_data);
14105 if (new_data)
14106 free(new_data);
14107
14108 return status;
14109}
14110
14111MJsonNode* db_scl(HNDLE hDB)
14112{
14113#ifdef LOCAL_ROUTINES
14114 MJsonNode* scl = MJsonNode::MakeObject();
14115 MJsonNode* clients = MJsonNode::MakeArray();
14116
14117 scl->AddToObject("now", MJsonNode::MakeNumber(ss_time_sec()));
14118 scl->AddToObject("clients", clients);
14119
14120 /* lock database */
14122
14123 DATABASE *pdb = &_database[hDB - 1];
14124 DATABASE_HEADER *pheader = pdb->database_header;
14125
14127
14128 /* list clients */
14129 for (int i = 0; i < pheader->max_client_index; i++) {
14130 DATABASE_CLIENT *pclient = &pheader->client[i];
14131 if (pclient->pid) {
14132 MJsonNode* c = MJsonNode::MakeObject();
14133 c->AddToObject("slot", MJsonNode::MakeNumber(i));
14134 c->AddToObject("pid", MJsonNode::MakeNumber(pclient->pid));
14135 c->AddToObject("name", MJsonNode::MakeString(pclient->name));
14136 std::string path = msprintf("/System/Clients/%d/Host", pclient->pid);
14137 const KEY* pkey = db_find_pkey_locked(pheader, NULL, path.c_str(), NULL, NULL);
14138 if (pkey) {
14139 int host_size = pkey->total_size;
14140 char* host = (char*)malloc(host_size);
14141 assert(host != NULL);
14142 db_get_data_locked(pheader, pkey, 0, host, &host_size, TID_STRING, NULL);
14143 c->AddToObject("host", MJsonNode::MakeString(host));
14144 free(host);
14145 }
14146 c->AddToObject("watchdog_timeout_millisec", MJsonNode::MakeNumber(pclient->watchdog_timeout));
14147 // "now" and "last_activity" is millisecond time wrapped around at 32 bits, must do unsigned 32-bit math here, not in javascript
14148 c->AddToObject("last_activity_millisec", MJsonNode::MakeNumber(now - pclient->last_activity));
14149 clients->AddToArray(c);
14150 }
14151 }
14152
14154
14155 return scl;
14156#else
14157 return MJsonNode::MakeNull();
14158#endif
14159}
14160
14161MJsonNode* db_sor(HNDLE hDB, const char* root_path)
14162{
14163 //printf("db_sor(%s)\n", root_path);
14164 assert(root_path != NULL);
14165#ifdef LOCAL_ROUTINES
14166 size_t root_path_len = strlen(root_path);
14167 MJsonNode* sor = MJsonNode::MakeArray();
14168
14169 /* lock database */
14171
14172 DATABASE *pdb = &_database[hDB - 1];
14173 DATABASE_HEADER *pheader = pdb->database_header;
14174
14175 /* list clients */
14176 for (int i = 0; i < pheader->max_client_index; i++) {
14177 DATABASE_CLIENT *pclient = &pheader->client[i];
14178 if (pclient->pid) {
14179 for (int j = 0; j < pclient->max_index; j++) {
14180 std::string path = db_get_path_locked(pheader, pclient->open_record[j].handle);
14181 if (path.length() < root_path_len)
14182 continue;
14183 if (strncmp(root_path, path.c_str(), root_path_len) != 0)
14184 continue;
14185 MJsonNode* c = MJsonNode::MakeObject();
14186 c->AddToObject("name", MJsonNode::MakeString(pclient->name));
14187 //c->AddToObject("handle", MJsonNode::MakeNumber(pclient->open_record[j].handle));
14188 c->AddToObject("access_mode", MJsonNode::MakeNumber(pclient->open_record[j].access_mode));
14189 c->AddToObject("flags", MJsonNode::MakeNumber(pclient->open_record[j].flags));
14190 c->AddToObject("path", MJsonNode::MakeString(path.c_str()));
14191 sor->AddToArray(c);
14192 }
14193 }
14194 }
14195
14197
14198 return sor;
14199#else
14200 return MJsonNode::MakeNull();
14201#endif
14202}
14203
/* end of odbfunctionc */
14205
14206/* emacs
14207 * Local Variables:
14208 * tab-width: 8
14209 * c-basic-offset: 3
14210 * indent-tabs-mode: nil
14211 * End:
14212 */
#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:3317
#define CM_NO_CLIENT
Definition midas.h:584
#define DB_STRUCT_MISMATCH
Definition midas.h:654
#define DB_OUT_OF_RANGE
Definition midas.h:651
#define DB_NO_SEMAPHORE
Definition midas.h:637
#define DB_FULL
Definition midas.h:640
#define DB_KEY_EXIST
Definition midas.h:641
#define DB_INVALID_HANDLE
Definition midas.h:635
#define DB_INVALID_PARAM
Definition midas.h:639
#define DB_FILE_ERROR
Definition midas.h:647
#define DB_NO_ACCESS
Definition midas.h:648
#define DB_SUCCESS
Definition midas.h:631
#define DB_NO_MEMORY
Definition midas.h:633
#define DB_INVALID_NAME
Definition midas.h:634
#define DB_NO_KEY
Definition midas.h:642
#define DB_STRUCT_SIZE_MISMATCH
Definition midas.h:649
#define DB_CORRUPTED
Definition midas.h:653
#define DB_TYPE_MISMATCH
Definition midas.h:645
#define DB_CREATED
Definition midas.h:632
#define DB_INVALID_LINK
Definition midas.h:652
#define DB_OPEN_RECORD
Definition midas.h:650
#define DB_NO_MORE_SUBKEYS
Definition midas.h:646
#define DB_VERSION_MISMATCH
Definition midas.h:656
#define DB_TRUNCATED
Definition midas.h:644
#define DB_NO_SLOT
Definition midas.h:636
#define SS_SUCCESS
Definition midas.h:663
#define SS_FILE_ERROR
Definition midas.h:669
#define SS_NO_MEMORY
Definition midas.h:665
#define SS_TIMEOUT
Definition midas.h:674
#define SS_CREATED
Definition midas.h:664
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 ALIGN8(x)
Definition midas.h:522
#define MAX_STRING_LENGTH
Definition msystem.h:113
#define O_TEXT
Definition msystem.h:220
#define MSG_ODB
Definition msystem.h:296
INT ss_get_struct_align()
Definition system.cxx:1319
INT ss_suspend_get_odb_port(INT *port)
Definition system.cxx:4327
bool ss_is_valid_utf8(const char *string)
Definition system.cxx:8068
INT ss_mutex_release(MUTEX_T *mutex)
Definition system.cxx:3157
int ss_isnan(double x)
Definition system.cxx:7961
INT ss_shm_flush(const char *name, const void *adr, size_t size, HNDLE handle, bool wait_for_thread)
Definition system.cxx:1176
DWORD ss_millitime()
Definition system.cxx:3393
INT ss_semaphore_create(const char *name, HNDLE *semaphore_handle)
Definition system.cxx:2460
INT ss_mutex_delete(MUTEX_T *mutex)
Definition system.cxx:3211
INT ss_getpid(void)
Definition system.cxx:1377
INT ss_mutex_create(MUTEX_T **mutex, BOOL recursive)
Definition system.cxx:2941
INT ss_shm_open(const char *name, INT size, void **adr, size_t *shm_size, HNDLE *handle, BOOL get_size)
Definition system.cxx:324
INT ss_semaphore_release(HNDLE semaphore_handle)
Definition system.cxx:2781
void ss_stack_history_entry(char *tag)
Definition system.cxx:8022
std::string ss_tid_to_string(midas_thread_t thread_id)
Definition system.cxx:1571
DWORD ss_time()
Definition system.cxx:3462
INT ss_resume(INT port, const char *message)
Definition system.cxx:4844
midas_thread_t ss_gettid(void)
Definition system.cxx:1519
INT ss_semaphore_delete(HNDLE semaphore_handle, INT destroy_flag)
Definition system.cxx:2869
double ss_time_sec()
Definition system.cxx:3467
double ss_nan()
Definition system.cxx:7940
INT ss_semaphore_wait_for(HNDLE semaphore_handle, DWORD timeout_millisec)
Definition system.cxx:2639
INT ss_shm_unprotect(HNDLE handle, void **adr, size_t shm_size, BOOL read, BOOL write, const char *caller_name)
Definition system.cxx:1060
INT ss_shm_close(const char *name, void *adr, size_t shm_size, HNDLE handle, INT destroy_flag)
Definition system.cxx:755
INT send_tcp(int sock, char *buffer, DWORD buffer_size, INT flags)
Definition system.cxx:5279
BOOL ss_pid_exists(int pid)
Definition system.cxx:1440
INT ss_shm_protect(HNDLE handle, void *adr, size_t shm_size)
Definition system.cxx:1003
INT ss_mutex_wait_for(MUTEX_T *mutex, INT timeout)
Definition system.cxx:3037
INT cm_msg_flush_buffer()
Definition midas.cxx:865
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:915
BOOL equal_ustring(const char *str1, const char *str2)
Definition odb.cxx:3201
static void free_key(DATABASE_HEADER *pheader, void *address, INT size)
Definition odb.cxx:277
INT db_find_key1(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4125
INT db_sprintfh(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10983
INT db_flush_database(HNDLE hDB)
Definition odb.cxx:2268
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:9759
INT db_remove_open_record(HNDLE hDB, HNDLE hKey, BOOL lock)
Definition odb.cxx:12498
static void check_open_keys(HNDLE hDB, HNDLE hKey, KEY *pkey, INT level, void *info)
Definition odb.cxx:12734
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:6893
INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
Definition odb.cxx:3856
INT db_send_changed_records()
Definition odb.cxx:13777
INT db_find_link(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4274
INT db_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition odb.cxx:3059
static void * malloc_key(DATABASE_HEADER *pheader, INT size, const char *caller)
Definition odb.cxx:202
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:7057
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5415
static int db_delete_client_info_wlocked(HNDLE hDB, DATABASE_HEADER *pheader, int pid, db_err_msg **msg)
Definition odb.cxx:2760
INT db_reorder_key(HNDLE hDB, HNDLE hKey, INT idx)
Definition odb.cxx:6361
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:1030
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:10527
static INT _record_list_entries
Definition odb.cxx:59
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:13291
INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE *hDB, const char *client_name)
Definition odb.cxx:1787
static int db_paste_node(HNDLE hDB, HNDLE hKeyRoot, PMXML_NODE node)
Definition odb.cxx:8780
INT db_get_open_records(HNDLE hDB, HNDLE hKey, char *str, INT buf_size, BOOL fix)
Definition odb.cxx:5188
INT db_paste_xml(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
Definition odb.cxx:8998
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:7101
void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition odb.cxx:2827
INT db_show_mem(HNDLE hDB, char **result, BOOL verbose)
Definition odb.cxx:670
static const KEY * db_resolve_link_locked(const DATABASE_HEADER *, const KEY *, int *pstatus, db_err_msg **)
Definition odb.cxx:1094
INT db_lock_database(HNDLE hDB)
Definition odb.cxx:2455
static bool db_validate_hkey(const DATABASE_HEADER *pheader, HNDLE hKey)
Definition odb.cxx:917
std::string strcomb1(const char **list)
Definition odb.cxx:598
INT db_delete_key1(HNDLE hDB, HNDLE hKey, INT level, BOOL follow_links)
Definition odb.cxx:3669
static void * malloc_data(DATABASE_HEADER *pheader, INT size)
Definition odb.cxx:360
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:9811
INT db_save_xml(HNDLE hDB, HNDLE hKey, const char *filename)
Definition odb.cxx:9480
INT db_set_link_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7425
static INT db_find_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, const char *key_name, HNDLE *subhKey, db_err_msg **msg)
Definition odb.cxx:4031
INT db_get_path(HNDLE hDB, HNDLE hKey, char *path, INT buf_size)
Definition odb.cxx:4990
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:10352
INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align, const char *rec_str)
Definition odb.cxx:11805
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:3364
INT db_copy(HNDLE hDB, HNDLE hKey, char *buffer, INT *buffer_size, const char *path)
Definition odb.cxx:8217
INT db_set_link_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7754
BOOL strmatch(char *pattern, char *str)
Definition odb.cxx:3235
INT db_get_record_size(HNDLE hDB, HNDLE hKey, INT align, INT *buf_size)
Definition odb.cxx:11615
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:7148
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:5506
INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6539
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3308
INT db_check_record(HNDLE hDB, HNDLE hKey, const char *keyname, const char *rec_str, BOOL correct)
Definition odb.cxx:12972
static int validate_free_data(DATABASE_HEADER *pheader, int free_data)
Definition odb.cxx:336
INT db_copy_json_save(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:10475
INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, int *buffer_size, bool header)
Definition odb.cxx:9037
INT db_unlock_database(HNDLE hDB)
Definition odb.cxx:2577
MJsonNode * db_scl(HNDLE hDB)
Definition odb.cxx:14111
static RECORD_LIST * _record_list
Definition odb.cxx:58
INT db_save_struct(HNDLE hDB, HNDLE hKey, const char *file_name, const char *struct_name, BOOL append)
Definition odb.cxx:10626
INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align)
Definition odb.cxx:11709
static void * realloc_data(DATABASE_HEADER *pheader, void *address, INT old_size, INT new_size, const char *caller)
Definition odb.cxx:529
void xml_encode(char *src, int size)
Definition odb.cxx:9303
INT db_unwatch(HNDLE hDB, HNDLE hKey)
Definition odb.cxx:13887
INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
Definition odb.cxx:8027
INT db_get_next_link(HNDLE hDB, HNDLE hKey, HNDLE *subkey_handle)
Definition odb.cxx:5816
INT db_scan_tree_link(HNDLE hDB, HNDLE hKey, INT level, void(*callback)(HNDLE, HNDLE, KEY *, INT, void *), void *info)
Definition odb.cxx:4846
INT db_save(HNDLE hDB, HNDLE hKey, const char *filename, BOOL bRemote)
Definition odb.cxx:9245
static bool db_validate_and_repair_db_wlocked(DATABASE_HEADER *pheader, db_err_msg **msg)
Definition odb.cxx:1659
INT db_allow_write_locked(DATABASE *p, const char *caller_name)
Definition odb.cxx:2548
INT db_scan_tree(HNDLE hDB, HNDLE hKey, INT level, INT(*callback)(HNDLE, HNDLE, KEY *, INT, void *), void *info)
Definition odb.cxx:4767
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6019
static INT db_get_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, KEY *key, db_err_msg **msg)
Definition odb.cxx:5942
static int _global_open_count
Definition odb.cxx:12732
INT db_get_link(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6072
static INT _database_entries
Definition odb.cxx:55
static const KEYLIST * db_get_pkeylist(const DATABASE_HEADER *pheader, HNDLE hKey, const KEY *pkey, const char *caller, db_err_msg **msg, bool kludge_repair=false)
Definition odb.cxx:982
static bool db_validate_data_offset(const DATABASE_HEADER *pheader, int offset)
Definition odb.cxx:905
static int db_remove_open_record_wlocked(DATABASE *pdb, DATABASE_HEADER *pheader, HNDLE hKey)
Definition odb.cxx:12452
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:10250
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:12049
INT db_load(HNDLE hDB, HNDLE hKeyRoot, const char *filename, BOOL bRemote)
Definition odb.cxx:8126
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:13934
INT db_sprintff(char *string, const char *format, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10919
static void db_delete_client_wlocked(DATABASE_HEADER *pheader, int jclient, db_err_msg **msg)
Definition odb.cxx:2725
static void add_to_buf(struct print_key_info_buf *buf, const char *s)
Definition odb.cxx:620
INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition odb.cxx:3014
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7648
static HNDLE db_pkey_to_hkey(const DATABASE_HEADER *pheader, const KEY *pkey)
Definition odb.cxx:1025
static bool db_validate_key_offset(const DATABASE_HEADER *pheader, int offset)
Definition odb.cxx:893
static int free_data(DATABASE_HEADER *pheader, void *address, INT size, const char *caller)
Definition odb.cxx:448
static void db_save_tree_struct(HNDLE hDB, HNDLE hKey, int hfile, INT level)
Definition odb.cxx:9118
INT db_find_keys(HNDLE hDB, HNDLE hKeyRoot, char *odbpath, std::vector< HNDLE > &hKeyVector)
Definition odb.cxx:4586
INT db_save_string(HNDLE hDB, HNDLE hKey, const char *file_name, const char *string_name, BOOL append)
Definition odb.cxx:10687
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:1519
INT db_copy_json_index(HNDLE hDB, HNDLE hKey, int index, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:10219
INT db_close_all_records()
Definition odb.cxx:13514
static const KEY * db_enum_next_locked(const DATABASE_HEADER *pheader, const KEY *pdir, const KEY *pkey, db_err_msg **msg)
Definition odb.cxx:1086
INT db_paste(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
Definition odb.cxx:8487
INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER *writer)
Definition odb.cxx:9346
static void static void db_print_msg(const db_err_msg *msg)
Definition odb.cxx:113
INT db_watch(HNDLE hDB, HNDLE hKey, void(*dispatcher)(INT, INT, INT, void *), void *info)
Definition odb.cxx:13813
static std::string db_get_path_locked(const DATABASE_HEADER *pheader, HNDLE hKey)
Definition odb.cxx:4962
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:6191
INT db_close_all_databases(void)
Definition odb.cxx:2360
INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7215
INT db_enum_link(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5725
static const KEY * db_get_pkey(const DATABASE_HEADER *pheader, HNDLE hKey, int *pstatus, const char *caller, db_err_msg **msg)
Definition odb.cxx:930
INT db_copy_json_ls(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:10421
void name2c(char *str)
Definition odb.cxx:9097
char * strcomb(const char **list)
Definition odb.cxx:571
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:7014
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:10384
static WATCH_LIST * _watch_list
Definition odb.cxx:61
static int db_fix_open_records(HNDLE hDB, HNDLE hKey, KEY *key, INT level, void *xresult)
Definition odb.cxx:5146
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10843
INT db_update_last_activity(DWORD millitime)
Definition odb.cxx:2692
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:10508
void strarrayindex(char *odbpath, int *index1, int *index2)
Definition odb.cxx:3273
INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7313
DATABASE_CLIENT * db_get_my_client_locked(DATABASE *pdb)
Definition odb.cxx:1365
static int db_set_mode_wlocked(DATABASE_HEADER *, KEY *, WORD mode, int recurse, db_err_msg **)
Definition odb.cxx:7979
INT db_get_data1(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type, INT *num_values)
Definition odb.cxx:6756
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:10443
static int db_set_value_wlocked(DATABASE_HEADER *pheader, HNDLE hDB, 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:5300
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:5261
static int db_find_open_records(HNDLE hDB, HNDLE hKey, KEY *key, INT level, void *xresult)
Definition odb.cxx:5104
static int db_notify_clients_locked(const DATABASE_HEADER *pheader, HNDLE hDB, HNDLE hKeyMod, int index, BOOL bWalk, db_err_msg **msg)
Definition odb.cxx:12539
static void json_write_data(char **buffer, int *buffer_size, int *buffer_end, int level, const KEY *key, const char *p)
Definition odb.cxx:9642
static INT _watch_list_entries
Definition odb.cxx:62
static const KEY * db_find_pkey_locked(const DATABASE_HEADER *pheader, const KEY *pkey, const char *key_name, int *pstatus, db_err_msg **msg)
Definition odb.cxx:3865
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4079
static const KEY * db_enum_first_locked(const DATABASE_HEADER *pheader, const KEY *pkey, db_err_msg **msg)
Definition odb.cxx:1056
INT db_update_record_local(INT hDB, INT hKeyRoot, INT hKey, int index)
Definition odb.cxx:13552
INT db_find_link1(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4439
static void db_recurse_record_tree_locked(HNDLE hDB, const DATABASE_HEADER *pheader, 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:11439
INT db_copy_json_array(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:10150
MJsonNode * db_sor(HNDLE hDB, const char *root_path)
Definition odb.cxx:14161
INT db_get_link_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6656
void db_set_watchdog_params(DWORD timeout)
Definition odb.cxx:2976
static void db_validate_sizes()
Definition odb.cxx:1411
INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
Definition odb.cxx:13599
INT db_rename_key(HNDLE hDB, HNDLE hKey, const char *name)
Definition odb.cxx:6261
BOOL ends_with_ustring(const char *str, const char *suffix)
Definition odb.cxx:3222
void db_cleanup2(const char *client_name, int ignore_timeout, DWORD actual_time, const char *who)
Definition odb.cxx:2897
void json_write(char **buffer, int *buffer_size, int *buffer_end, int level, const char *s, int quoted)
Definition odb.cxx:9527
INT EXPRT db_paste_json(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
static bool is_utf8(const char *string)
Definition odb.cxx:822
INT db_get_key_time(HNDLE hDB, HNDLE hKey, DWORD *delta)
Definition odb.cxx:6132
int db_delete_client_info(HNDLE hDB, int pid)
Definition odb.cxx:2791
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)
Definition odb.cxx:1145
static void db_flush_msg(db_err_msg **msg)
Definition odb.cxx:167
static int db_validate_name(const char *name, int maybe_path, const char *caller_name, db_err_msg **msg)
Definition odb.cxx:833
INT db_set_client_name(HNDLE hDB, const char *client_name)
Definition odb.cxx:2402
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:5365
INT db_notify_clients_array(HNDLE hDB, HNDLE hKeys[], INT size)
Definition odb.cxx:12623
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:13441
INT db_merge_data(HNDLE hDB, HNDLE hKeyRoot, const char *name, void *data, INT data_size, INT num_values, INT type)
Definition odb.cxx:7928
INT db_notify_clients(HNDLE hDB, HNDLE hKeyMod, int index, BOOL bWalk)
Definition odb.cxx:12594
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12291
INT db_set_data_index1(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type, BOOL bNotify)
Definition odb.cxx:7828
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:11866
INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5586
INT EXPRT db_resize_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int num_values, int max_string_length)
Definition odb.cxx:14025
INT db_close_record(HNDLE hDB, HNDLE hKey)
Definition odb.cxx:13473
static int db_validate_open_records_wlocked(DATABASE_HEADER *pheader, db_err_msg **msg)
Definition odb.cxx:1596
INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
Definition odb.cxx:12800
INT db_sscanf(const char *data_str, void *data, INT *data_size, INT i, DWORD tid)
Definition odb.cxx:11314
static db_err_msg * _last_error_message
Definition odb.cxx:111
INT db_unwatch_all()
Definition odb.cxx:13917
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:121
INT db_add_open_record(HNDLE hDB, HNDLE hKey, WORD access_mode)
Definition odb.cxx:12363
INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string *s)
Definition odb.cxx:14005
INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
Definition odb.cxx:2664
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:7502
INT db_get_record2(HNDLE hDB, HNDLE hKey, void *data, INT *xbuf_size, INT align, const char *rec_str, BOOL correct)
Definition odb.cxx:12169
INT db_create_link(HNDLE hDB, HNDLE hKey, const char *link_name, const char *destination)
Definition odb.cxx:3601
static void merge_records(HNDLE hDB, HNDLE hKey, KEY *pkey, INT level, void *info)
Definition odb.cxx:12659
INT db_close_database(HNDLE hDB)
Definition odb.cxx:2152
INT db_protect_database(HNDLE hDB)
Definition odb.cxx:3167
static INT print_key_info(HNDLE hDB, HNDLE hKey, KEY *pkey, INT level, void *info)
Definition odb.cxx:638
struct db_err_msg_struct db_err_msg
Definition odb.cxx:69
const char * extract_key(const char *key_list, char *key_name, int key_name_length)
Definition odb.cxx:3187
INT db_get_free_mem(HNDLE hDB, INT *key_size, INT *data_size)
Definition odb.cxx:786
static DATABASE * _database
Definition odb.cxx:54
static void json_ensure_decimal_dot(char *str)
Definition odb.cxx:9627
#define RPC_DB_ADD_OPEN_RECORD
Definition mrpc.h:72
#define RPC_DB_SET_DATA_INDEX
Definition mrpc.h:67
void rpc_convert_data(void *data, INT tid, INT flags, INT total_size, INT convert_flags)
Definition midas.cxx:11706
#define RPC_DB_CLOSE_DATABASE
Definition mrpc.h:53
#define RPC_DB_CREATE_RECORD
Definition mrpc.h:80
#define RPC_DB_LOAD
Definition mrpc.h:75
#define RPC_DB_CREATE_LINK
Definition mrpc.h:56
#define RPC_DB_NOTIFY_CLIENTS_ARRAY
Definition mrpc.h:96
#define RPC_DB_ENUM_LINK
Definition mrpc.h:78
#define RPC_DB_DELETE_KEY
Definition mrpc.h:62
#define RPC_DB_SET_NUM_VALUES
Definition mrpc.h:88
#define RPC_DB_COPY_XML
Definition mrpc.h:98
#define RPC_DB_ENUM_KEY
Definition mrpc.h:63
bool rpc_is_remote(void)
Definition midas.cxx:12761
const char * rpc_tid_name_old(INT id)
Definition midas.cxx:11771
#define RPC_DB_SET_VALUE
Definition mrpc.h:57
#define RPC_DB_SET_CLIENT_NAME
Definition mrpc.h:76
#define RPC_DB_GET_DATA1
Definition mrpc.h:87
#define RPC_DB_REMOVE_OPEN_RECORD
Definition mrpc.h:73
#define RPC_DB_CLOSE_ALL_DATABASES
Definition mrpc.h:54
#define RPC_DB_SET_LINK_DATA_INDEX
Definition mrpc.h:94
#define RPC_DB_RENAME_KEY
Definition mrpc.h:77
#define RPC_DB_GET_NEXT_LINK
Definition mrpc.h:90
#define RPC_DB_GET_DATA
Definition mrpc.h:65
#define RPC_DB_SET_MODE
Definition mrpc.h:68
INT rpc_call(DWORD routine_id,...)
Definition midas.cxx:13663
const char * rpc_tid_name(INT id)
Definition midas.cxx:11764
#define RPC_DB_FIND_LINK
Definition mrpc.h:60
#define RPC_DB_GET_LINK
Definition mrpc.h:91
#define RPC_DB_GET_KEY
Definition mrpc.h:64
#define RPC_DB_SET_DATA
Definition mrpc.h:66
#define RPC_DB_SET_LINK_DATA
Definition mrpc.h:93
#define RPC_DB_GET_LINK_DATA
Definition mrpc.h:92
#define RPC_DB_SET_DATA_INDEX1
Definition mrpc.h:85
#define RPC_DB_GET_PARENT
Definition mrpc.h:97
#define RPC_DB_REORDER_KEY
Definition mrpc.h:79
#define RPC_DB_OPEN_DATABASE
Definition mrpc.h:52
#define RPC_DB_CHECK_RECORD
Definition mrpc.h:89
#define RPC_DB_GET_DATA_INDEX
Definition mrpc.h:81
int rpc_name_tid(const char *name)
Definition midas.cxx:11778
#define RPC_DB_GET_VALUE
Definition mrpc.h:58
#define RPC_DB_GET_KEY_INFO
Definition mrpc.h:86
bool rpc_is_mserver(void)
Definition midas.cxx:12818
#define RPC_DB_FLUSH_DATABASE
Definition mrpc.h:84
#define RPC_DB_SAVE
Definition mrpc.h:74
#define RPC_DB_GET_PATH
Definition mrpc.h:61
#define RPC_DB_GET_KEY_TIME
Definition mrpc.h:82
#define RPC_DB_SET_RECORD
Definition mrpc.h:71
#define RPC_DB_GET_RECORD_SIZE
Definition mrpc.h:69
#define RPC_DB_FIND_KEY
Definition mrpc.h:59
#define RPC_DB_GET_OPEN_RECORDS
Definition mrpc.h:83
INT rpc_get_convert_flags(void)
Definition midas.cxx:13030
INT rpc_tid_size(INT id)
Definition midas.cxx:11757
void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
Definition midas.cxx:11681
#define RPC_DB_SET_DATA1
Definition mrpc.h:95
#define RPC_DB_CREATE_KEY
Definition mrpc.h:55
#define RPC_DB_GET_RECORD
Definition mrpc.h:70
void ** info
Definition fesimdaq.cxx:41
HNDLE hKey
DWORD n[4]
Definition mana.cxx:247
INT index
Definition mana.cxx:271
char protect[10][256]
Definition mana.cxx:251
void * data
Definition mana.cxx:268
INT odb_size
Definition analyzer.cxx:46
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:1462
INT _semaphore_elog
Definition midas.cxx:1463
INT _semaphore_history
Definition midas.cxx:1464
std::string msprintf(const char *format,...)
Definition midas.cxx:410
#define JSFLAG_RECURSE
Definition midas.h:1724
#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:1719
int INT
Definition midas.h:129
#define JSFLAG_FOLLOW_LINKS
Definition midas.h:1723
#define JSFLAG_LOWERCASE
Definition midas.h:1725
#define MATTRPRINTF(a, b)
Definition midas.h:221
#define JSFLAG_OMIT_LAST_WRITTEN
Definition midas.h:1727
#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:1585
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:1726
#define WATCHDOG_INTERVAL
Definition midas.h:288
#define JS_LEVEL_1
Definition midas.h:1720
#define MAX_OPEN_RECORDS
Definition midas.h:276
#define TRUE
Definition midas.h:182
#define JS_MUST_BE_SUBDIR
Definition midas.h:1721
unsigned char UINT8
Definition midas.h:136
#define POINTER_T
Definition midas.h:166
#define JSFLAG_SAVE_KEYS
Definition midas.h:1722
unsigned int UINT32
Definition midas.h:140
#define JSFLAG_OMIT_OLD
Definition midas.h:1728
#define RPC_FIXARRAY
Definition midas.h:1583
#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)
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
TH1X EXPRT * h1_book(const char *name, const char *title, int bins, double min, double max)
Definition rmidas.h:24
Definition midas.h:1215
DWORD last_activity
Definition msystem.h:417
char name[NAME_LENGTH]
Definition msystem.h:411
OPEN_RECORD open_record[MAX_OPEN_RECORDS]
Definition msystem.h:421
DWORD watchdog_timeout
Definition msystem.h:418
char name[NAME_LENGTH]
Definition msystem.h:426
DATABASE_CLIENT client[MAX_CLIENTS]
Definition msystem.h:436
INT first_free_data
Definition msystem.h:434
INT max_client_index
Definition msystem.h:429
BOOL protect
Definition msystem.h:453
void * database_data
Definition msystem.h:447
INT timeout
Definition msystem.h:457
HNDLE shm_size
Definition msystem.h:451
BOOL protect_read
Definition msystem.h:454
INT lock_cnt
Definition msystem.h:449
INT client_index
Definition msystem.h:445
HNDLE semaphore
Definition msystem.h:448
HNDLE shm_handle
Definition msystem.h:452
DATABASE_HEADER * database_header
Definition msystem.h:446
BOOL attached
Definition msystem.h:444
BOOL protect_write
Definition msystem.h:455
BOOL inside_lock_unlock
Definition msystem.h:458
void * shm_adr
Definition msystem.h:450
Definition midas.h:1026
INT num_values
Definition midas.h:1028
DWORD type
Definition midas.h:1027
WORD notify_count
Definition midas.h:1034
INT total_size
Definition midas.h:1031
WORD access_mode
Definition midas.h:1033
INT last_written
Definition midas.h:1037
char name[NAME_LENGTH]
Definition midas.h:1029
INT item_size
Definition midas.h:1032
INT num_keys
Definition midas.h:1042
NET_COMMAND_HEADER header
Definition msystem.h:286
char param[32]
Definition msystem.h:287
WORD access_mode
Definition msystem.h:467
void(* dispatcher)(INT, INT, void *)
Definition msystem.h:471
void * info
Definition msystem.h:472
INT buf_size
Definition msystem.h:470
HNDLE handle
Definition msystem.h:465
void * data
Definition msystem.h:468
void * copy
Definition msystem.h:469
HNDLE hDB
Definition msystem.h:466
Definition midas.h:1234
DATABASE_HEADER * pheader
Definition odb.cxx:1510
HNDLE handle
Definition msystem.h:479
HNDLE hDB
Definition msystem.h:480
void * info
Definition msystem.h:482
void(* dispatcher)(INT, INT, INT, void *info)
Definition msystem.h:481
std::string routine
Definition odb.cxx:107
std::string filename
Definition odb.cxx:105
std::string text
Definition odb.cxx:108
db_err_msg * next
Definition odb.cxx:103
char c
Definition system.cxx:1310
static double comma(double a, double b)
Definition tinyexpr.c:248
static te_expr * list(state *s)
Definition tinyexpr.c:567