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 assert(str != NULL);
588
589 str[0] = 0;
590 for (i = 0; list[i]; i++) {
591 strcat(str, list[i]);
592 strcat(str, "\n");
593 }
594
595 return str;
596}
597
598/*------------------------------------------------------------------*/
599
600std::string strcomb1(const char **list)
601/* convert list of strings into single string to be used by db_paste() */
602{
603 std::string s;
604
605 for (int i = 0; list[i]; i++) {
606 s += list[i];
607 s += "\n";
608 }
609
610 return s;
611}
612
613/*------------------------------------------------------------------*/
614
616{
618 int used;
619 char* buf;
620};
621
622static void add_to_buf(struct print_key_info_buf* buf, const char* s)
623{
624 int len = strlen(s);
625 if (buf->used + len + 10 > buf->alloc_size) {
626 int new_size = 1024 + 2*buf->alloc_size + len;
627 //printf("realloc %d->%d, used %d, adding %d\n", buf->alloc_size, new_size, buf->used, len);
628 buf->buf = (char*)realloc(buf->buf, new_size);
629 assert(buf->buf != NULL);
630 buf->alloc_size = new_size;
631 }
632
633 memcpy(buf->buf + buf->used, s, len);
634 buf->used += len;
635 buf->buf[buf->used] = 0; // zero-terminate the string
636}
637
638#ifdef LOCAL_ROUTINES
639
641{
643 int i;
644
645 char str[256];
646
647 sprintf(str, "%08X %08X %08X ",
648 (int) (hKey - sizeof(DATABASE_HEADER)),
649 (int) (pkey->data - sizeof(DATABASE_HEADER)), (int) pkey->total_size);
650
651 assert(strlen(str)+10 < sizeof(str));
652
653 for (i = 0; i < level; i++)
654 strcat(str, " ");
655
656 assert(strlen(str)+10 < sizeof(str));
657
658 strcat(str, pkey->name);
659 strcat(str, "\n");
660
661 assert(strlen(str)+10 < sizeof(str));
662
663 //printf("str [%s]\n", str);
664
666
667 return SUCCESS;
668}
669
670static bool db_validate_data_offset(const DATABASE_HEADER * pheader, int offset);
671
673{
675
676 struct print_key_info_buf buf;
677 buf.buf = NULL;
678 buf.used = 0;
679 buf.alloc_size = 0;
680
682
684
685 char str[256];
686
687 sprintf(str, "Database header size is 0x%04X, all following values are offset by this!\n", (int)sizeof(DATABASE_HEADER));
688 add_to_buf(&buf, str);
689 sprintf(str, "Key area 0x00000000 - 0x%08X, size %d bytes\n", pheader->key_size - 1, pheader->key_size);
690 add_to_buf(&buf, str);
691 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);
692 add_to_buf(&buf, str);
693
694 add_to_buf(&buf, "Keylist:\n");
695 add_to_buf(&buf, "--------\n");
696 total_size_key = 0;
697
698 if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
699 add_to_buf(&buf, "ODB is corrupted: pheader->first_free_key is invalid\n");
701 if (result) {
702 *result = buf.buf;
703 } else {
704 free(buf.buf);
705 }
706 return DB_CORRUPTED;
707 }
708
709 FREE_DESCRIP *pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
710
711 while ((POINTER_T) pfree != (POINTER_T) pheader) {
713 sprintf(str, "Free block at 0x%08X, size 0x%08X, next 0x%08X\n",
714 (int) ((POINTER_T) pfree - (POINTER_T) pheader - sizeof(DATABASE_HEADER)),
715 pfree->size, pfree->next_free ? (int) (pfree->next_free - sizeof(DATABASE_HEADER)) : 0);
716 add_to_buf(&buf, str);
717 if (!db_validate_key_offset(pheader, pfree->next_free)) {
718 add_to_buf(&buf, "ODB is corrupted: next_free is invalid!");
719 break;
720 }
721 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
722 }
723
724 sprintf(str, "\nFree Key area: %d bytes out of %d bytes\n", total_size_key, pheader->key_size);
725 add_to_buf(&buf, str);
726
727 add_to_buf(&buf, "\nData:\n");
728 add_to_buf(&buf, "-----\n");
729 total_size_data = 0;
730
731 if (!db_validate_data_offset(pheader, pheader->first_free_data)) {
732 add_to_buf(&buf, "ODB is corrupted: pheader->first_free_data is invalid\n");
734 if (result) {
735 *result = buf.buf;
736 } else {
737 free(buf.buf);
738 }
739 return DB_CORRUPTED;
740 }
741
742 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
743
744 while ((POINTER_T) pfree != (POINTER_T) pheader) {
745 total_size_data += pfree->size;
746 sprintf(str, "Free block at 0x%08X, size 0x%08X, next 0x%08X\n",
747 (int) ((POINTER_T) pfree - (POINTER_T) pheader - sizeof(DATABASE_HEADER)),
748 pfree->size, pfree->next_free ? (int) (pfree->next_free - sizeof(DATABASE_HEADER)) : 0);
749 add_to_buf(&buf, str);
750 if (!db_validate_data_offset(pheader, pfree->next_free)) {
751 add_to_buf(&buf, "ODB is corrupted: next_free is invalid!");
752 break;
753 }
754 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
755 }
756
757 sprintf(str, "\nFree Data area: %d bytes out of %d bytes\n", total_size_data, pheader->data_size);
758 add_to_buf(&buf, str);
759
760 sprintf(str, "\nFree: %1d (%1.1lf%%) keylist, %1d (%1.1lf%%) data\n",
762 100 * (double) total_size_key / pheader->key_size,
763 total_size_data, 100 * (double) total_size_data / pheader->data_size);
764 add_to_buf(&buf, str);
765
766 if (verbose) {
767 add_to_buf(&buf, "\n\n");
768 add_to_buf(&buf, "Key Data Size\n");
769 add_to_buf(&buf, "----------------------------\n");
770 db_scan_tree(hDB, pheader->root_key, 0, print_key_info, &buf);
771 }
772
773 sprintf(str, "\nTotal ODB size: %d (0x%08X) Bytes, %lg MiB\n",
774 pheader->key_size + pheader->data_size, pheader->key_size + pheader->data_size,
775 ((pheader->key_size + pheader->data_size) / 1E6));
776 add_to_buf(&buf, str);
778
779 if (result) {
780 *result = buf.buf;
781 } else {
782 free(buf.buf);
783 }
784
785 return DB_SUCCESS;
786}
787
788INT db_get_free_mem(HNDLE hDB, INT *key_size, INT *data_size)
789{
790 DATABASE_HEADER *pheader;
792
793 *data_size = 0;
794 *key_size = 0;
795
797
798 pheader = _database[hDB - 1].database_header;
799
800 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
801
802 while ((POINTER_T) pfree != (POINTER_T) pheader) {
803 *key_size += pfree->size;
804 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
805 }
806
807 *data_size = 0;
808 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
809
810 while ((POINTER_T) pfree != (POINTER_T) pheader) {
811 *data_size += pfree->size;
812 pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
813 }
814
816 return DB_SUCCESS;
817}
818
819#endif // LOCAL_ROUTINES
820
821// Method to check if a given string is valid UTF-8. Returns 1 if it is.
822// This method was taken from stackoverflow user Christoph, specifically
823// http://stackoverflow.com/questions/1031645/how-to-detect-utf-8-in-plain-c
824static bool is_utf8(const char * string)
825{
826 if (!string)
827 return false;
828
829 return ss_is_valid_utf8(string);
830}
831
832#ifdef LOCAL_ROUTINES
833
834/*------------------------------------------------------------------*/
835static int db_validate_name(const char* name, int maybe_path, const char* caller_name, db_err_msg** msg)
836{
837 //printf("db_validate_name [%s] length %d, maybe_path %d from %s\n", name, (int)strlen(name), maybe_path, caller_name);
838
839 if (name == NULL) {
840 db_msg(msg, MERROR, "db_validate_name", "Invalid name passed to %s: should not be NULL", caller_name);
841 return DB_INVALID_NAME;
842 }
843
844 size_t len = strlen(name);
845
846 if (len < 1) {
847 db_msg(msg, MERROR, "db_validate_name", "Invalid name passed to %s: should not be an empty string", caller_name);
848 return DB_INVALID_NAME;
849 }
850
851 if (strchr(name, '[')) {
852 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"[\"", name, caller_name);
853 return DB_INVALID_NAME;
854 }
855
856 if (name[0] == ' ') {
857 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not start with a space", name, caller_name);
858 return DB_INVALID_NAME;
859 }
860
861 if (name[len-1] == ' ') {
862 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not end with a space", name, caller_name);
863 return DB_INVALID_NAME;
864 }
865
866 if (strchr(name, ']')) {
867 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"[\"", name, caller_name);
868 return DB_INVALID_NAME;
869 }
870
871 if (!is_utf8(name)) {
872 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: invalid unicode UTF-8 encoding", name, caller_name);
873 return DB_INVALID_NAME;
874 }
875
876 if (!maybe_path) {
877 if (strchr(name, '/')) {
878 db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"/\"", name, caller_name);
879 return DB_INVALID_NAME;
880 }
881
882 if (strlen(name) >= NAME_LENGTH) {
883 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);
884 return DB_INVALID_NAME;
885 }
886 }
887
888 //if (strcmp(name, "test")==0)
889 //return DB_INVALID_NAME;
890
891 return DB_SUCCESS;
892}
893
894/*------------------------------------------------------------------*/
895static bool db_validate_key_offset(const DATABASE_HEADER * pheader, int offset)
896/* check if key offset lies in valid range */
897{
898 if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
899 return false;
900
901 if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size)
902 return false;
903
904 return true;
905}
906
907static bool db_validate_data_offset(const DATABASE_HEADER * pheader, int offset)
908/* check if data offset lies in valid range */
909{
910 if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
911 return false;
912
913 if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size)
914 return false;
915
916 return true;
917}
918
919static bool db_validate_hkey(const DATABASE_HEADER * pheader, HNDLE hKey)
920{
921 if (hKey == 0) {
922 cm_msg(MERROR, "db_validate_hkey", "Error: invalid zero hkey %d", hKey);
923 return false;
924 }
925 if (!db_validate_key_offset(pheader, hKey)) {
926 cm_msg(MERROR, "db_validate_hkey", "Error: invalid hkey %d", hKey);
927 return false;
928 }
929 return true;
930}
931
932static const KEY* db_get_pkey(const DATABASE_HEADER* pheader, HNDLE hKey, int* pstatus, const char* caller, db_err_msg **msg)
933{
935
936 if (!hKey) {
938 hKey = pheader->root_key;
939 }
940
941 /* check if hKey argument is correct */
942 if (hKey == 0) {
943 if (pstatus)
945 return NULL;
946 }
947
948 /* check if hKey argument is correct */
949 if (!db_validate_key_offset(pheader, hKey)) {
950 if (pstatus)
952 return NULL;
953 }
954
955 const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
956
957 if (pkey->type < 1 || pkey->type >= TID_LAST) {
958 DWORD tid = pkey->type;
959 if (hKey_is_root_key) {
960 db_msg(msg, MERROR, caller, "db_get_pkey: root_key hkey %d invalid key type %d, database root directory is corrupted", hKey, tid);
961 if (pstatus)
963 return NULL;
964 } else {
965 std::string path = db_get_path_locked(pheader, hKey);
966 db_msg(msg, MERROR, caller, "db_get_pkey: hkey %d path \"%s\" invalid key type %d", hKey, path.c_str(), tid);
967 }
968 if (pstatus)
970 return NULL;
971 }
972
973 if (pkey->name[0] == 0) {
974 std::string path = db_get_path_locked(pheader, hKey);
975 db_msg(msg, MERROR, caller, "db_get_pkey: hkey %d path \"%s\" invalid name \"%s\" is empty", hKey, path.c_str(), pkey->name);
976 if (pstatus)
978 return NULL;
979 }
980
981 return pkey;
982}
983
984static 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)
985{
986 if (pkey->type != TID_KEY) {
987 std::string path = db_get_path_locked(pheader, hKey);
988 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);
989 return NULL;
990 }
991
992 if (!hKey) {
993 hKey = pheader->root_key;
994 }
995
996 if (!db_validate_data_offset(pheader, pkey->data)) {
997 std::string path = db_get_path_locked(pheader, hKey);
998 db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkey->data %d", hKey, path.c_str(), pkey->data);
999 return NULL;
1000 }
1001
1002 const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->data);
1003
1004 if (pkeylist->parent != hKey) {
1005 std::string path = db_get_path_locked(pheader, hKey);
1006 db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkeylist->parent %d should be hkey %d", hKey, path.c_str(), pkeylist->parent, hKey);
1007 return NULL;
1008 }
1009
1010 if (pkeylist->first_key == 0 && pkeylist->num_keys != 0) {
1011 if (!kludge_repair) {
1012 std::string path = db_get_path_locked(pheader, hKey);
1013 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);
1014 return NULL;
1015 }
1016
1017 // FIXME: this repair should be done in db_validate_and_repair_key()
1018
1019 std::string path = db_get_path_locked(pheader, hKey);
1020 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);
1021 ((KEYLIST*)pkeylist)->num_keys = 0;
1022 }
1023
1024 return pkeylist;
1025}
1026
1027static HNDLE db_pkey_to_hkey(const DATABASE_HEADER* pheader, const KEY* pkey)
1028{
1029 return (POINTER_T) pkey - (POINTER_T) pheader;
1030}
1031
1032static const KEY* db_get_parent(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, const char* caller, db_err_msg **msg)
1033{
1034 if (pkey->parent_keylist == 0) {
1035 // they asked for the parent of "/", return "/"
1036 return db_get_pkey(pheader, pheader->root_key, pstatus, caller, msg);
1037 }
1038
1039 if (!db_validate_data_offset(pheader, pkey->parent_keylist)) {
1040 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);
1041 if (pstatus)
1043 return NULL;
1044 }
1045
1046 const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
1047
1048 if (pkeylist->first_key == 0 && pkeylist->num_keys != 0) {
1049 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);
1050 if (pstatus)
1052 return NULL;
1053 }
1054
1055 return db_get_pkey(pheader, pkeylist->parent, pstatus, caller, msg);
1056}
1057
1058static const KEY* db_enum_first_locked(const DATABASE_HEADER *pheader, const KEY* pkey, db_err_msg **msg)
1059{
1060 HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
1061
1062 if (pkey->type != TID_KEY) {
1063 std::string path = db_get_path_locked(pheader, hKey);
1064 db_msg(msg, MERROR, "db_enum_first_locked", "hkey %d path \"%s\" tid %d is not a directory", hKey, path.c_str(), pkey->type);
1065 return NULL;
1066 }
1067
1068 const KEYLIST *pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
1069
1070 if (!pkeylist) {
1071 // error
1072 return NULL;
1073 }
1074
1075 if (pkeylist->num_keys == 0) {
1076 // empty directory
1077 return NULL;
1078 }
1079
1080 if (pkeylist->first_key == 0) {
1081 // empty directory
1082 return NULL;
1083 }
1084
1085 return db_get_pkey(pheader, pkeylist->first_key, NULL, "db_enum_first_locked", msg);
1086}
1087
1088static const KEY* db_enum_next_locked(const DATABASE_HEADER *pheader, const KEY* pdir, const KEY* pkey, db_err_msg **msg)
1089{
1090 if (pkey->next_key == 0)
1091 return NULL;
1092
1093 return db_get_pkey(pheader, pkey->next_key, NULL, "db_enum_next_locked", msg);
1094}
1095
1096static const KEY* db_resolve_link_locked(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, db_err_msg** msg)
1097{
1098 if (pkey->type != TID_LINK)
1099 return pkey;
1100
1101 // FIXME: need to validate pkey->data
1102
1103 if (*((char *) pheader + pkey->data) == '/') {
1104 return db_find_pkey_locked(pheader, NULL, (char*)pheader + pkey->data, pstatus, msg);
1105 } else {
1106 return db_find_pkey_locked(pheader, pkey, (char*)pheader + pkey->data, pstatus, msg);
1107 }
1108}
1109
1110/*
1111static void db_print_pkey(const DATABASE_HEADER * pheader, const KEY* pkey, int recurse = 0, const char *path = NULL, HNDLE parenthkeylist = 0)
1112{
1113 HNDLE hkey = db_pkey_to_hkey(pheader, pkey);
1114
1115 std::string xpath;
1116 if (path == NULL) {
1117 xpath = db_get_path_locked(pheader, hkey);
1118 path = xpath.c_str();
1119 }
1120
1121 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);
1122
1123 if (pkey->type != TID_KEY) {
1124 printf("\n");
1125 } else {
1126 const KEYLIST *pkeylist = db_get_pkeylist(pheader, hkey, pkey, "db_validate_key", NULL);
1127
1128 if (pkeylist) {
1129 printf(", pkeylist parent %d, num_keys %d, first_key %d", pkeylist->parent, pkeylist->num_keys, pkeylist->first_key);
1130 printf("\n");
1131 }
1132 }
1133}
1134
1135static void db_print_hkey(const DATABASE_HEADER * pheader, HNDLE hkey, int recurse = 0, const char *path = NULL, HNDLE parenthkeylist = 0)
1136{
1137 const KEY *pkey = db_get_pkey(pheader, hkey, NULL, "db_print_key", NULL);
1138
1139 if (!pkey) {
1140 return;
1141 }
1142
1143 db_print_pkey(pheader, pkey, recurse, path, parenthkeylist);
1144}
1145*/
1146
1148{
1149 int status;
1150 bool flag = true;
1151
1152 //printf("path \"%s\", parenthkey %d, hkey %d, pkey->name \"%s\", type %d\n", path, parenthkeylist, hkey, pkey->name, pkey->type);
1153
1154 //std::string xpath = db_get_path_locked(pheader, hkey);
1155 //if (xpath != path)
1156 // printf("hkey %d, path \"%s\" vs \"%s\"\n", hkey, path, xpath.c_str());
1157
1158 //db_print_key(pheader, 0, path, parenthkeylist, hkey);
1159
1160 if (hkey==0 || !db_validate_key_offset(pheader, hkey)) {
1161 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid hkey", hkey, path);
1162 return false;
1163 }
1164
1165 /* check key type */
1166 if (pkey->type <= 0 || pkey->type >= TID_LAST) {
1167 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", name \"%s\", invalid key type %d", hkey, path, pkey->name, pkey->type);
1168 return false;
1169 }
1170
1171 /* check key name */
1172 status = db_validate_name(pkey->name, FALSE, "db_validate_key", msg);
1173 if (status != DB_SUCCESS) {
1174 char newname[NAME_LENGTH];
1175 sprintf(newname, "%p", pkey);
1176 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\": invalid name \"%s\" replaced with \"%s\"", hkey, path, pkey->name, newname);
1177 mstrlcpy(pkey->name, newname, sizeof(pkey->name));
1178 flag = false;
1179 //return false;
1180 }
1181
1182 /* check parent */
1183 if (pkey->parent_keylist != parenthkeylist) {
1184 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);
1185 return false;
1186 }
1187
1188 if (!db_validate_data_offset(pheader, pkey->data)) {
1189 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));
1190 return false;
1191 }
1192
1193 /* check key sizes */
1194 if ((pkey->total_size < 0) || (pkey->total_size > pheader->data_size)) {
1195 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->total_size %d", hkey, path, pkey->total_size);
1196 return false;
1197 }
1198
1199 if ((pkey->item_size < 0) || (pkey->item_size > pheader->data_size)) {
1200 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->item_size: %d", hkey, path, pkey->item_size);
1201 return false;
1202 }
1203
1204 if ((pkey->num_values < 0) || (pkey->num_values > pheader->data_size)) {
1205 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->num_values: %d", hkey, path, pkey->num_values);
1206 return false;
1207 }
1208
1209 /* check and correct key size */
1210 if (pkey->total_size != pkey->item_size * pkey->num_values) {
1211 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);
1212 pkey->total_size = pkey->item_size * pkey->num_values;
1213 flag = false;
1214 }
1215
1216 /* check and correct key size */
1217 if (pkey->data == 0 && pkey->total_size != 0) {
1218 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);
1219 pkey->num_values = 0;
1220 pkey->total_size = 0;
1221 flag = false;
1222 }
1223
1224 if (pkey->type == TID_STRING || pkey->type == TID_LINK) {
1225 const char* s = (char*)pheader + pkey->data;
1226 if (!is_utf8(s)) {
1227 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", string value is not valid UTF-8", hkey, path);
1228 //flag = false;
1229 }
1230 }
1231
1232 /* check for empty link */
1233 if (pkey->type == TID_LINK) {
1234 // minimum symlink length is 3 bytes:
1235 // one byte "/"
1236 // one byte odb entry name
1237 // one byte "\0"
1238 if (pkey->total_size <= 2) {
1239 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK is an empty link", hkey, path);
1240 flag = false;
1241 //return false;
1242 }
1243 }
1244
1245 /* check for too long link */
1246 if (pkey->type == TID_LINK) {
1247 if (pkey->total_size >= MAX_ODB_PATH) {
1248 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);
1249 flag = false;
1250 //return false;
1251 }
1252 }
1253
1254 /* check for link loop */
1255 if (pkey->type == TID_LINK) {
1256 const char* link = (char*)pheader + pkey->data;
1257 int link_len = strlen(link);
1258 int path_len = strlen(path);
1259 if (link_len == path_len) {
1260 // check for link to itself
1261 if (equal_ustring(link, path)) {
1262 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK to \"%s\" is a link to itself", hkey, path, link);
1263 flag = false;
1264 //return false;
1265 }
1266 } else if (link_len < path_len) {
1267 // check for link to the "path" subdirectory
1268 char tmp[MAX_ODB_PATH];
1269 memcpy(tmp, path, link_len);
1270 tmp[link_len] = 0;
1271 if (equal_ustring(link, tmp) && path[link_len] == DIR_SEPARATOR) {
1272 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK to \"%s\" is a loop", hkey, path, link);
1273 flag = false;
1274 //return false;
1275 }
1276 }
1277 }
1278
1279 /* check access mode */
1280 if ((pkey->access_mode & ~(MODE_READ | MODE_WRITE | MODE_DELETE | MODE_EXCLUSIVE | MODE_ALLOC))) {
1281 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->access_mode %d", hkey, path, pkey->access_mode);
1282 flag = false;
1283 //return false;
1284 }
1285
1286 /* check if access time is in the future */
1287 if (pkey->last_written > 0 && ((DWORD)pkey->last_written > ss_time() + 3600)) {
1288 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->last_written time %d", hkey, path, pkey->last_written);
1289 flag = false;
1290 //return false;
1291 }
1292
1293 if (pkey->type == TID_KEY) {
1294 bool pkeylist_ok = true;
1295 // FIXME: notice the kludged repair of pkeylist! K.O.
1296 const KEYLIST *pkeylist = db_get_pkeylist(pheader, hkey, pkey, "db_validate_key", msg, true);
1297
1298 if (!pkeylist) {
1299 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->data %d", hkey, path, pkey->data);
1300 flag = false;
1301 } else {
1302 if (pkeylist->parent != hkey) {
1303 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);
1304 flag = false;
1305 pkeylist_ok = false;
1306 }
1307
1308 if (pkeylist->num_keys < 0 || pkeylist->num_keys > pheader->key_size) {
1309 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->num_keys %d", hkey, path, pkeylist->num_keys);
1310 flag = false;
1311 pkeylist_ok = false;
1312 }
1313
1314 if (pkeylist->num_keys == 0 && pkeylist->first_key == 0) {
1315 // empty key
1316 } else if (pkeylist->first_key == 0 || !db_validate_key_offset(pheader, pkeylist->first_key)) {
1317 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->first_key %d", hkey, path, pkeylist->first_key);
1318 flag = false;
1319 pkeylist_ok = false;
1320 }
1321
1322 if (pkeylist_ok) {
1323 //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);
1324
1325 HNDLE subhkey = pkeylist->first_key;
1326
1327 int count = 0;
1328 while (subhkey != 0) {
1329 KEY* subpkey = (KEY*)db_get_pkey(pheader, subhkey, NULL, "db_validate_key", msg);
1330 if (!subpkey) {
1331 db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid subhkey %d", hkey, path, subhkey);
1332 pkeylist_ok = false;
1333 flag = false;
1334 break;
1335 }
1336
1337 std::string buf;
1338 buf += path;
1339 buf += "/";
1340 buf += subpkey->name;
1341
1342 //printf("pkey %p, next %d, name [%s], path %s\n", subpkey, subpkey->next_key, subpkey->name, buf.c_str());
1343
1344 if (recurse) {
1345 flag &= db_validate_and_repair_key_wlocked(pheader, recurse + 1, buf.c_str(), pkey->data, subhkey, subpkey, msg);
1346 }
1347
1348 count++;
1349 subhkey = subpkey->next_key;
1350 }
1351
1352 if (count != pkeylist->num_keys) {
1353 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);
1354 ((KEYLIST*)pkeylist)->num_keys = count;
1355 flag = false;
1356 pkeylist_ok = false;
1357 }
1358 }
1359 }
1360 }
1361
1362 return flag;
1363}
1364
1365/*------------------------------------------------------------------*/
1366
1368{
1369 assert(pdb);
1370 assert(pdb->database_header);
1371
1372 DATABASE_HEADER *pheader = pdb->database_header;
1373
1374 int idx = pdb->client_index;
1375
1376 if (idx < 0 || idx >= pheader->max_client_index || idx >= MAX_CLIENTS) {
1377 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);
1378 ss_semaphore_release(pdb->semaphore);
1379 abort();
1380 }
1381
1382 DATABASE_CLIENT* pclient = &pheader->client[idx]; // safe to dereference on "idx"
1383
1384 if (pclient->name[0] == 0) {
1385 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);
1386 ss_semaphore_release(pdb->semaphore);
1387 abort();
1388 }
1389
1390 int pid = getpid();
1391
1392 if (pclient->pid != pid) {
1393 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);
1394 ss_semaphore_release(pdb->semaphore);
1395 abort();
1396 }
1397
1398 //printf("db_get_my_client_locked: idx %d, name [%s], pid %d\n", idx, pclient->name, pid);
1399
1400 //if (strcmp(pclient->name, "mdump") == 0) {
1401 // ss_semaphore_release(pdb->semaphore);
1402 // for (int i=0; i<15; i++) {
1403 // printf("sleep %d\n", i);
1404 // ::sleep(1);
1405 // }
1406 // abort();
1407 //}
1408
1409 return pclient;
1410}
1411
1412/*------------------------------------------------------------------*/
1414{
1415 /* validate size of data structures (miscompiled, 32/64 bit mismatch, etc */
1416
1417 if (0) {
1418#define S(x) printf("assert(sizeof(%-20s) == %6d);\n", #x, (int)sizeof(x))
1419 // basic data types
1420 S(char *);
1421 S(char);
1422 S(int);
1423 S(long int);
1424 S(float);
1425 S(double);
1426 S(BOOL);
1427 S(WORD);
1428 S(DWORD);
1429 S(INT);
1430 S(POINTER_T);
1432 // data buffers
1436 // history files
1437 S(HIST_RECORD);
1438 S(DEF_RECORD);
1439 S(INDEX_RECORD);
1440 S(TAG);
1441 // ODB shared memory structures
1442 S(KEY);
1443 S(KEYLIST);
1444 S(OPEN_RECORD);
1447 // misc structures
1448 S(EVENT_HEADER);
1449 S(RUNINFO);
1452 S(BANK_HEADER);
1453 S(BANK);
1454 S(BANK32);
1456 S(PROGRAM_INFO);
1457 S(ALARM_CLASS);
1458 S(ALARM);
1459 //S(CHN_SETTINGS);
1460 //S(CHN_STATISTICS);
1461#undef S
1462 }
1463
1464#if 0
1466 printf("EQUIPMENT_INFO offset of event_id: %d\n", (int)((char*)&eq.event_id - (char*)&eq));
1467 printf("EQUIPMENT_INFO offset of eq_type: %d\n", (int)((char*)&eq.eq_type - (char*)&eq));
1468 printf("EQUIPMENT_INFO offset of event_limit: %d\n", (int)((char*)&eq.event_limit - (char*)&eq));
1469 printf("EQUIPMENT_INFO offset of num_subevents: %d\n", (int)((char*)&eq.num_subevents - (char*)&eq));
1470 printf("EQUIPMENT_INFO offset of status: %d\n", (int)((char*)&eq.status - (char*)&eq));
1471 printf("EQUIPMENT_INFO offset of hidden: %d\n", (int)((char*)&eq.hidden - (char*)&eq));
1472#endif
1473
1474 assert(sizeof(UINT8) == 1);
1475 assert(sizeof(INT8) == 1);
1476 assert(sizeof(UINT16) == 2);
1477 assert(sizeof(INT16) == 2);
1478 assert(sizeof(UINT32) == 4);
1479 assert(sizeof(INT32) == 4);
1480 assert(sizeof(UINT64) == 8);
1481 assert(sizeof(INT64) == 8);
1482
1483#ifdef OS_LINUX
1484 assert(sizeof(EVENT_REQUEST) == 16); // ODB v3
1485 assert(sizeof(BUFFER_CLIENT) == 256);
1486 assert(sizeof(BUFFER_HEADER) == 16444);
1487 assert(sizeof(HIST_RECORD) == 20);
1488 assert(sizeof(DEF_RECORD) == 40);
1489 assert(sizeof(INDEX_RECORD) == 12);
1490 assert(sizeof(TAG) == 40);
1491 assert(sizeof(KEY) == 68);
1492 assert(sizeof(KEYLIST) == 12);
1493 assert(sizeof(OPEN_RECORD) == 8);
1494 assert(sizeof(DATABASE_CLIENT) == 2112);
1495 assert(sizeof(DATABASE_HEADER) == 135232);
1496 assert(sizeof(EVENT_HEADER) == 16);
1497 //assert(sizeof(EQUIPMENT_INFO) == 696); has been moved to dynamic checking inside mhttpd.c
1498 assert(sizeof(EQUIPMENT_STATS) == 24);
1499 assert(sizeof(BANK_HEADER) == 8);
1500 assert(sizeof(BANK) == 8);
1501 assert(sizeof(BANK32) == 12);
1502 assert(sizeof(ANA_OUTPUT_INFO) == 792);
1503 assert(sizeof(PROGRAM_INFO) == 316);
1504 assert(sizeof(ALARM) == 460);
1505 assert(sizeof(ALARM_CLASS) == 352);
1506 //assert(sizeof(CHN_SETTINGS) == 648); // ODB v3
1507 //assert(sizeof(CHN_STATISTICS) == 56); // ODB v3
1508#endif
1509}
1510
1520
1522{
1523 int found = 0;
1524 int count = 0;
1525 int status;
1526 int k;
1528
1529 KEY* pkey = (KEY*)xpkey; // drop "const": we already have "allow_write"
1530
1531 HNDLE hKey = db_pkey_to_hkey(uorp->pheader, pkey);
1532
1533 for (k=0; k<uorp->num_keys; k++)
1534 if (uorp->hkeys[k] == hKey) {
1535 found = 1;
1536 count = uorp->counts[k];
1537 break;
1538 }
1539
1540 if (pkey->notify_count == 0 && !found)
1541 return DB_SUCCESS; // no open record here
1542
1543 std::string path = db_get_path_locked(uorp->pheader, hKey);
1544 if (path == "") {
1545 db_msg(msg, MINFO, "db_update_open_record", "Invalid hKey %d", hKey);
1546 return DB_SUCCESS;
1547 }
1548
1549 //if (!db_validate_hkey(uorp->pheader, hKey)) {
1550 // cm_msg(MINFO, "db_update_open_record", "Invalid hKey %d", hKey);
1551 // return DB_SUCCESS;
1552 //}
1553 //
1554 //KEY* pkey = (KEY *) ((char *) uorp->pheader + hKey);
1555
1556 //printf("path [%s], type %d, notify_count %d\n", path, pkey->type, pkey->notify_count);
1557
1558 // extra check: are we looking at the same key?
1559 //assert(xkey->notify_count == pkey->notify_count);
1560
1561#if 0
1562 printf("%s, notify_count %d, found %d, our count %d\n", path, pkey->notify_count, found, count);
1563#endif
1564
1565 if (pkey->notify_count==0 && found) {
1566 db_msg(msg, MINFO, "db_update_open_record", "Added missing open record flag to \"%s\"", path.c_str());
1567 pkey->notify_count = count;
1568 uorp->num_modified++;
1569 return DB_SUCCESS;
1570 }
1571
1572 if (pkey->notify_count!=0 && !found) {
1573 db_msg(msg, MINFO, "db_update_open_record", "Removed open record flag from \"%s\"", path.c_str());
1574 pkey->notify_count = 0;
1575 uorp->num_modified++;
1576
1577 if (pkey->access_mode | MODE_EXCLUSIVE) {
1578 status = db_set_mode_wlocked(uorp->pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 1, msg);
1579 if (status != DB_SUCCESS) {
1580 db_msg(msg, MERROR, "db_update_open_record", "Cannot remove exclusive access mode from \"%s\", db_set_mode() status %d", path.c_str(), status);
1581 return DB_SUCCESS;
1582 }
1583 db_msg(msg, MINFO, "db_update_open_record", "Removed exclusive access mode from \"%s\"", path.c_str());
1584 }
1585 return DB_SUCCESS;
1586 }
1587
1588 if (pkey->notify_count != uorp->counts[k]) {
1589 db_msg(msg, MINFO, "db_update_open_record", "Updated notify_count of \"%s\" from %d to %d", path.c_str(), pkey->notify_count, count);
1590 pkey->notify_count = count;
1591 uorp->num_modified++;
1592 return DB_SUCCESS;
1593 }
1594
1595 return DB_SUCCESS;
1596}
1597
1599{
1600 int status = DB_SUCCESS;
1602 int i, j, k;
1603
1605 uor.num_keys = 0;
1606 uor.hkeys = (HNDLE*)calloc(uor.max_keys, sizeof(HNDLE));
1607 uor.counts = (int*)calloc(uor.max_keys, sizeof(int));
1608 uor.modes = (int*)calloc(uor.max_keys, sizeof(int));
1609 uor.num_modified = 0;
1610
1611 assert(uor.hkeys != NULL);
1612 assert(uor.counts != NULL);
1613 assert(uor.modes != NULL);
1614
1615 uor.pheader = pheader;
1616
1617 for (i = 0; i < pheader->max_client_index; i++) {
1618 DATABASE_CLIENT* pclient = &pheader->client[i];
1619 for (j = 0; j < pclient->max_index; j++)
1620 if (pclient->open_record[j].handle) {
1621 int found = 0;
1622 for (k=0; k<uor.num_keys; k++) {
1623 if (uor.hkeys[k] == pclient->open_record[j].handle) {
1624 uor.counts[k]++;
1625 found = 1;
1626 break;
1627 }
1628 }
1629 if (!found) {
1630 uor.hkeys[uor.num_keys] = pclient->open_record[j].handle;
1631 uor.counts[uor.num_keys] = 1;
1632 uor.modes[uor.num_keys] = pclient->open_record[j].access_mode;
1633 uor.num_keys++;
1634 }
1635 }
1636 }
1637
1638#if 0
1639 for (i=0; i<uor.num_keys; i++)
1640 printf("index %d, handle %d, count %d, access mode %d\n", i, uor.hkeys[i], uor.counts[i], uor.modes[i]);
1641#endif
1642
1643 const KEY* proot = db_get_pkey(pheader, 0, &status, "db_validate_open_record", msg);
1644
1645 if (proot) {
1647 }
1648
1649 if (uor.num_modified) {
1650 db_msg(msg, MINFO, "db_validate_open_records", "Corrected %d ODB entries", uor.num_modified);
1651 }
1652
1653 free(uor.hkeys);
1654 free(uor.counts);
1655 free(uor.modes);
1656
1657 return status;
1658}
1659
1660/*------------------------------------------------------------------*/
1662{
1663 int total_size_key = 0;
1664 int total_size_data = 0;
1665 double ratio;
1667 bool flag = true;
1668
1669 /* validate size of data structures (miscompiled, 32/64 bit mismatch, etc */
1670
1672
1673 /* validate the key free list */
1674
1675 if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
1676 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));
1677 return false;
1678 }
1679
1680 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
1681
1682 while ((POINTER_T) pfree != (POINTER_T) pheader) {
1683
1684 if (pfree->next_free != 0 && !db_validate_key_offset(pheader, pfree->next_free)) {
1685 db_msg(msg, MERROR, "db_validate_db", "Warning: database corruption, invalid key area next_free 0x%08X", pfree->next_free - (int)sizeof(DATABASE_HEADER));
1686 flag = false;
1687 break;
1688 }
1689
1690 total_size_key += pfree->size;
1691 FREE_DESCRIP *nextpfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
1692
1693 if (pfree->next_free != 0 && nextpfree == pfree) {
1694 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));
1695 pfree->next_free = 0;
1696 flag = false;
1697 break;
1698 //return false;
1699 }
1700
1701 pfree = nextpfree;
1702 }
1703
1704 ratio = ((double) (pheader->key_size - total_size_key)) / ((double) pheader->key_size);
1705 if (ratio > 0.9)
1706 db_msg(msg, MERROR, "db_validate_db", "Warning: database key area is %.0f%% full", ratio * 100.0);
1707
1708 if (total_size_key > pheader->key_size) {
1709 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);
1710 flag = false;
1711 }
1712
1713 /* validate the data free list */
1714
1715 if (!db_validate_data_offset(pheader, pheader->first_free_data)) {
1716 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));
1717 return false;
1718 }
1719
1720 //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);
1721
1722 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
1723
1724 while ((POINTER_T) pfree != (POINTER_T) pheader) {
1725
1726 if (pfree->next_free != 0 && !db_validate_data_offset(pheader, pfree->next_free)) {
1727 db_msg(msg, MERROR, "db_validate_db", "Warning: database corruption, invalid data area next_free 0x%08X", pfree->next_free - (int)sizeof(DATABASE_HEADER));
1728 flag = false;
1729 break;
1730 //return false;
1731 }
1732
1733 total_size_data += pfree->size;
1734 FREE_DESCRIP *nextpfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
1735
1736 if (pfree->next_free != 0 && nextpfree == pfree) {
1737 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));
1738 pfree->next_free = 0;
1739 flag = false;
1740 break;
1741 //return false;
1742 }
1743
1744 pfree = nextpfree;
1745 }
1746
1747 ratio = ((double) (pheader->data_size - total_size_data)) / ((double) pheader->data_size);
1748 if (ratio > 0.9)
1749 db_msg(msg, MERROR, "db_validate_db", "Warning: database data area is %.0f%% full", ratio * 100.0);
1750
1751 if (total_size_data > pheader->data_size) {
1752 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);
1753 flag = false;
1754 //return false;
1755 }
1756
1757 /* validate the tree of keys, starting from the root key */
1758
1759 if (!db_validate_key_offset(pheader, pheader->root_key)) {
1760 db_msg(msg, MERROR, "db_validate_db", "Error: database corruption, pheader->root_key 0x%08X is invalid", pheader->root_key - (int)sizeof(DATABASE_HEADER));
1761 return false;
1762 }
1763
1764 flag &= db_validate_and_repair_key_wlocked(pheader, 1, "", 0, pheader->root_key, (KEY *) ((char *) pheader + pheader->root_key), msg);
1765
1766 if (!flag) {
1767 db_msg(msg, MERROR, "db_validate_db", "Error: ODB corruption detected, see previous messages");
1768 }
1769
1770 return flag;
1771}
1772
1773#endif // LOCAL_ROUTINES
1774
1776#endif /* DOXYGEN_SHOULD_SKIP_THIS */
1777
1778/********************************************************************/
1789INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE * hDB, const char *client_name)
1790{
1791 if (rpc_is_remote())
1793
1794#ifdef LOCAL_ROUTINES
1795 {
1796 INT i, status;
1797 HNDLE handle;
1800 DATABASE_HEADER *pheader;
1801 KEY *pkey;
1805 DWORD timeout;
1807
1808 /* restrict name length */
1810
1811 int odb_size_limit = 100*1000*1000;
1813 cm_msg(MERROR, "db_open_database", "invalid database size: %d bytes. ODB size limit is %d bytes", database_size, odb_size_limit);
1814 return DB_INVALID_PARAM;
1815 }
1816
1817 if (strlen(client_name) >= NAME_LENGTH) {
1818 cm_msg(MERROR, "db_open_database", "client name \'%s\' is longer than %d characters", client_name, NAME_LENGTH-1);
1819 return DB_INVALID_PARAM;
1820 }
1821
1822 if (strchr(client_name, '/') != NULL) {
1823 cm_msg(MERROR, "db_open_database", "client name \'%s\' should not contain the slash \'/\' character", client_name);
1824 return DB_INVALID_PARAM;
1825 }
1826
1827 /* allocate new space for the new database descriptor */
1828 if (_database_entries == 0) {
1829 _database = (DATABASE *) malloc(sizeof(DATABASE));
1830 assert(_database != NULL);
1831 memset(_database, 0, sizeof(DATABASE));
1832 if (_database == NULL) {
1833 *hDB = 0;
1834 return DB_NO_MEMORY;
1835 }
1836
1838 i = 0;
1839 } else {
1840 /* check if database already open */
1841 for (i = 0; i < _database_entries; i++)
1842 if (_database[i].attached && equal_ustring(_database[i].name, database_name)) {
1843 /* check if database belongs to this thread */
1844 *hDB = i + 1;
1845 return DB_SUCCESS;
1846 }
1847
1848 /* check for a deleted entry */
1849 for (i = 0; i < _database_entries; i++)
1850 if (!_database[i].attached)
1851 break;
1852
1853 /* if not found, create new one */
1854 if (i == _database_entries) {
1856 assert(_database != NULL);
1857
1859
1861 if (_database == NULL) {
1863 *hDB = 0;
1864 return DB_NO_MEMORY;
1865 }
1866 }
1867 }
1868
1869 handle = (HNDLE) i;
1870
1871 /* open shared memory region */
1872 void* shm_adr = NULL;
1873 size_t shm_size = 0;
1874 HNDLE shm_handle;
1875
1876 status = ss_shm_open(database_name, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2), &shm_adr, &shm_size, &shm_handle, TRUE);
1877
1878 if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
1879 *hDB = 0;
1880 return DB_INVALID_NAME;
1881 }
1882
1883 _database[handle].shm_adr = shm_adr;
1884 _database[handle].shm_size = shm_size;
1885 _database[handle].shm_handle = shm_handle;
1886
1887 _database[handle].database_header = (DATABASE_HEADER *) shm_adr;
1888
1889 /* shortcut to header */
1890 pheader = _database[handle].database_header;
1891
1892 /* save name */
1893 strcpy(_database[handle].name, database_name);
1894
1896
1897 /* clear memeory for debugging */
1898 /* memset(pheader, 0, sizeof(DATABASE_HEADER) + 2*ALIGN8(database_size/2)); */
1899
1900 if (shm_created && pheader->name[0] == 0) {
1901 /* setup header info if database was created */
1902 memset(pheader, 0, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2));
1903
1904 strcpy(pheader->name, database_name);
1905 pheader->version = DATABASE_VERSION;
1906 pheader->key_size = ALIGN8(database_size / 2);
1907 pheader->data_size = ALIGN8(database_size / 2);
1908 pheader->root_key = sizeof(DATABASE_HEADER);
1909 pheader->first_free_key = sizeof(DATABASE_HEADER);
1910 pheader->first_free_data = sizeof(DATABASE_HEADER) + pheader->key_size;
1911
1912 /* set up free list */
1913 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
1914 pfree->size = pheader->key_size;
1915 pfree->next_free = 0;
1916
1917 pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
1918 pfree->size = pheader->data_size;
1919 pfree->next_free = 0;
1920
1921 /* create root key */
1922 pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_open_database_A");
1923 assert(pkey);
1924
1925 /* set key properties */
1926 pkey->type = TID_KEY;
1927 pkey->num_values = 1;
1928 pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
1929 strcpy(pkey->name, "root");
1930 pkey->parent_keylist = 0;
1931
1932 /* create keylist */
1933 pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST), "db_open_database_B");
1934 assert(pkeylist);
1935
1936 /* store keylist in data field */
1937 pkey->data = (POINTER_T) pkeylist - (POINTER_T) pheader;
1938 pkey->item_size = sizeof(KEYLIST);
1939 pkey->total_size = sizeof(KEYLIST);
1940
1941 pkeylist->parent = (POINTER_T) pkey - (POINTER_T) pheader;
1942 pkeylist->num_keys = 0;
1943 pkeylist->first_key = 0;
1944 }
1945
1946 /* check database version */
1947 if (pheader->version != DATABASE_VERSION) {
1948 cm_msg(MERROR, "db_open_database",
1949 "Different database format: Shared memory is %d, program is %d", pheader->version, DATABASE_VERSION);
1950 return DB_VERSION_MISMATCH;
1951 }
1952
1953 /* check database size vs shared memory size */
1954 if (_database[handle].shm_size < (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size) {
1955 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);
1956 return DB_VERSION_MISMATCH;
1957 }
1958
1959 /* check root key */
1960 if (!db_validate_key_offset(pheader, pheader->root_key)) {
1961 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key offset %d is invalid", pheader->root_key);
1962 return DB_VERSION_MISMATCH;
1963 } else {
1964 pkey = (KEY*)((char*)pheader + pheader->root_key);
1965
1966 if (pkey->type != TID_KEY) {
1967 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key type %d is not TID_KEY", pkey->type);
1968 return DB_VERSION_MISMATCH;
1969 }
1970
1971 if (strcmp(pkey->name, "root") != 0) {
1972 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key name \"%s\" is not \"root\"", pkey->name);
1973 return DB_VERSION_MISMATCH;
1974 }
1975
1976 // what if we are connecting to an incompatible ODB?
1977 // A call to db_validate_and_repair_key() maybe will
1978 // corrupt it here. But we have no choice,
1979 // if we skip it here and continue,
1980 // db_validate_and_repair_db() will call it later anyway... K.O.
1981
1982 db_err_msg* msg = NULL;
1983 bool ok = db_validate_and_repair_key_wlocked(pheader, 0, "", 0, pheader->root_key, pkey, &msg);
1984 if (msg)
1985 db_flush_msg(&msg);
1986 if (!ok) {
1987 cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key is invalid");
1988 return DB_VERSION_MISMATCH;
1989 }
1990 }
1991
1992 /* set default mutex and semaphore timeout */
1993 _database[handle].timeout = 10000;
1994
1995 /* create mutexes for the database */
1996 status = ss_mutex_create(&_database[handle].mutex, TRUE);
1997 if (status != SS_SUCCESS && status != SS_CREATED) {
1998 *hDB = 0;
1999 return DB_NO_SEMAPHORE;
2000 }
2001
2002 /* create semaphore for the database */
2003 status = ss_semaphore_create(database_name, &(_database[handle].semaphore));
2004 if (status != SS_SUCCESS && status != SS_CREATED) {
2005 *hDB = 0;
2006 return DB_NO_SEMAPHORE;
2007 }
2008 _database[handle].lock_cnt = 0;
2009
2010 _database[handle].protect = FALSE;
2011 _database[handle].protect_read = FALSE;
2012 _database[handle].protect_write = FALSE;
2013
2014 /* first lock database */
2015 status = db_lock_database(handle + 1);
2016 if (status != DB_SUCCESS)
2017 return status;
2018
2019 /* we have the database locked, without write protection */
2020
2021 /*
2022 Now we have a DATABASE_HEADER, so let's setup a CLIENT
2023 structure in that database. The information there can also
2024 be seen by other processes.
2025 */
2026
2027 /*
2028 update the client count
2029 */
2030 int num_clients = 0;
2031 int max_client_index = 0;
2032 for (i = 0; i < MAX_CLIENTS; i++) {
2033 if (pheader->client[i].pid == 0)
2034 continue;
2035 num_clients++;
2036 max_client_index = i + 1;
2037 }
2038 pheader->num_clients = num_clients;
2039 pheader->max_client_index = max_client_index;
2040
2041 /*fprintf(stderr,"num_clients: %d, max_client: %d\n",pheader->num_clients,pheader->max_client_index); */
2042
2043 /* remove dead clients */
2044 for (i = 0; i < MAX_CLIENTS; i++) {
2045 if (pheader->client[i].pid == 0)
2046 continue;
2047 if (!ss_pid_exists(pheader->client[i].pid)) {
2049 int client_pid;
2050
2051 mstrlcpy(client_name_tmp, pheader->client[i].name, sizeof(client_name_tmp));
2052 client_pid = pheader->client[i].pid;
2053
2054 // removed: /* decrement notify_count for open records and clear exclusive mode */
2055 // open records are corrected later, by db_validate_open_records()
2056
2057 /* clear entry from client structure in database header */
2058 memset(&(pheader->client[i]), 0, sizeof(DATABASE_CLIENT));
2059
2060 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);
2061 }
2062 }
2063
2064 /*
2065 Look for empty client slot
2066 */
2067 for (i = 0; i < MAX_CLIENTS; i++)
2068 if (pheader->client[i].pid == 0)
2069 break;
2070
2071 if (i == MAX_CLIENTS) {
2072 db_unlock_database(handle + 1);
2073 *hDB = 0;
2074 cm_msg(MERROR, "db_open_database", "maximum number of clients exceeded");
2075 return DB_NO_SLOT;
2076 }
2077
2078 /* store slot index in _database structure */
2079 _database[handle].client_index = i;
2080
2081 /*
2082 Save the index of the last client of that database so that later only
2083 the clients 0..max_client_index-1 have to be searched through.
2084 */
2085 pheader->num_clients++;
2086 if (i + 1 > pheader->max_client_index)
2087 pheader->max_client_index = i + 1;
2088
2089 /* setup database header and client structure */
2090 pclient = &pheader->client[i];
2091
2092 memset(pclient, 0, sizeof(DATABASE_CLIENT));
2093 mstrlcpy(pclient->name, client_name, sizeof(pclient->name));
2094 pclient->pid = ss_getpid();
2095 pclient->num_open_records = 0;
2096
2098
2099 pclient->last_activity = ss_millitime();
2100
2102 pclient->watchdog_timeout = timeout;
2103
2104 /* check ODB for corruption */
2105 db_err_msg* msg = NULL;
2106 bool ok = db_validate_and_repair_db_wlocked(pheader, &msg);
2107 if (msg)
2108 db_flush_msg(&msg);
2109 if (!ok) {
2110 /* do not treat corrupted odb as a fatal error- allow the user
2111 to preceed at own risk- the database is already corrupted,
2112 so no further harm can possibly be made. */
2113 /*
2114 db_unlock_database(handle + 1);
2115 *hDB = 0;
2116 return DB_CORRUPTED;
2117 */
2118 }
2119
2120 /* setup _database entry */
2121 _database[handle].database_data = _database[handle].database_header + 1;
2122 _database[handle].attached = TRUE;
2123 _database[handle].protect = FALSE;
2124 _database[handle].protect_read = FALSE;
2125 _database[handle].protect_write = FALSE;
2126
2127 *hDB = (handle + 1);
2128
2130 if (status != DB_SUCCESS) {
2131 db_unlock_database(handle + 1);
2132 if (msg)
2133 db_flush_msg(&msg);
2134 cm_msg(MERROR, "db_open_database", "Error: db_validate_open_records() status %d", status);
2135 return status;
2136 }
2137
2138 db_unlock_database(handle + 1);
2139
2140 if (msg)
2141 db_flush_msg(&msg);
2142
2143 if (shm_created)
2144 return DB_CREATED;
2145 }
2146#endif /* LOCAL_ROUTINES */
2147
2148 return DB_SUCCESS;
2149}
2150
2151/********************************************************************/
2158{
2159 if (rpc_is_remote())
2161
2162#ifdef LOCAL_ROUTINES
2163 else {
2164 INT destroy_flag, i, j;
2165 char xname[256];
2166
2167 if (hDB > _database_entries || hDB <= 0) {
2168 cm_msg(MERROR, "db_close_database", "invalid database handle %d", hDB);
2169 return DB_INVALID_HANDLE;
2170 }
2171
2172 /* flush database to disk */
2174
2175 /* first lock database */
2177
2178 DATABASE* pdb = &_database[hDB - 1];
2179
2180 if (!pdb->attached) {
2182 cm_msg(MERROR, "db_close_database", "database not attached");
2183 return DB_INVALID_HANDLE;
2184 }
2185
2186 DATABASE_HEADER *pheader = pdb->database_header;
2188
2189 db_allow_write_locked(&_database[hDB-1], "db_close_database");
2190
2191 /* close all open records */
2192 for (i = 0; i < pclient->max_index; i++)
2193 if (pclient->open_record[i].handle)
2194 db_remove_open_record_wlocked(pdb, pheader, pclient->open_record[i].handle);
2195
2196 /* mark entry in _database as empty */
2197 pdb->attached = FALSE;
2198
2199 /* clear entry from client structure in database header */
2200 memset(pclient, 0, sizeof(DATABASE_CLIENT));
2201
2202 /* calculate new max_client_index entry */
2203 for (i = MAX_CLIENTS - 1; i >= 0; i--)
2204 if (pheader->client[i].pid != 0)
2205 break;
2206 pheader->max_client_index = i + 1;
2207
2208 /* count new number of clients */
2209 for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
2210 if (pheader->client[i].pid != 0)
2211 j++;
2212 pheader->num_clients = j;
2213
2214 destroy_flag = (pheader->num_clients == 0);
2215
2216 /* flush shared memory to disk before it gets deleted */
2217 if (destroy_flag)
2218 ss_shm_flush(pheader->name, pdb->shm_adr, pdb->shm_size, pdb->shm_handle, true);
2219
2220 mstrlcpy(xname, pheader->name, sizeof(xname));
2221
2222 /* unmap shared memory, delete it if we are the last */
2223 ss_shm_close(xname, pdb->shm_adr, pdb->shm_size, pdb->shm_handle, destroy_flag);
2224
2225 pheader = NULL; // after ss_shm_close(), pheader points nowhere
2226 pdb->database_header = NULL; // ditto
2227
2228 /* unlock database */
2230
2231 /* delete semaphore */
2233
2234 /* delete mutex */
2235 ss_mutex_delete(pdb->mutex);
2236
2237 /* update _database_entries */
2238 if (hDB == _database_entries)
2240
2241 if (_database_entries > 0)
2243 else {
2244 free(_database);
2245 _database = NULL;
2246 }
2247
2248 /* if we are the last one, also delete other semaphores */
2249 if (destroy_flag) {
2250 //extern INT _semaphore_elog, _semaphore_alarm, _semaphore_history, _semaphore_msg;
2252
2253 if (_semaphore_elog)
2255 if (_semaphore_alarm)
2259 //if (_semaphore_msg)
2260 // ss_semaphore_delete(_semaphore_msg, TRUE);
2261 }
2262
2263 }
2264#endif /* LOCAL_ROUTINES */
2265
2266 return DB_SUCCESS;
2267}
2268
2270#ifndef DOXYGEN_SHOULD_SKIP_THIS
2271
2272/*------------------------------------------------------------------*/
2274/********************************************************************\
2275
2276 Routine: db_flush_database
2277
2278 Purpose: Flushes the shared memory of a database to its disk file.
2279
2280 Input:
2281 HNDLE hDB Handle to the database, which is used as
2282 an index to the _database array.
2283
2284 Output:
2285 none
2286
2287 Function value:
2288 DB_SUCCESS Successful completion
2289 DB_INVALID_HANDLE Database handle is invalid
2290 RPC_NET_ERROR Network error
2291
2292\********************************************************************/
2293{
2294 if (rpc_is_remote())
2296
2297#ifdef LOCAL_ROUTINES
2298 else {
2299 int status, size;
2301 uint32_t last_flush = 0;
2302
2303 if (hDB > _database_entries || hDB <= 0) {
2304 cm_msg(MERROR, "db_flush_database", "invalid database handle");
2305 return DB_INVALID_HANDLE;
2306 }
2307
2308 /* create keys if not present */
2309 size = sizeof(flush_period);
2310 db_get_value(hDB, 0, "/System/Flush/Flush period", &flush_period, &size, TID_UINT32, true);
2311 size = sizeof(last_flush);
2312 db_get_value(hDB, 0, "/System/Flush/Last flush", &last_flush, &size, TID_UINT32, true);
2313
2314 HNDLE hkey;
2315 status = db_find_key(hDB, 0, "/System/Flush/Last flush", &hkey);
2316 if (status != DB_SUCCESS) {
2317 cm_msg(MERROR, "db_flush_database", "Cannot obtain key /System/Flush/Last flush");
2318 return DB_INVALID_HANDLE;
2319 }
2320
2322 DATABASE *pdb = &_database[hDB - 1];
2323 DATABASE_HEADER *pheader = pdb->database_header;
2324
2325 if (!_database[hDB - 1].attached) {
2327 cm_msg(MERROR, "db_flush_database", "invalid database handle");
2328 return DB_INVALID_HANDLE;
2329 }
2330
2331 db_err_msg *msg = nullptr;
2332
2333 const KEY *pkey = db_get_pkey(pheader, hkey, &status, "db_flush_database", &msg);
2334
2335 if (!pkey) {
2337 if (msg)
2338 db_flush_msg(&msg);
2339 return CM_NO_CLIENT;
2340 }
2341
2342 size = sizeof(last_flush);
2343 status = db_get_data_locked(pheader, pkey, 0, &last_flush, &size, TID_UINT32, &msg);
2344
2345 /* only flush if period has expired */
2346 if (ss_time() > last_flush + flush_period) {
2347 db_allow_write_locked(pdb, "db_flush_database");
2348
2349 /* update last flush time in ODB */
2350 last_flush = ss_time();
2351 db_set_value_wlocked(pheader, hDB, 0, "/System/Flush/Last flush", &last_flush, sizeof(last_flush), 1, TID_UINT32, &msg);
2352
2353 /* flush shared memory to disk */
2354 ss_shm_flush(pheader->name, _database[hDB - 1].shm_adr, _database[hDB - 1].shm_size, _database[hDB - 1].shm_handle, false);
2355 }
2356
2358 }
2359#endif /* LOCAL_ROUTINES */
2360
2361 return DB_SUCCESS;
2362}
2363
2364/*------------------------------------------------------------------*/
2366/********************************************************************\
2367
2368 Routine: db_close_all_databases
2369
2370 Purpose: Close all open databases and open records
2371
2372 Input:
2373 none
2374
2375 Output:
2376 none
2377
2378 Function value:
2379 DB_SUCCESS Successful completion
2380
2381\********************************************************************/
2382{
2383 INT status;
2384
2385 if (rpc_is_remote()) {
2387 if (status != DB_SUCCESS)
2388 return status;
2389 }
2390
2393
2394#ifdef LOCAL_ROUTINES
2395 {
2396 INT i;
2397
2398 for (i = _database_entries; i > 0; i--)
2400 }
2401#endif /* LOCAL_ROUTINES */
2402
2403 return DB_SUCCESS;
2404}
2405
2406/*------------------------------------------------------------------*/
2407INT db_set_client_name(HNDLE hDB, const char *client_name)
2408/********************************************************************\
2409
2410 Routine: db_set_client_name
2411
2412 Purpose: Set client name for a database. Used by cm_connect_experiment
2413 if a client name is duplicate and changed.
2414
2415 Input:
2416 INT hDB Handle to database
2417 char *client_name Name of this application
2418
2419 Output:
2420
2421 Function value:
2422 DB_SUCCESS Successful completion
2423 RPC_NET_ERROR Network error
2424
2425\********************************************************************/
2426{
2427 if (rpc_is_remote())
2428 return rpc_call(RPC_DB_SET_CLIENT_NAME, hDB, client_name);
2429
2430#ifdef LOCAL_ROUTINES
2431 {
2432 if (hDB > _database_entries || hDB <= 0) {
2433 cm_msg(MERROR, "db_set_client_name", "invalid database handle %d", hDB);
2434 return DB_INVALID_HANDLE;
2435 }
2436
2438
2439 DATABASE *pdb = &_database[hDB - 1];
2441 mstrlcpy(pclient->name, client_name, sizeof(pclient->name));
2442
2444 }
2445#endif /* LOCAL_ROUTINES */
2446
2447 return DB_SUCCESS;
2448}
2449
2451#endif /* DOXYGEN_SHOULD_SKIP_THIS */
2452
2453/********************************************************************/
2461{
2462#ifdef LOCAL_ROUTINES
2463 int status;
2464
2465 if (hDB > _database_entries || hDB <= 0) {
2466 cm_msg(MERROR, "db_lock_database", "invalid database handle %d, aborting...", hDB);
2467 abort();
2468 return DB_INVALID_HANDLE;
2469 }
2470
2471 /* obtain access mutex in multi-thread applications */
2472 status = ss_mutex_wait_for(_database[hDB - 1].mutex, _database[hDB - 1].timeout);
2473 if (status != SS_SUCCESS) {
2474 cm_msg(MERROR, "db_lock_database", "cannot lock ODB mutex, timeout %d ms, status %d, aborting...", _database[hDB - 1].timeout, status);
2475 abort();
2476 }
2477
2478 /* protect this function against recursive call from signal handlers */
2479 if (_database[hDB - 1].inside_lock_unlock) {
2480 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");
2481 abort();
2482 }
2483
2485
2486 //static int x = 0;
2487 //x++;
2488 //if (x > 5000) {
2489 // printf("inside db_lock_database(), press Ctrl-C now!\n");
2490 // sleep(5);
2491 //}
2492
2493 // test recursive locking
2494 // static int out=0;
2495 // out++;
2496 // printf("HERE %d!\n", out);
2497 // if (out>10) abort();
2498 // db_lock_database(hDB);
2499 // printf("OUT %d!\n", out);
2500
2501 if (_database[hDB - 1].lock_cnt == 0) {
2502 _database[hDB - 1].lock_cnt = 1;
2503 /* wait max. 5 minutes for semaphore (required if locking process is being debugged) */
2504 status = ss_semaphore_wait_for(_database[hDB - 1].semaphore, _database[hDB - 1].timeout);
2505 if (status == SS_TIMEOUT) {
2506 cm_msg(MERROR, "db_lock_database", "cannot lock ODB semaphore, timeout %d ms, aborting...", _database[hDB - 1].timeout);
2507 //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.
2508 abort();
2509 }
2510 if (status != SS_SUCCESS) {
2511 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);
2512 abort();
2513 }
2514 } else {
2515 _database[hDB - 1].lock_cnt++; // we have already the lock (recursive call), so just increase counter
2516 }
2517
2518#ifdef CHECK_LOCK_COUNT
2519 {
2520 char str[256];
2521
2522 sprintf(str, "db_lock_database, lock_cnt=%d", _database[hDB - 1].lock_cnt);
2524 }
2525#endif
2526
2527 if (_database[hDB - 1].protect) {
2528 if (_database[hDB - 1].database_header == NULL) {
2529 int status;
2530 assert(!_database[hDB - 1].protect_read);
2531 assert(!_database[hDB - 1].protect_write);
2532 status = ss_shm_unprotect(_database[hDB - 1].shm_handle, &_database[hDB - 1].shm_adr, _database[hDB - 1].shm_size, TRUE, FALSE, "db_lock_database");
2533 if (status != SS_SUCCESS) {
2534 cm_msg(MERROR, "db_lock_database", "cannot lock ODB, ss_shm_unprotect(TRUE,FALSE) failed with status %d, aborting...", status);
2536 ss_semaphore_release(_database[hDB - 1].semaphore);
2537 abort();
2538 }
2539 _database[hDB - 1].database_header = (DATABASE_HEADER *) _database[hDB - 1].shm_adr;
2542 }
2543 }
2544
2546
2547#endif /* LOCAL_ROUTINES */
2548
2549 return DB_SUCCESS;
2550}
2551
2552#ifdef LOCAL_ROUTINES
2554{
2555 assert(p);
2556 if (p->protect && !p->protect_write) {
2557 int status;
2558 assert(p->lock_cnt > 0);
2559 assert(p->database_header != NULL);
2560 assert(p->protect_read);
2562 if (status != SS_SUCCESS) {
2563 cm_msg(MERROR, "db_allow_write_locked", "cannot write to ODB, ss_shm_unprotect(TRUE,TRUE) failed with status %d, aborting...", status);
2566 abort();
2567 }
2569 p->protect_read = TRUE;
2570 p->protect_write = TRUE;
2571 }
2572 return DB_SUCCESS;
2573}
2574#endif /* LOCAL_ROUTINES */
2575
2576/********************************************************************/
2583{
2584#ifdef LOCAL_ROUTINES
2585
2586 if (hDB > _database_entries || hDB <= 0) {
2587 cm_msg(MERROR, "db_unlock_database", "invalid database handle %d", hDB);
2588 return DB_INVALID_HANDLE;
2589 }
2590#ifdef CHECK_LOCK_COUNT
2591 {
2592 char str[256];
2593
2594 sprintf(str, "db_unlock_database, lock_cnt=%d", _database[hDB - 1].lock_cnt);
2596 }
2597#endif
2598
2599 /* protect this function against recursive call from signal handlers */
2600 if (_database[hDB - 1].inside_lock_unlock) {
2601 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");
2602 abort();
2603 }
2604
2606
2607 //static int x = 0;
2608 //x++;
2609 //if (x > 5000) {
2610 // printf("inside db_unlock_database(), press Ctrl-C now!\n");
2611 // sleep(5);
2612 //}
2613
2614 if (_database[hDB - 1].lock_cnt == 1) {
2615 ss_semaphore_release(_database[hDB - 1].semaphore);
2616
2617 if (_database[hDB - 1].protect && _database[hDB - 1].database_header) {
2618 int status;
2619 assert(_database[hDB - 1].protect_read);
2620 assert(_database[hDB - 1].database_header);
2622 status = ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].shm_adr, _database[hDB - 1].shm_size);
2623 if (status != SS_SUCCESS) {
2624 cm_msg(MERROR, "db_unlock_database", "cannot unlock ODB, ss_shm_protect() failed with status %d, aborting...", status);
2626 abort();
2627 }
2630 }
2631 }
2632
2633 assert(_database[hDB - 1].lock_cnt > 0);
2634 _database[hDB - 1].lock_cnt--;
2635
2637
2638 /* release mutex for multi-thread applications */
2639 ss_mutex_release(_database[hDB - 1].mutex);
2640
2641#endif /* LOCAL_ROUTINES */
2642 return DB_SUCCESS;
2643}
2644
2645/********************************************************************/
2646#if 0
2648{
2649#ifdef LOCAL_ROUTINES
2650
2651 /* return zero if no ODB is open or we run remotely */
2652 if (_database_entries == 0)
2653 return 0;
2654
2655 if (hDB > _database_entries || hDB <= 0) {
2656 cm_msg(MERROR, "db_get_lock_cnt", "invalid database handle %d, aborting...", hDB);
2657 fprintf(stderr, "db_get_lock_cnt: invalid database handle %d, aborting...\n", hDB);
2658 abort();
2659 return DB_INVALID_HANDLE;
2660 }
2661
2662 return _database[hDB - 1].lock_cnt;
2663#else
2664 return 0;
2665#endif
2666}
2667#endif
2668
2670{
2671#ifdef LOCAL_ROUTINES
2672
2673 /* return zero if no ODB is open or we run remotely */
2674 if (_database_entries == 0)
2675 return 0;
2676
2677 if (hDB > _database_entries || hDB <= 0) {
2678 cm_msg(MERROR, "db_set_lock_timeout", "invalid database handle %d", hDB);
2679 return 0;
2680 }
2681
2682 if (timeout_millisec > 0) {
2684 }
2685
2686 return _database[hDB - 1].timeout;
2687#else
2688 return 0;
2689#endif
2690}
2691
2692#ifdef LOCAL_ROUTINES
2693
2698{
2699 bool found = false;
2700 int pid = ss_getpid();
2701 for (int i = 0; i < _database_entries; i++) {
2702 if (_database[i].attached) {
2703 db_lock_database(i + 1);
2704 db_allow_write_locked(&_database[i], "db_update_last_activity");
2705 assert(_database[i].database_header);
2706 /* update the last_activity entry to show that we are alive */
2707 for (int j=0; j<_database[i].database_header->max_client_index; j++) {
2709 //printf("client %d pid %d vs our pid %d\n", j, pdbclient->pid, pid);
2710 if (pdbclient->pid == pid) {
2712 found = true;
2713 }
2714 }
2715 db_unlock_database(i + 1);
2716 }
2717 }
2718
2719 if (!found) {
2720 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...");
2721 abort();
2722 }
2723
2724 return DB_SUCCESS;
2725}
2726
2727#endif // LOCAL_ROUTINES
2728
2729#ifdef LOCAL_ROUTINES
2731{
2733
2734 /* decrement notify_count for open records and clear exclusive mode */
2735 int k;
2736 for (k = 0; k < pdbclient->max_index; k++)
2737 if (pdbclient->open_record[k].handle) {
2738 KEY* pkey = (KEY *) ((char *) pheader + pdbclient->open_record[k].handle);
2739 if (pkey->notify_count > 0)
2740 pkey->notify_count--;
2741
2742 if (pdbclient->open_record[k].access_mode & MODE_WRITE)
2743 db_set_mode_wlocked(pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2, msg);
2744 }
2745
2746 /* clear entry from client structure in buffer header */
2747 memset(pdbclient, 0, sizeof(DATABASE_CLIENT));
2748
2749 /* calculate new max_client_index entry */
2750 for (k = MAX_CLIENTS - 1; k >= 0; k--)
2751 if (pheader->client[k].pid != 0)
2752 break;
2753 pheader->max_client_index = k + 1;
2754
2755 /* count new number of clients */
2756 int nc;
2757 for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
2758 if (pheader->client[k].pid != 0)
2759 nc++;
2760 pheader->num_clients = nc;
2761}
2762#endif
2763
2764#ifdef LOCAL_ROUTINES
2766{
2767 if (!pid)
2768 pid = ss_getpid();
2769
2770 char str[256];
2771 int status = 0;
2772
2773 sprintf(str, "System/Clients/%0d", pid);
2774 KEY* pkey = (KEY*)db_find_pkey_locked(pheader, NULL, str, &status, msg);
2775 if (!pkey) {
2776 return status;
2777 }
2778
2780 HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
2782 int32_t data = 0;
2783 db_set_value_wlocked(pheader, hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32, msg);
2784
2785 return status;
2786}
2787#endif
2788
2789/********************************************************************/
2797{
2798#ifdef LOCAL_ROUTINES
2799 if (hDB > _database_entries || hDB <= 0) {
2800 cm_msg(MERROR, "db_delete_client_info", "invalid database handle");
2801 return DB_INVALID_HANDLE;
2802 }
2803
2804 if (!_database[hDB - 1].attached) {
2805 cm_msg(MERROR, "db_delete_client_info", "invalid database handle");
2806 return DB_INVALID_HANDLE;
2807 }
2808
2809 /* lock database */
2811
2812 DATABASE *pdb = &_database[hDB - 1];
2813 DATABASE_HEADER *pheader = pdb->database_header;
2814
2815 db_allow_write_locked(pdb, "db_delete_client_info");
2816
2817 db_err_msg* msg = NULL;
2818
2819 int status = db_delete_client_info_wlocked(hDB, pheader, pid, &msg);
2820
2822
2823 if (msg)
2824 db_flush_msg(&msg);
2825
2826 return status;
2827#else
2828 return DB_SUCCESS;
2829#endif
2830}
2831
2833{
2834#ifdef LOCAL_ROUTINES
2835 int i;
2836 /* check online databases */
2837 for (i = 0; i < _database_entries; i++) {
2838 DATABASE *pdb = &_database[i];
2839 if (!pdb->attached) // empty slot
2840 continue;
2841
2842 db_lock_database(i + 1);
2843 db_allow_write_locked(pdb, "db_cleanup");
2844
2845 DATABASE_HEADER* pheader = pdb->database_header;
2847
2848 /* update the last_activity entry to show that we are alive */
2849 pclient->last_activity = actual_time;
2850
2851 /* don't check other clients if interval is stange */
2852 if (wrong_interval) {
2853 db_unlock_database(i + 1);
2854 continue;
2855 }
2856
2857 db_err_msg *msg = NULL;
2858
2859 /* now check other clients */
2860 int j;
2861 for (j = 0; j < pheader->max_client_index; j++) {
2862 DATABASE_CLIENT *pdbclient = &pheader->client[j];
2863 int client_pid = pdbclient->pid;
2864 if (client_pid == 0) // empty slot
2865 continue;
2866
2867 if (!ss_pid_exists(client_pid)) {
2868 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);
2869
2870 db_delete_client_wlocked(pheader, j, &msg);
2871 db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2872
2873 continue;
2874 }
2875
2876 /* now make again the check with the buffer locked */
2878 if ((pdbclient->watchdog_timeout &&
2879 actual_time > pdbclient->last_activity &&
2880 actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout)
2881 ) {
2882 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)",
2883 pdbclient->name, pheader->name, client_pid,
2884 who,
2885 (actual_time - pdbclient->last_activity) / 1000.0,
2886 pdbclient->watchdog_timeout / 1000.0);
2887
2888 db_delete_client_wlocked(pheader, j, &msg);
2889 db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2890 }
2891 }
2892
2893 db_unlock_database(i + 1);
2894 if (msg)
2895 db_flush_msg(&msg);
2896 }
2897#endif
2898}
2899
2900#ifdef LOCAL_ROUTINES
2901
2902void db_cleanup2(const char* client_name, int ignore_timeout, DWORD actual_time, const char *who)
2903{
2904 /* check online databases */
2905 int i;
2906 for (i = 0; i < _database_entries; i++) {
2907 if (_database[i].attached) {
2908 /* update the last_activity entry to show that we are alive */
2909
2910 db_lock_database(i + 1);
2911
2912 db_err_msg *msg = NULL;
2913
2914 DATABASE* pdb = &_database[i];
2915
2916 db_allow_write_locked(pdb, "db_cleanup2");
2917
2919
2920 DATABASE_HEADER* pheader = pdb->database_header;
2922 pclient->last_activity = now;
2923
2924 /* now check other clients */
2925 int j;
2926 for (j = 0; j < pheader->max_client_index; j++) {
2927 DATABASE_CLIENT* pdbclient = &pheader->client[j];
2928 if (j == pdb->client_index) // do not check ourselves
2929 continue;
2930 if (!pdbclient->pid) // empty slot
2931 continue;
2932 if ((client_name == NULL || client_name[0] == 0
2933 || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
2934 int client_pid = pdbclient->pid;
2935
2936 if (!ss_pid_exists(client_pid)) {
2937 db_msg(&msg, MINFO, "db_cleanup2", "Client \'%s\' on database \'%s\' pid %d does not exist and db_cleanup2 called by %s removed it",
2938 pdbclient->name, pheader->name,
2939 client_pid,
2940 who);
2941
2942 db_delete_client_wlocked(pheader, j, &msg);
2943 db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2944
2945 /* go again though whole list */
2946 j = 0;
2947 continue;
2948 }
2949
2951 if (ignore_timeout)
2953 else
2954 interval = pdbclient->watchdog_timeout;
2955
2956 /* If client process has no activity, clear its buffer entry. */
2957
2958 if ((interval > 0 && now - pdbclient->last_activity > interval)) {
2959 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)",
2960 pdbclient->name, pheader->name,
2961 who,
2962 (now - pdbclient->last_activity) / 1000.0, interval / 1000.0);
2963
2964 db_delete_client_wlocked(pheader, j, &msg);
2965 db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2966
2967 /* go again though whole list */
2968 j = 0;
2969 continue;
2970 }
2971 }
2972 }
2973
2974 db_unlock_database(i + 1);
2975 if (msg)
2976 db_flush_msg(&msg);
2977 }
2978 }
2979}
2980
2982{
2983 /* set watchdog flag of all open databases */
2984 for (int i = _database_entries; i > 0; i--) {
2985
2987
2988 DATABASE* pdb = &_database[i - 1];
2989
2990 if (!pdb->attached) {
2992 continue;
2993 }
2994
2996
2997 db_allow_write_locked(pdb, "db_set_watchdog_params");
2998
2999 /* clear entry from client structure in buffer header */
3000 pclient->watchdog_timeout = timeout;
3001
3002 /* show activity */
3003 pclient->last_activity = ss_millitime();
3004
3006 }
3007}
3008
3009/********************************************************************/
3019INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD * timeout, DWORD * last)
3020{
3021 if (hDB > _database_entries || hDB <= 0) {
3022 cm_msg(MERROR, "db_get_watchdog_info", "invalid database handle");
3023 return DB_INVALID_HANDLE;
3024 }
3025
3026 if (!_database[hDB - 1].attached) {
3027 cm_msg(MERROR, "db_get_watchdog_info", "invalid database handle");
3028 return DB_INVALID_HANDLE;
3029 }
3030
3031 /* lock database */
3033
3034 DATABASE *pdb = &_database[hDB - 1];
3035 DATABASE_HEADER *pheader = pdb->database_header;
3036
3037 /* find client */
3038 for (int i = 0; i < pheader->max_client_index; i++) {
3039 DATABASE_CLIENT *pclient = &pheader->client[i];
3040 if (pclient->pid && equal_ustring(pclient->name, client_name)) {
3041 *timeout = pclient->watchdog_timeout;
3042 *last = ss_millitime() - pclient->last_activity;
3044 return DB_SUCCESS;
3045 }
3046 }
3047
3048 *timeout = *last = 0;
3049
3051
3052 return CM_NO_CLIENT;
3053}
3054
3055/********************************************************************/
3065{
3066 if (hDB > _database_entries || hDB <= 0) {
3067 cm_msg(MERROR, "db_check_client", "invalid database handle");
3068 return DB_INVALID_HANDLE;
3069 }
3070
3071 if (!_database[hDB - 1].attached) {
3072 cm_msg(MERROR, "db_check_client", "invalid database handle");
3073 return DB_INVALID_HANDLE;
3074 }
3075
3076 int status;
3077
3079
3080 db_err_msg* msg = NULL;
3081
3082 DATABASE *pdb = &_database[hDB - 1];
3083 DATABASE_HEADER *pheader = pdb->database_header;
3084
3085 const KEY* pkey = db_get_pkey(pheader, hKeyClient, &status, "db_check_client", &msg);
3086
3087 if (!pkey) {
3089 if (msg)
3090 db_flush_msg(&msg);
3091 return CM_NO_CLIENT;
3092 }
3093
3094 int client_pid = atoi(pkey->name);
3095
3096 const KEY* pkey_name = db_find_pkey_locked(pheader, pkey, "Name", &status, &msg);
3097
3098 if (!pkey_name) {
3100 if (msg)
3101 db_flush_msg(&msg);
3102 return CM_NO_CLIENT;
3103 }
3104
3105 char name[256];
3106 name[0] = 0;
3107 int size = sizeof(name);
3108 status = db_get_data_locked(pheader, pkey_name, 0, name, &size, TID_STRING, &msg);
3109
3110 //fprintf(stderr, "db_check_client: hkey %d, status %d, pid %d, name \'%s\'\n", hKeyClient, status, client_pid, name);
3111
3112 if (status != DB_SUCCESS) {
3114 if (msg)
3115 db_flush_msg(&msg);
3116 return CM_NO_CLIENT;
3117 }
3118
3119 bool dead = false;
3120 bool found = false;
3121
3122 /* loop through clients */
3123 for (int i = 0; i < pheader->max_client_index; i++) {
3124 DATABASE_CLIENT *pclient = &pheader->client[i];
3125 if (pclient->pid == client_pid) {
3126 found = true;
3127 break;
3128 }
3129 }
3130
3131 if (found) {
3132 /* check that the client is still running: PID still exists */
3133 if (!ss_pid_exists(client_pid)) {
3134 dead = true;
3135 }
3136 }
3137
3139
3140 if (!found || dead) {
3141 /* client not found : delete ODB stucture */
3142
3143 db_allow_write_locked(pdb, "db_check_client");
3144
3146
3147 if (status != DB_SUCCESS)
3148 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);
3149 else if (!found)
3150 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);
3151 else if (dead)
3152 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);
3153
3155 }
3156
3158 if (msg)
3159 db_flush_msg(&msg);
3160
3161 return status;
3162}
3163
3164#endif // LOCAL_ROUTINES
3165
3166/********************************************************************/
3173{
3174 if (rpc_is_remote())
3175 return DB_SUCCESS;
3176
3177#ifdef LOCAL_ROUTINES
3178 if (hDB > _database_entries || hDB <= 0) {
3179 cm_msg(MERROR, "db_protect_database", "invalid database handle %d", hDB);
3180 return DB_INVALID_HANDLE;
3181 }
3182
3183 _database[hDB - 1].protect = TRUE;
3184 ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].database_header, _database[hDB - 1].shm_size);
3186#endif /* LOCAL_ROUTINES */
3187 return DB_SUCCESS;
3188}
3189
3190/*---- helper routines ---------------------------------------------*/
3191
3192const char *extract_key(const char *key_list, char *key_name, int key_name_length)
3193{
3194 int i = 0;
3195
3196 if (*key_list == '/')
3197 key_list++;
3198
3199 while (*key_list && *key_list != '/' && ++i < key_name_length)
3200 *key_name++ = *key_list++;
3201 *key_name = 0;
3202
3203 return key_list;
3204}
3205
3206BOOL equal_ustring(const char *str1, const char *str2)
3207{
3208 if (str1 == NULL && str2 != NULL)
3209 return FALSE;
3210 if (str1 != NULL && str2 == NULL)
3211 return FALSE;
3212 if (str1 == NULL && str2 == NULL)
3213 return TRUE;
3214 if (strlen(str1) != strlen(str2))
3215 return FALSE;
3216
3217 while (*str1)
3218 if (toupper(*str1++) != toupper(*str2++))
3219 return FALSE;
3220
3221 if (*str2)
3222 return FALSE;
3223
3224 return TRUE;
3225}
3226
3227BOOL ends_with_ustring(const char *str, const char *suffix)
3228{
3229 int len_str = strlen(str);
3230 int len_suffix = strlen(suffix);
3231
3232 // suffix is longer than the string
3233 if (len_suffix > len_str)
3234 return FALSE;
3235
3237}
3238
3239//utility function to match string against wildcard pattern
3240BOOL strmatch(char* pattern, char* str){
3241 switch(*pattern){
3242 case 0:
3243 if(*str == 0){
3244 //end of pattern
3245 return true;
3246 }else
3247 return false;
3248 case '*':
3249 {
3250 int i=0;
3251 // check for end of the string
3252 if(pattern[1] == 0) return true;
3253 // loop on all possible matches
3254 while(str[i] != 0) if(strmatch(pattern + 1, str+(i++))) return true;
3255 return false;
3256 }
3257 case '?':
3258 if(*str == 0){
3259 //end of string
3260 return false;
3261 } else {
3262 return strmatch(pattern+1, str+1);
3263 }
3264 default:
3265 if(*str == 0){
3266 //end of string
3267 return false;
3268 } else if(toupper(*str) == toupper(*pattern)){
3269 //recursion
3270 return strmatch(pattern+1, str+1);
3271 } else {
3272 return false;
3273 }
3274 }
3275}
3276
3277//utility function to extract array indexes
3278void strarrayindex(char* odbpath, int* index1, int* index2){
3279 char* pc;
3280
3281 *index1 = *index2 = 0;
3282 if (odbpath[strlen(odbpath) - 1] == ']') {
3283 if (strchr(odbpath, '[')) {
3284 if (*(strchr(odbpath, '[') + 1) == '*')
3285 *index1 = -1;
3286 else if (strchr((strchr(odbpath, '[') + 1), '.') ||
3287 strchr((strchr(odbpath, '[') + 1), '-')) {
3288 *index1 = atoi(strchr(odbpath, '[') + 1);
3289 pc = strchr(odbpath, '[') + 1;
3290 while (*pc != '.' && *pc != '-')
3291 pc++;
3292 while (*pc == '.' || *pc == '-')
3293 pc++;
3294 *index2 = atoi(pc);
3295 } else
3296 *index1 = atoi(strchr(odbpath, '[') + 1);
3297 }
3298
3299 //remove everything after bracket
3300 *strchr(odbpath, '[') = 0;
3301 }
3302}
3303
3304/********************************************************************/
3313INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
3314{
3315
3316 if (rpc_is_remote())
3317 return rpc_call(RPC_DB_CREATE_KEY, hDB, hKey, key_name, type);
3318
3319#ifdef LOCAL_ROUTINES
3320 {
3321 int status;
3322
3323 if (hDB > _database_entries || hDB <= 0) {
3324 cm_msg(MERROR, "db_create_key", "invalid database handle");
3325 return DB_INVALID_HANDLE;
3326 }
3327
3328 if (!_database[hDB - 1].attached) {
3329 cm_msg(MERROR, "db_create_key", "invalid database handle");
3330 return DB_INVALID_HANDLE;
3331 }
3332
3333 /* lock database */
3335
3337
3338 db_err_msg *msg = NULL;
3339
3340 KEY* pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_create_key", &msg);
3341
3342 if (!pkey) {
3344 if (msg)
3345 db_flush_msg(&msg);
3346 return DB_INVALID_HANDLE;
3347 }
3348
3349 db_allow_write_locked(&_database[hDB-1], "db_create_key");
3350
3351 KEY* newpkey = NULL;
3352
3353 status = db_create_key_wlocked(pheader, pkey, key_name, type, &newpkey, &msg);
3354
3356
3357 if (msg)
3358 db_flush_msg(&msg);
3359
3360 return status;
3361 }
3362#endif /* LOCAL_ROUTINES */
3363
3364 return DB_SUCCESS;
3365}
3366
3367#ifdef LOCAL_ROUTINES
3368
3369int db_create_key_wlocked(DATABASE_HEADER* pheader, KEY* parentKey, const char *key_name, DWORD type, KEY** pnewkey, db_err_msg** msg)
3370{
3371 int status;
3372
3373 if (pnewkey)
3374 *pnewkey = NULL;
3375
3376 if (parentKey== NULL) {
3377 parentKey = (KEY*) db_get_pkey(pheader, pheader->root_key, &status, "db_create_key_wlocked", msg);
3378 if (!parentKey) {
3379 return status;
3380 }
3381 }
3382
3383 status = db_validate_name(key_name, TRUE, "db_create_key_wlocked", msg);
3384 if (status != DB_SUCCESS) {
3385 return status;
3386 }
3387
3388 /* check type */
3390 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());
3391 return DB_INVALID_PARAM;
3392 }
3393
3394 const KEY* pdir = parentKey;
3395
3396 if (pdir->type != TID_KEY) {
3397 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);
3398 return DB_NO_KEY;
3399 }
3400
3401 KEY* pcreated = NULL;
3402
3403 const char* pkey_name = key_name;
3404 do {
3405 // key name is limited to NAME_LENGTH, but buffer has to be slightly longer
3406 // to prevent truncation before db_validate_name checks for correct length. K.O.
3407 char name[NAME_LENGTH+100];
3408
3409 /* extract single key from key_name */
3411
3412 status = db_validate_name(name, FALSE, "db_create_key", msg);
3413 if (status != DB_SUCCESS) {
3414 return status;
3415 }
3416
3417 /* do not allow empty names, like '/dir/dir//dir/' */
3418 if (name[0] == 0) {
3419 return DB_INVALID_PARAM;
3420 }
3421
3422 /* check if parent or current directory */
3423 if (strcmp(name, "..") == 0) {
3424 pdir = db_get_parent(pheader, pdir, &status, "db_create_key", msg);
3425 if (!pdir) {
3426 return status;
3427 }
3428 continue;
3429 }
3430 if (strcmp(name, ".") == 0)
3431 continue;
3432
3433 KEY* pprev = NULL;
3434 const KEY* pitem = db_enum_first_locked(pheader, pdir, msg);
3435
3436 while (pitem) {
3437 if (equal_ustring(name, pitem->name)) {
3438 break;
3439 }
3440 pprev = (KEY*)pitem;
3441 pitem = db_enum_next_locked(pheader, pdir, pitem, msg);
3442 }
3443
3444 if (!pitem) {
3445 /* not found: create new key */
3446
3447 /* check parent for write access */
3448 const KEY* pkeyparent = db_get_parent(pheader, pdir, &status, "db_create_key", msg);
3449 if (!pkeyparent) {
3450 return status;
3451 }
3452
3453 if (!(pkeyparent->access_mode & MODE_WRITE) || (pkeyparent->access_mode & MODE_EXCLUSIVE)) {
3454 return DB_NO_ACCESS;
3455 }
3456
3457 KEYLIST* pkeylist = (KEYLIST*)db_get_pkeylist(pheader, db_pkey_to_hkey(pheader, pdir), pdir, "db_create_key_wlocked", msg);
3458
3459 if (!pkeylist) {
3460 return DB_CORRUPTED;
3461 }
3462
3463 pkeylist->num_keys++;
3464
3465 if (*pkey_name == '/' || type == TID_KEY) {
3466 /* create new key with keylist */
3467 KEY* pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_create_key_subdir");
3468
3469 if (pkey == NULL) {
3470 if (pkeylist->num_keys > 0)
3471 pkeylist->num_keys--;
3472
3473 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());
3474 return DB_FULL;
3475 }
3476
3477 /* append key to key list */
3478 if (pprev)
3479 pprev->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
3480 else
3481 pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
3482
3483 /* set key properties */
3484 pkey->type = TID_KEY;
3485 pkey->num_values = 1;
3486 pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
3487 mstrlcpy(pkey->name, name, sizeof(pkey->name));
3488 pkey->parent_keylist = (POINTER_T) pkeylist - (POINTER_T) pheader;
3489
3490 /* find space for new keylist */
3491 pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST), "db_create_key_B");
3492
3493 if (pkeylist == NULL) {
3494 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());
3495 return DB_FULL;
3496 }
3497
3498 /* store keylist in data field */
3499 pkey->data = (POINTER_T) pkeylist - (POINTER_T) pheader;
3500 pkey->item_size = sizeof(KEYLIST);
3501 pkey->total_size = sizeof(KEYLIST);
3502
3503 pkeylist->parent = (POINTER_T) pkey - (POINTER_T) pheader;
3504 pkeylist->num_keys = 0;
3505 pkeylist->first_key = 0;
3506
3507 pcreated = pkey;
3508 pdir = pkey; // descend into newly created subdirectory
3509 } else {
3510 /* create new key with data */
3511 KEY* pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_create_key_data");
3512
3513 if (pkey == NULL) {
3514 if (pkeylist->num_keys > 0)
3515 pkeylist->num_keys--;
3516
3517 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());
3518 return DB_FULL;
3519 }
3520
3521 /* append key to key list */
3522 if (pprev)
3523 pprev->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
3524 else
3525 pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
3526
3527 pkey->type = type;
3528 pkey->num_values = 1;
3529 pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
3530 mstrlcpy(pkey->name, name, sizeof(pkey->name));
3531 pkey->parent_keylist = (POINTER_T) pkeylist - (POINTER_T) pheader;
3532
3533 /* allocate data */
3534 pkey->item_size = rpc_tid_size(type);
3535 if (pkey->item_size > 0) {
3536 void* data = malloc_data(pheader, pkey->item_size);
3537 if (data == NULL) {
3538 pkey->total_size = 0;
3539 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());
3540 return DB_FULL;
3541 }
3542
3543 pkey->total_size = pkey->item_size;
3544 pkey->data = (POINTER_T)data - (POINTER_T)pheader;
3545 } else {
3546 /* first data is empty */
3547 pkey->item_size = 0;
3548 pkey->total_size = 0;
3549 pkey->data = 0;
3550 }
3551 pcreated = pkey;
3552 }
3553 } else {
3554 /* key found: descend */
3555
3556 /* resolve links */
3557 if (pitem->type == TID_LINK && pkey_name[0]) {
3558 pdir = db_resolve_link_locked(pheader, pitem, &status, msg);
3559 if (!pdir) {
3560 return status;
3561 }
3562 continue;
3563 }
3564
3565 if (!(*pkey_name == '/')) {
3566 if (pitem->type != type) {
3567 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());
3568 return DB_TYPE_MISMATCH;
3569 }
3570 //db_print_pkey(pheader, pitem);
3571
3572 if (pnewkey)
3573 *pnewkey = (KEY*)pitem;
3574
3575 return DB_KEY_EXIST;
3576 }
3577
3578 if (pitem->type != TID_KEY) {
3579 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());
3580 return DB_TYPE_MISMATCH;
3581 }
3582
3583 pdir = pitem;
3584 }
3585 } while (*pkey_name == '/');
3586
3587 assert(pcreated != NULL);
3588
3589 if (pnewkey)
3590 *pnewkey = pcreated;
3591
3592 return DB_SUCCESS;
3593}
3594
3595#endif /* LOCAL_ROUTINES */
3596
3597/********************************************************************/
3607{
3608 HNDLE hkey;
3609 int status;
3610
3611 if (rpc_is_remote())
3613
3614 if (destination == NULL) {
3615 cm_msg(MERROR, "db_create_link", "link destination name is NULL");
3616 return DB_INVALID_NAME;
3617 }
3618
3619 if (destination[0] != '/') {
3620 cm_msg(MERROR, "db_create_link", "link destination name \'%s\' should start with \'/\', relative links are forbidden", destination);
3621 return DB_INVALID_NAME;
3622 }
3623
3624 if (strlen(destination) < 1) {
3625 cm_msg(MERROR, "db_create_link", "link destination name \'%s\' is too short", destination);
3626 return DB_INVALID_NAME;
3627 }
3628
3629 if ((destination[0] == '/') && (destination[1] == 0)) {
3630 cm_msg(MERROR, "db_create_link", "links to \"/\" are forbidden");
3631 return DB_INVALID_NAME;
3632 }
3633
3634 /* check if destination exists */
3636 if (status != DB_SUCCESS) {
3637 cm_msg(MERROR, "db_create_link", "Link destination \"%s\" does not exist", destination);
3638 return DB_NO_KEY;
3639 }
3640
3641 //printf("db_create_link: [%s] hkey %d\n", destination, hkey);
3642
3643 /* check if link already exists */
3645 if (status != DB_SUCCESS) {
3646 // create new link
3648 } else {
3649 // check if key is TID_LINK
3650 KEY key;
3651 db_get_key(hDB, hkey, &key);
3652 if (key.type != TID_LINK) {
3653 cm_msg(MERROR, "db_create_link", "Existing key \"%s\" is not a link", link_name);
3654 return DB_KEY_EXIST;
3655 }
3656
3657 // modify existing link
3659 }
3660
3661 return status;
3662}
3663
3664/********************************************************************/
3675{
3676#ifdef LOCAL_ROUTINES
3677 {
3678 DATABASE_HEADER *pheader;
3683 INT status;
3684 BOOL locked = FALSE;
3685
3686 if (hDB > _database_entries || hDB <= 0) {
3687 cm_msg(MERROR, "db_delete_key1", "invalid database handle");
3688 return DB_INVALID_HANDLE;
3689 }
3690
3691 if (!_database[hDB - 1].attached) {
3692 cm_msg(MERROR, "db_delete_key1", "invalid database handle");
3693 return DB_INVALID_HANDLE;
3694 }
3695
3696 if (hKey < (int) sizeof(DATABASE_HEADER)) {
3697 cm_msg(MERROR, "db_delete_key1", "invalid key handle");
3698 return DB_INVALID_HANDLE;
3699 }
3700
3701 /* lock database at the top level */
3702 if (level == 0) {
3704 locked = TRUE;
3705 }
3706
3707 pheader = _database[hDB - 1].database_header;
3708
3709 /* check if hKey argument is correct */
3710 if (!db_validate_hkey(pheader, hKey)) {
3711 if (locked)
3713 return DB_INVALID_HANDLE;
3714 }
3715
3716 pkey = (KEY *) ((char *) pheader + hKey);
3717
3718 /* check if someone has opened key or parent */
3719 if (level == 0)
3720 do {
3721#ifdef CHECK_OPEN_RECORD
3722 if (pkey->notify_count) {
3723 if (locked)
3725 return DB_OPEN_RECORD;
3726 }
3727#endif
3728 if (pkey->parent_keylist == 0)
3729 break;
3730
3731 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
3732 // FIXME: validate pkeylist->parent
3733 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
3734 } while (TRUE);
3735
3736 pkey = (KEY *) ((char *) pheader + hKey);
3737 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
3738
3740
3741 /* first recures subtree for keys */
3742 if (pkey->type == TID_KEY && pkeylist->first_key) {
3743 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
3744
3745 do {
3746 pnext_key = (KEY *) (POINTER_T) pkey->next_key; // FIXME: what is this casting of hKey to pointer?
3747
3749
3750 if (status == DB_NO_ACCESS)
3751 deny_delete = TRUE;
3752
3753 if (pnext_key) {
3754 // FIXME: validate pnext_key
3755 pkey = (KEY *) ((char *) pheader + (POINTER_T) pnext_key);
3756 }
3757 } while (pnext_key);
3758 }
3759
3760 /* follow links if requested */
3761 if (pkey->type == TID_LINK && follow_links) {
3762 status = db_find_key1(hDB, 0, (char *) pheader + pkey->data, &hKeyLink);
3763 if (status == DB_SUCCESS && follow_links < 100)
3765
3766 if (follow_links == 100)
3767 cm_msg(MERROR, "db_delete_key1", "try to delete cyclic link");
3768 }
3769
3770 /* check if hKey argument is correct */
3771 if (!db_validate_hkey(pheader, hKey)) {
3772 if (locked)
3774 return DB_INVALID_HANDLE;
3775 }
3776
3777 pkey = (KEY *) ((char *) pheader + hKey);
3778
3779 /* return if key was already deleted by cyclic link */
3780 if (pkey->parent_keylist == 0) {
3781 if (locked)
3783 return DB_SUCCESS;
3784 }
3785
3786 /* now delete key */
3787 if (hKey != pheader->root_key) {
3788 if (!(pkey->access_mode & MODE_DELETE) || deny_delete) {
3789 if (locked)
3791 return DB_NO_ACCESS;
3792 }
3793#ifdef CHECK_OPEN_RECORD
3794 if (pkey->notify_count) {
3795 if (locked)
3797 return DB_OPEN_RECORD;
3798 }
3799#endif
3800 db_allow_write_locked(&_database[hDB - 1], "db_delete_key1");
3801
3802 /* delete key data */
3803 if (pkey->type == TID_KEY)
3804 free_key(pheader, (char *) pheader + pkey->data, pkey->total_size);
3805 else
3806 free_data(pheader, (char *) pheader + pkey->data, pkey->total_size, "db_delete_key1");
3807
3808 /* unlink key from list */
3809 pnext_key = (KEY *) (POINTER_T) pkey->next_key;
3810 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
3811
3812 if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
3813 /* key is first in list */
3814 pkeylist->first_key = (POINTER_T) pnext_key;
3815 } else {
3816 /* find predecessor */
3817 pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
3818 while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey)
3819 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
3820 pkey_tmp->next_key = (POINTER_T) pnext_key;
3821 }
3822
3823 /* delete key */
3824 free_key(pheader, pkey, sizeof(KEY));
3825 pkeylist->num_keys--;
3826 }
3827
3828 if (locked)
3830 }
3831#endif /* LOCAL_ROUTINES */
3832
3833 return DB_SUCCESS;
3834}
3835
3836/********************************************************************/
3868
3869#ifdef LOCAL_ROUTINES
3870static const KEY* db_find_pkey_locked(const DATABASE_HEADER *pheader, const KEY* pkey, const char *key_name, int *pstatus, db_err_msg **msg)
3871{
3872 if (pkey == NULL) {
3873 pkey = db_get_pkey(pheader, pheader->root_key, pstatus, "db_find_key", msg);
3874 if (!pkey) {
3875 return NULL;
3876 }
3877 }
3878
3879 HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
3880
3881 if (pkey->type != TID_KEY) {
3882 DWORD tid = pkey->type;
3883 std::string path = db_get_path_locked(pheader, hKey);
3884 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);
3885 if (pstatus)
3886 *pstatus = DB_NO_KEY;
3887 return NULL;
3888 }
3889
3890 if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
3891 if (!(pkey->access_mode & MODE_READ)) {
3892 if (pstatus)
3894 return NULL;
3895 }
3896 return pkey;
3897 }
3898
3899 const KEYLIST *pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
3900 if (!pkeylist) {
3901 if (pstatus)
3903 return NULL;
3904 }
3905
3907
3908 const char *pkey_name = key_name;
3909 do {
3910 assert(pkeylist!=NULL); // should never happen!
3911
3912 char str[MAX_ODB_PATH];
3913
3914 /* extract single subkey from key_name */
3915 pkey_name = extract_key(pkey_name, str, sizeof(str));
3916
3917 /* strip trailing '[n]' */
3918 if (strchr(str, '[') && str[strlen(str) - 1] == ']')
3919 *strchr(str, '[') = 0;
3920
3921 /* check if parent or current directory */
3922 if (strcmp(str, "..") == 0) {
3923 if (pkey->parent_keylist) {
3924 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
3925 // FIXME: validate pkeylist->parent
3926 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
3927 }
3928 continue;
3929 }
3930 if (strcmp(str, ".") == 0)
3931 continue;
3932
3934
3935 hKey = pkeylist->first_key;
3936
3937 if (hKey == 0) {
3938 // empty subdirectory
3939 if (pstatus)
3940 *pstatus = DB_NO_KEY;
3941 return NULL;
3942 }
3943
3944 int i;
3945 for (i = 0; i < pkeylist->num_keys; i++) {
3946 pkey = db_get_pkey(pheader, hKey, pstatus, "db_find_key", msg);
3947
3948 if (!pkey) {
3949 std::string path = db_get_path_locked(pheader, last_good_hkey);
3950 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);
3951 return NULL;
3952 }
3953
3954 if (!db_validate_key_offset(pheader, pkey->next_key)) {
3955 std::string path = db_get_path_locked(pheader, hKey);
3956 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);
3957 if (pstatus)
3959 return NULL;
3960 }
3961
3962 if (equal_ustring(str, pkey->name))
3963 break;
3964
3965 if (pkey->next_key == 0) {
3966 if (pstatus)
3967 *pstatus = DB_NO_KEY;
3968 return NULL;
3969 }
3970
3971 hKey = pkey->next_key;
3972 }
3973
3974 if (i == pkeylist->num_keys) {
3975 if (pstatus)
3976 *pstatus = DB_NO_KEY;
3977 return NULL;
3978 }
3979
3980 /* resolve links */
3981 if (pkey->type == TID_LINK) {
3982 /* copy destination, strip '/' */
3983 mstrlcpy(str, (char *) pheader + pkey->data, sizeof(str));
3984 if (str[strlen(str) - 1] == '/')
3985 str[strlen(str) - 1] = 0;
3986
3987 /* if link is pointer to array index, return link instead of destination */
3988 if (str[strlen(str) - 1] == ']')
3989 break;
3990
3991 /* append rest of key name if existing */
3992 if (pkey_name[0]) {
3993 mstrlcat(str, pkey_name, sizeof(str));
3994 return db_find_pkey_locked(pheader, NULL, str, pstatus, msg);
3995 } else {
3996 /* if last key in chain is a link, return its destination */
3997 int status = 0;
3998 const KEY *plink = db_find_pkey_locked(pheader, NULL, str, &status, msg);
3999 if (pstatus) {
4000 if (status == DB_NO_KEY)
4002 else
4003 *pstatus = status;
4004 }
4005 return plink;
4006 }
4007 }
4008
4009 /* key found: check if last in chain */
4010 if (*pkey_name == '/') {
4011 if (pkey->type != TID_KEY) {
4012 if (pstatus)
4013 *pstatus = DB_NO_KEY;
4014 return NULL;
4015 }
4016 }
4017
4018 if (pkey->type == TID_KEY) {
4019 /* descend one level */
4020 pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
4021
4022 if (!pkeylist) {
4023 if (pstatus)
4025 return NULL;
4026 }
4027 } else {
4028 pkeylist = NULL;
4029 }
4030
4031 } while (*pkey_name == '/' && *(pkey_name + 1));
4032
4033 return pkey;
4034}
4035
4036static int db_find_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, const char *key_name, HNDLE *subhKey, db_err_msg **msg)
4037{
4038 int status;
4039 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_find_key", msg);
4040 if (!pkey) {
4041 if (subhKey)
4042 *subhKey = 0;
4043 return status;
4044 }
4045
4046 const KEY* plink = db_find_pkey_locked(pheader, pkey, key_name, &status, msg);
4047
4048 if (!plink) {
4049 *subhKey = 0;
4050 return status;
4051 }
4052
4053 *subhKey = db_pkey_to_hkey(pheader, plink);
4054
4055 return DB_SUCCESS;
4056}
4057#endif /* LOCAL_ROUTINES */
4058
4059/********************************************************************/
4084INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4085{
4086 if (rpc_is_remote())
4087 return rpc_call(RPC_DB_FIND_KEY, hDB, hKey, key_name, subhKey);
4088
4089#ifdef LOCAL_ROUTINES
4090 {
4091 DATABASE_HEADER *pheader;
4092 INT status;
4093
4094 *subhKey = 0;
4095
4096 if (hDB > _database_entries || hDB <= 0) {
4097 cm_msg(MERROR, "db_find_key", "invalid database handle");
4098 return DB_INVALID_HANDLE;
4099 }
4100
4101 if (!_database[hDB - 1].attached) {
4102 cm_msg(MERROR, "db_find_key", "invalid database handle");
4103 return DB_INVALID_HANDLE;
4104 }
4105
4106 db_err_msg *msg = NULL;
4107
4109
4110 pheader = _database[hDB - 1].database_header;
4111
4112 status = db_find_key_locked(pheader, hKey, key_name, subhKey, &msg);
4113
4115
4116 if (msg)
4117 db_flush_msg(&msg);
4118
4119 return status;
4120 }
4121#endif /* LOCAL_ROUTINES */
4122
4123 return DB_SUCCESS;
4124}
4125
4127#ifndef DOXYGEN_SHOULD_SKIP_THIS
4128
4129/*------------------------------------------------------------------*/
4130INT db_find_key1(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4131/********************************************************************\
4132
4133 Routine: db_find_key1
4134
4135 Purpose: Same as db_find_key, but without DB locking
4136
4137 Input:
4138 HNDLE bufer_handle Handle to the database
4139 HNDLE hKey Key handle to start the search
4140 char *key_name Name of key in the form "/key/key/key"
4141
4142 Output:
4143 INT *handle Key handle
4144
4145 Function value:
4146 DB_SUCCESS Successful completion
4147 DB_INVALID_HANDLE Database handle is invalid
4148 DB_NO_KEY Key doesn't exist
4149 DB_NO_ACCESS No access to read key
4150
4151\********************************************************************/
4152{
4153 if (rpc_is_remote())
4154 return rpc_call(RPC_DB_FIND_KEY, hDB, hKey, key_name, subhKey);
4155
4156#ifdef LOCAL_ROUTINES
4157 {
4158 DATABASE_HEADER *pheader;
4160 KEY *pkey;
4161 const char *pkey_name;
4162 INT i;
4163
4164 *subhKey = 0;
4165
4166 if (hDB > _database_entries || hDB <= 0) {
4167 cm_msg(MERROR, "db_find_key", "invalid database handle");
4168 return DB_INVALID_HANDLE;
4169 }
4170
4171 if (!_database[hDB - 1].attached) {
4172 cm_msg(MERROR, "db_find_key", "invalid database handle");
4173 return DB_INVALID_HANDLE;
4174 }
4175
4176 pheader = _database[hDB - 1].database_header;
4177 if (!hKey)
4178 hKey = pheader->root_key;
4179
4180 /* check if hKey argument is correct */
4181 if (!db_validate_hkey(pheader, hKey)) {
4182 return DB_INVALID_HANDLE;
4183 }
4184
4185 pkey = (KEY *) ((char *) pheader + hKey);
4186
4187 if (pkey->type != TID_KEY) {
4188 cm_msg(MERROR, "db_find_key", "key has no subkeys");
4189 *subhKey = 0;
4190 return DB_NO_KEY;
4191 }
4192 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4193
4194 if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
4195 if (!(pkey->access_mode & MODE_READ)) {
4196 *subhKey = 0;
4197 return DB_NO_ACCESS;
4198 }
4199
4200 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4201
4202 return DB_SUCCESS;
4203 }
4204
4205 pkey_name = key_name;
4206 do {
4207 char str[MAX_ODB_PATH];
4208
4209 /* extract single subkey from key_name */
4210 pkey_name = extract_key(pkey_name, str, sizeof(str));
4211
4212 /* check if parent or current directory */
4213 if (strcmp(str, "..") == 0) {
4214 if (pkey->parent_keylist) {
4215 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4216 // FIXME: validate pkeylist->parent
4217 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
4218 }
4219 continue;
4220 }
4221 if (strcmp(str, ".") == 0)
4222 continue;
4223
4224 /* check if key is in keylist */
4225 // FIXME: validate pkeylist->first_key
4226 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
4227
4228 for (i = 0; i < pkeylist->num_keys; i++) {
4229 if (equal_ustring(str, pkey->name))
4230 break;
4231
4232 // FIXME: validate pkey->next_key
4233 pkey = (KEY *) ((char *) pheader + pkey->next_key);
4234 }
4235
4236 if (i == pkeylist->num_keys) {
4237 *subhKey = 0;
4238 return DB_NO_KEY;
4239 }
4240
4241 /* resolve links */
4242 if (pkey->type == TID_LINK) {
4243 /* copy destination, strip '/' */
4244 strcpy(str, (char *) pheader + pkey->data);
4245 if (str[strlen(str) - 1] == '/')
4246 str[strlen(str) - 1] = 0;
4247
4248 /* append rest of key name if existing */
4249 if (pkey_name[0]) {
4251 return db_find_key1(hDB, 0, str, subhKey);
4252 } else {
4253 /* if last key in chain is a link, return its destination */
4254 return db_find_link1(hDB, 0, str, subhKey);
4255 }
4256 }
4257
4258 /* key found: check if last in chain */
4259 if (*pkey_name == '/') {
4260 if (pkey->type != TID_KEY) {
4261 *subhKey = 0;
4262 return DB_NO_KEY;
4263 }
4264 }
4265
4266 /* descend one level */
4267 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4268
4269 } while (*pkey_name == '/' && *(pkey_name + 1));
4270
4271 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4272 }
4273#endif /* LOCAL_ROUTINES */
4274
4275 return DB_SUCCESS;
4276}
4277
4278/*------------------------------------------------------------------*/
4279INT db_find_link(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4280/********************************************************************\
4281
4282 Routine: db_find_link
4283
4284 Purpose: Find a key or link by name and return its handle
4285 (internal address). The only difference of this routine
4286 compared with db_find_key is that if the LAST key in
4287 the chain is a link, it is NOT evaluated. Links not being
4288 the last in the chain are evaluated.
4289
4290 Input:
4291 HNDLE bufer_handle Handle to the database
4292 HNDLE hKey Key handle to start the search
4293 char *key_name Name of key in the form "/key/key/key"
4294
4295 Output:
4296 INT *handle Key handle
4297
4298 Function value:
4299 DB_SUCCESS Successful completion
4300 DB_INVALID_HANDLE Database handle is invalid
4301 DB_NO_KEY Key doesn't exist
4302 DB_NO_ACCESS No access to read key
4303
4304\********************************************************************/
4305{
4306 if (rpc_is_remote())
4307 return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
4308
4309#ifdef LOCAL_ROUTINES
4310 {
4311 DATABASE_HEADER *pheader;
4313 KEY *pkey;
4314 const char *pkey_name;
4315 INT i;
4316
4317 *subhKey = 0;
4318
4319 if (hDB > _database_entries || hDB <= 0) {
4320 cm_msg(MERROR, "db_find_link", "Invalid database handle");
4321 return DB_INVALID_HANDLE;
4322 }
4323
4324 if (!_database[hDB - 1].attached) {
4325 cm_msg(MERROR, "db_find_link", "invalid database handle");
4326 return DB_INVALID_HANDLE;
4327 }
4328
4330
4331 pheader = _database[hDB - 1].database_header;
4332 if (!hKey)
4333 hKey = pheader->root_key;
4334
4335 /* check if hKey argument is correct */
4336 if (!db_validate_hkey(pheader, hKey)) {
4338 return DB_INVALID_HANDLE;
4339 }
4340
4341 pkey = (KEY *) ((char *) pheader + hKey);
4342
4343 if (pkey->type != TID_KEY) {
4345 cm_msg(MERROR, "db_find_link", "key has no subkeys");
4346 return DB_NO_KEY;
4347 }
4348 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4349
4350 if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
4351 if (!(pkey->access_mode & MODE_READ)) {
4352 *subhKey = 0;
4354 return DB_NO_ACCESS;
4355 }
4356
4357 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4358
4360 return DB_SUCCESS;
4361 }
4362
4363 pkey_name = key_name;
4364 do {
4365 char str[MAX_ODB_PATH];
4366
4367 /* extract single subkey from key_name */
4368 pkey_name = extract_key(pkey_name, str, sizeof(str));
4369
4370 /* check if parent or current directory */
4371 if (strcmp(str, "..") == 0) {
4372 if (pkey->parent_keylist) {
4373 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4374 // FIXME: validate pkeylist->parent
4375 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
4376 }
4377 continue;
4378 }
4379 if (strcmp(str, ".") == 0)
4380 continue;
4381
4382 /* check if key is in keylist */
4383 // FIXME: validate pkeylist->first_key
4384 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
4385
4386 for (i = 0; i < pkeylist->num_keys; i++) {
4387 if (!db_validate_key_offset(pheader, pkey->next_key)) {
4388 int pkey_next_key = pkey->next_key;
4390 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));
4391 *subhKey = 0;
4392 return DB_CORRUPTED;
4393 }
4394
4395 if (equal_ustring(str, pkey->name))
4396 break;
4397
4398 pkey = (KEY *) ((char *) pheader + pkey->next_key); // FIXME: pkey->next_key could be zero
4399 }
4400
4401 if (i == pkeylist->num_keys) {
4402 *subhKey = 0;
4404 return DB_NO_KEY;
4405 }
4406
4407 /* resolve links if not last in chain */
4408 if (pkey->type == TID_LINK && *pkey_name == '/') {
4409 /* copy destination, strip '/' */
4410 strcpy(str, (char *) pheader + pkey->data);
4411 if (str[strlen(str) - 1] == '/')
4412 str[strlen(str) - 1] = 0;
4413
4414 /* append rest of key name */
4417 return db_find_link(hDB, 0, str, subhKey);
4418 }
4419
4420 /* key found: check if last in chain */
4421 if ((*pkey_name == '/')) {
4422 if (pkey->type != TID_KEY) {
4423 *subhKey = 0;
4425 return DB_NO_KEY;
4426 }
4427 }
4428
4429 /* descend one level */
4430 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4431
4432 } while (*pkey_name == '/' && *(pkey_name + 1));
4433
4434 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4435
4437 }
4438#endif /* LOCAL_ROUTINES */
4439
4440 return DB_SUCCESS;
4441}
4442
4443/*------------------------------------------------------------------*/
4444INT db_find_link1(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4445/********************************************************************\
4446
4447 Routine: db_find_link1
4448
4449 Purpose: Same ad db_find_link, but without DB locking
4450
4451 Input:
4452 HNDLE bufer_handle Handle to the database
4453 HNDLE hKey Key handle to start the search
4454 char *key_name Name of key in the form "/key/key/key"
4455
4456 Output:
4457 INT *handle Key handle
4458
4459 Function value:
4460 DB_SUCCESS Successful completion
4461 DB_INVALID_HANDLE Database handle is invalid
4462 DB_NO_KEY Key doesn't exist
4463 DB_NO_ACCESS No access to read key
4464
4465\********************************************************************/
4466{
4467 if (rpc_is_remote())
4468 return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
4469
4470#ifdef LOCAL_ROUTINES
4471 {
4472 DATABASE_HEADER *pheader;
4474 KEY *pkey;
4475 const char *pkey_name;
4476 INT i;
4477
4478 *subhKey = 0;
4479
4480 if (hDB > _database_entries || hDB <= 0) {
4481 cm_msg(MERROR, "db_find_link", "Invalid database handle");
4482 return DB_INVALID_HANDLE;
4483 }
4484
4485 if (!_database[hDB - 1].attached) {
4486 cm_msg(MERROR, "db_find_link", "invalid database handle");
4487 return DB_INVALID_HANDLE;
4488 }
4489
4490 pheader = _database[hDB - 1].database_header;
4491 if (!hKey)
4492 hKey = pheader->root_key;
4493
4494 /* check if hKey argument is correct */
4495 if (!db_validate_hkey(pheader, hKey)) {
4496 return DB_INVALID_HANDLE;
4497 }
4498
4499 pkey = (KEY *) ((char *) pheader + hKey);
4500
4501 if (pkey->type != TID_KEY) {
4502 cm_msg(MERROR, "db_find_link", "key has no subkeys");
4503 return DB_NO_KEY;
4504 }
4505 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4506
4507 if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
4508 if (!(pkey->access_mode & MODE_READ)) {
4509 *subhKey = 0;
4510 return DB_NO_ACCESS;
4511 }
4512
4513 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4514
4515 return DB_SUCCESS;
4516 }
4517
4518 pkey_name = key_name;
4519 do {
4520 char str[MAX_ODB_PATH];
4521 /* extract single subkey from key_name */
4522 pkey_name = extract_key(pkey_name, str, sizeof(str));
4523
4524 /* check if parent or current directory */
4525 if (strcmp(str, "..") == 0) {
4526 if (pkey->parent_keylist) {
4527 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4528 // FIXME: validate pkeylist->parent
4529 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
4530 }
4531 continue;
4532 }
4533 if (strcmp(str, ".") == 0)
4534 continue;
4535
4536 /* check if key is in keylist */
4537 // FIXME: validate pkeylist->first_key
4538 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
4539
4540 for (i = 0; i < pkeylist->num_keys; i++) {
4541 if (!db_validate_key_offset(pheader, pkey->next_key)) {
4542 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));
4543 *subhKey = 0;
4544 return DB_CORRUPTED;
4545 }
4546
4547 if (equal_ustring(str, pkey->name))
4548 break;
4549
4550 pkey = (KEY *) ((char *) pheader + pkey->next_key); // FIXME: pkey->next_key could be zero
4551 }
4552
4553 if (i == pkeylist->num_keys) {
4554 *subhKey = 0;
4555 return DB_NO_KEY;
4556 }
4557
4558 /* resolve links if not last in chain */
4559 if (pkey->type == TID_LINK && *pkey_name == '/') {
4560 /* copy destination, strip '/' */
4561 strcpy(str, (char *) pheader + pkey->data);
4562 if (str[strlen(str) - 1] == '/')
4563 str[strlen(str) - 1] = 0;
4564
4565 /* append rest of key name */
4567 return db_find_link1(hDB, 0, str, subhKey);
4568 }
4569
4570 /* key found: check if last in chain */
4571 if ((*pkey_name == '/')) {
4572 if (pkey->type != TID_KEY) {
4573 *subhKey = 0;
4574 return DB_NO_KEY;
4575 }
4576 }
4577
4578 /* descend one level */
4579 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4580
4581 } while (*pkey_name == '/' && *(pkey_name + 1));
4582
4583 *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4584 }
4585#endif /* LOCAL_ROUTINES */
4586
4587 return DB_SUCCESS;
4588}
4589
4590/*------------------------------------------------------------------*/
4592/********************************************************************\
4593
4594 Routine: db_find_keys
4595
4596 Purpose: finds all ODB keys matching an odb path
4597 '*' and '?' wildcard expansion available
4598
4599 Input:
4600 HNDLE hDB Handle to the database
4601 HNDLE hKeyRoot Key handle to start the search
4602 char *odbpath Pattern of keys in the form "/key/key/key", can include '*' and '?'
4603
4604 Output:
4605 std::vector<HNDLE> &hKeyVector Key handle
4606
4607 Function value:
4608 DB_SUCCESS Successful completion
4609 DB_INVALID_HANDLE Database handle is invalid
4610 DB_NO_KEY No access to any key
4611 DB_NO_ACCESS No access to a read key
4612
4613\********************************************************************/
4614{
4615 HNDLE hKey, hSubKey=0;
4616 KEY key;
4617 INT status;
4618 char *pattern = NULL;
4619 char *subkeypath = NULL;
4620 char *parentkeypath = NULL;
4621 char localpath[256];
4622
4623 //make work on a local copy od odbpath to allow recursion
4624 strcpy(localpath, odbpath);
4626
4627 char *wildcard = strpbrk(localpath, "*?");
4628 if(wildcard){
4629 //wildcard found
4630 subkeypath = strchr(wildcard, '/');
4631 if(subkeypath){
4632 //truncate the string at slash
4633 *subkeypath = 0;
4634 subkeypath++;
4635 }
4637 if(parentkeypath){
4638 //truncate there too
4639 *parentkeypath = 0;
4640 pattern = parentkeypath+1;
4641 if((parentkeypath-1) == localpath){
4642 //path starts with '/', no parent path
4644 } else {
4646 }
4647 } else {
4648 //wildcard at top level, start with pattern
4649 pattern = localpath;
4651 }
4652 }
4653
4654 //if available search for parent key path
4655 if(parentkeypath){
4657 if (status != DB_SUCCESS)
4658 return status;
4659 } else {
4660 hKey = hKeyRoot;
4661 }
4662
4663 //if a pattern is found
4664 if(pattern){
4665 //try match all subkeys
4666 status = DB_NO_KEY;
4667 for (int i=0 ; ; i++)
4668 {
4670 if (!hSubKey)
4671 break; // end of list reached
4673
4674 if(strmatch(pattern, key.name)){
4675 //match
4676 if(!subkeypath){
4677 //found
4678 hKeyVector.push_back(hSubKey);
4679
4680 } else if (key.type == TID_KEY){
4681 //recurse with hSubKey as root key and subkeypath as path
4683 if (subkeystatus != DB_NO_KEY)
4685
4686 if (status != DB_SUCCESS && status != DB_NO_KEY)
4687 break;
4688 }
4689 }
4690 }
4691
4692 return status;
4693 } else {
4694 //no pattern: hKey matches!
4695 db_get_key(hDB, hKey, &key);
4696 hKeyVector.push_back(hKey);
4697
4698 return DB_SUCCESS;
4699 }
4700
4701}
4702
4703/*------------------------------------------------------------------*/
4705/********************************************************************\
4706
4707 Routine: db_get_parent
4708
4709 Purpose: return an handle to the parent key
4710
4711 Input:
4712 HNDLE bufer_handle Handle to the database
4713 HNDLE hKey Key handle of the key
4714
4715 Output:
4716 INT *handle Parent key handle
4717
4718 Function value:
4719 DB_SUCCESS Successful completion
4720
4721\********************************************************************/
4722{
4723 if (rpc_is_remote())
4725
4726#ifdef LOCAL_ROUTINES
4727 {
4728
4729 DATABASE_HEADER *pheader;
4730 const KEY *pkey;
4731
4732 if (hDB > _database_entries || hDB <= 0) {
4733 cm_msg(MERROR, "db_get_parent", "invalid database handle");
4734 return DB_INVALID_HANDLE;
4735 }
4736
4737 if (!_database[hDB - 1].attached) {
4738 cm_msg(MERROR, "db_get_parent", "invalid database handle");
4739 return DB_INVALID_HANDLE;
4740 }
4741
4742 if (hKey < (int) sizeof(DATABASE_HEADER)) {
4743 cm_msg(MERROR, "db_get_parent", "invalid key handle");
4744 return DB_INVALID_HANDLE;
4745 }
4746
4748
4749 pheader = _database[hDB - 1].database_header;
4750 pkey = (const KEY *) ((char *) pheader + hKey);
4751
4752 /* find parent key */
4753 const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4754
4755 if (!db_validate_hkey(pheader, pkeylist->parent)) {
4757 return DB_INVALID_HANDLE;
4758 }
4759
4760 pkey = (const KEY *) ((char *) pheader + pkeylist->parent);
4761
4762 *parenthKey = (POINTER_T) pkey - (POINTER_T) pheader;
4763
4765 }
4766#endif
4767
4768 return DB_SUCCESS;
4769}
4770
4771/*------------------------------------------------------------------*/
4773/********************************************************************\
4774
4775 Routine: db_scan_tree
4776
4777 Purpose: Scan a subtree recursively and call 'callback' for each key
4778
4779 Input:
4780 HNDLE hDB Handle to the database
4781 HNDLE hKeyRoot Key to start scan from, 0 for root
4782 INT level Recursion level
4783 void *callback Callback routine called with params:
4784 hDB Copy of hDB
4785 hKey Copy of hKey
4786 key Key associated with hKey
4787 INT Recursion level
4788 info Copy of *info
4789 void *info Optional data copied to callback routine
4790
4791 Output:
4792 implicit via callback
4793
4794 Function value:
4795 DB_SUCCESS Successful completion
4796 <all error codes of db_get_key>
4797
4798\********************************************************************/
4799{
4800 HNDLE hSubkey;
4801 KEY key;
4802 INT i, status;
4803
4805 if (status != DB_SUCCESS)
4806 return status;
4807
4809 if (status == 0)
4810 return status;
4811
4812 if (key.type == TID_KEY) {
4813 for (i = 0;; i++) {
4815
4816 if (!hSubkey)
4817 break;
4818
4820 }
4821 }
4822
4823 return DB_SUCCESS;
4824}
4825
4826#ifdef LOCAL_ROUTINES
4827
4828int 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)
4829{
4830 assert(pkey != NULL);
4831 assert(level < MAX_ODB_PATH);
4832
4833 int status = callback(pheader, pkey, level, info, msg);
4834 if (status == 0)
4835 return status;
4836
4837 if (pkey->type == TID_KEY) {
4838 const KEY* subkey = db_enum_first_locked(pheader, pkey, msg);
4839 while (subkey != NULL) {
4840 db_scan_tree_locked(pheader, subkey, level + 1, callback, info, msg);
4841 subkey = db_enum_next_locked(pheader, pkey, subkey, msg);
4842 }
4843 }
4844
4845 return DB_SUCCESS;
4846}
4847
4848#endif // LOCAL_ROUTINES
4849
4850/*------------------------------------------------------------------*/
4852/********************************************************************\
4853
4854 Routine: db_scan_tree_link
4855
4856 Purpose: Scan a subtree recursively and call 'callback' for each key.
4857 Similar to db_scan_tree but without following links.
4858
4859 Input:
4860 HNDLE hDB Handle to the database
4861 HNDLE hKeyRoot Key to start scan from, 0 for root
4862 INT level Recursion level
4863 void *callback Callback routine called with params:
4864 hDB Copy of hDB
4865 hKey Copy of hKey
4866 key Key associated with hKey
4867 INT Recursion level
4868 info Copy of *info
4869 void *info Optional data copied to callback routine
4870
4871 Output:
4872 implicit via callback
4873
4874 Function value:
4875 DB_SUCCESS Successful completion
4876 <all error codes of db_get_key>
4877
4878\********************************************************************/
4879{
4880 HNDLE hSubkey;
4881 KEY key;
4882 INT i, status;
4883
4885 if (status != DB_SUCCESS)
4886 return status;
4887
4888 callback(hDB, hKey, &key, level, info);
4889
4890 if (key.type == TID_KEY) {
4891 for (i = 0;; i++) {
4893
4894 if (!hSubkey)
4895 break;
4896
4898 }
4899 }
4900
4901 return DB_SUCCESS;
4902}
4903
4904#ifdef LOCAL_ROUTINES
4905/*------------------------------------------------------------------*/
4906static std::string db_get_path_locked(const DATABASE_HEADER* pheader, const KEY* pkey)
4907{
4908 std::string path = "";
4909 while (1) {
4910 //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());
4911
4912 /* check key type */
4913 if (pkey->type <= 0 || pkey->type >= TID_LAST) {
4914 char buf[256];
4915 sprintf(buf, "(INVALID_KEY_TYPE_%d)", pkey->type);
4916 std::string xpath;
4917 xpath += buf;
4918 if (path.length() > 0) {
4919 xpath += "/";
4920 xpath += path;
4921 }
4922 return xpath;
4923 }
4924
4925 /* add key name in front of path */
4926 std::string str = path;
4927 path = "";
4928 if (pkey->name[0] == 0) {
4929 path += "(EMPTY_NAME)";
4930 } else {
4931 path += pkey->name;
4932 }
4933 if (str.length() > 0)
4934 path += "/";
4935 path += str;
4936
4937 if (!pkey->parent_keylist) {
4938 return path;
4939 }
4940
4941 if (!db_validate_data_offset(pheader, pkey->parent_keylist)) {
4942 return "(INVALID_PARENT_KEYLIST)/" + path;
4943 }
4944
4945 /* find parent key */
4946 const KEYLIST* pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4947
4948 if (pkeylist->parent == pheader->root_key) {
4949 return "/" + path;
4950 }
4951
4952 if (pkeylist->parent == 0) {
4953 return "(NULL_PARENT)/" + path;
4954 }
4955
4956 if (!db_validate_key_offset(pheader, pkeylist->parent)) {
4957 return "(INVALID_PARENT)/" + path;
4958 }
4959
4960 pkey = (const KEY *) ((char *) pheader + pkeylist->parent);
4961 };
4962
4963 /* NOT REACHED */
4964}
4965
4966/*------------------------------------------------------------------*/
4967static std::string db_get_path_locked(const DATABASE_HEADER* pheader, HNDLE hKey)
4968{
4969 //printf("db_get_path_locked: hkey %d\n", hKey);
4970
4971 if (!hKey)
4972 hKey = pheader->root_key;
4973
4974 if (hKey == pheader->root_key) {
4975 return "/";
4976 }
4977
4978 /* check if hKey argument is correct */
4979 if (hKey == 0) {
4980 return "(ZERO_HKEY)";
4981 }
4982
4983 /* check if hKey argument is correct */
4984 if (!db_validate_key_offset(pheader, hKey)) {
4985 return "(INVALID_HKEY)";
4986 }
4987
4988 const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
4989
4990 return db_get_path_locked(pheader, pkey);
4991}
4992#endif /* LOCAL_ROUTINES */
4993
4994/*------------------------------------------------------------------*/
4995INT db_get_path(HNDLE hDB, HNDLE hKey, char *path, INT buf_size)
4996/********************************************************************\
4997
4998 Routine: db_get_path
4999
5000 Purpose: Get full path of a key
5001
5002 Input:
5003 HNDLE hDB Handle to the database
5004 HNDLE hKey Key handle
5005 INT buf_size Maximum size of path buffer (including
5006 trailing zero)
5007
5008 Output:
5009 char path[buf_size] Path string
5010
5011 Function value:
5012 DB_SUCCESS Successful completion
5013 DB_INVALID_HANDLE Database handle is invalid
5014 DB_NO_MEMORY path buffer is to small to contain full
5015 string
5016
5017\********************************************************************/
5018{
5019 if (rpc_is_remote())
5020 return rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, buf_size);
5021
5022#ifdef LOCAL_ROUTINES
5023 {
5024 DATABASE_HEADER *pheader;
5025
5026 if (hDB > _database_entries || hDB <= 0) {
5027 cm_msg(MERROR, "db_get_path", "invalid database handle");
5028 return DB_INVALID_HANDLE;
5029 }
5030
5031 if (!_database[hDB - 1].attached) {
5032 cm_msg(MERROR, "db_get_path", "invalid database handle");
5033 return DB_INVALID_HANDLE;
5034 }
5035
5037
5038 pheader = _database[hDB - 1].database_header;
5039
5040 std::string xpath = db_get_path_locked(pheader, hKey);
5041
5043
5044 mstrlcpy(path, xpath.c_str(), buf_size);
5045
5046 return DB_SUCCESS;
5047 }
5048#endif /* LOCAL_ROUTINES */
5049
5050 return DB_SUCCESS;
5051}
5052
5053/*------------------------------------------------------------------*/
5055/********************************************************************\
5056
5057 Routine: db_get_path
5058
5059 Purpose: Get full path of a key
5060
5061 Input:
5062 HNDLE hDB Handle to the database
5063 HNDLE hKey Key handle
5064
5065 Function value: Path string
5066
5067\********************************************************************/
5068{
5069 if (rpc_is_remote()) {
5070 char path[MAX_ODB_PATH];
5071 int status = rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, sizeof(path));
5072 if (status != DB_SUCCESS) {
5073 sprintf(path, "(RPC_DB_GET_PATH status %d)", status);
5074 }
5075 return path;
5076 }
5077
5078#ifdef LOCAL_ROUTINES
5079 {
5080 DATABASE_HEADER *pheader;
5081
5082 if (hDB > _database_entries || hDB <= 0) {
5083 cm_msg(MERROR, "db_get_path", "invalid database handle");
5084 return "(DB_INVALID_HANDLE)";
5085 }
5086
5087 if (!_database[hDB - 1].attached) {
5088 cm_msg(MERROR, "db_get_path", "invalid database handle");
5089 return "(DB_INVALID_HANDLE)";
5090 }
5091
5093
5094 pheader = _database[hDB - 1].database_header;
5095
5096 std::string xpath = db_get_path_locked(pheader, hKey);
5097
5099
5100 return xpath;
5101 }
5102#endif /* LOCAL_ROUTINES */
5103
5104 return "(no LOCAL_ROUTINES)";
5105}
5106
5107#ifdef LOCAL_ROUTINES
5108/*------------------------------------------------------------------*/
5110{
5111 /* check if this key has notify count set */
5112 if (key->notify_count) {
5113
5115
5117
5118 std::string path = db_get_path_locked(pheader, hKey);
5119
5120 std::string line = msprintf("%s open %d times by", path.c_str(), key->notify_count);
5121
5122 //printf("path [%s] key.name [%s]\n", path, key->name);
5123
5124 int count = 0;
5125 for (int i = 0; i < pheader->max_client_index; i++) {
5126 DATABASE_CLIENT *pclient = &pheader->client[i];
5127 for (int j = 0; j < pclient->max_index; j++)
5128 if (pclient->open_record[j].handle == hKey) {
5129 count++;
5130 line += " \"";
5131 line += pclient->name;
5132 line += "\"";
5133 //sprintf(line + strlen(line), ", handle %d, mode %d ", pclient->open_record[j].handle, pclient->open_record[j].access_mode);
5134 }
5135 }
5136
5137 if (count < 1) {
5138 line += " a deleted client";
5139 }
5140
5141 line += "\n";
5142
5143 std::string *result = (std::string*)xresult;
5144 *result += line;
5145
5147 }
5148 return DB_SUCCESS;
5149}
5150
5152{
5153 std::string *result = (std::string*)xresult;
5154
5155 /* check if this key has notify count set */
5156 if (key->notify_count) {
5159 db_allow_write_locked(&_database[hDB - 1], "db_fix_open_records");
5160
5161 int i;
5162 for (i = 0; i < pheader->max_client_index; i++) {
5163 DATABASE_CLIENT *pclient = &pheader->client[i];
5164 int j;
5165 for (j = 0; j < pclient->max_index; j++)
5166 if (pclient->open_record[j].handle == hKey)
5167 break;
5168 if (j < pclient->max_index)
5169 break;
5170 }
5171 if (i == pheader->max_client_index) {
5172 /* check if hKey argument is correct */
5173 if (!db_validate_hkey(pheader, hKey)) {
5175 return DB_SUCCESS;
5176 }
5177
5178 /* reset notify count */
5179 KEY *pkey = (KEY *) ((char *) pheader + hKey);
5180 pkey->notify_count = 0;
5181
5182 std::string path = db_get_path_locked(pheader, hKey);
5183 *result += path;
5184 *result += " fixed\n";
5185 }
5186
5188 }
5189 return DB_SUCCESS;
5190}
5191#endif /* LOCAL_ROUTINES */
5192
5194/********************************************************************\
5195
5196 Routine: db_get_open_records
5197
5198 Purpose: Return a string with all open records
5199
5200 Input:
5201 HNDLE hDB Handle to the database
5202 HNDLE hKey Key to start search from, 0 for root
5203 INT buf_size Size of string
5204 INT fix If TRUE, fix records which are open
5205 but have no client belonging to it.
5206
5207 Output:
5208 char *str Result string. Individual records are
5209 separated with new lines.
5210
5211 Function value:
5212 DB_SUCCESS Successful completion
5213
5214\********************************************************************/
5215{
5216 str[0] = 0;
5217
5218 if (rpc_is_remote())
5219 return rpc_call(RPC_DB_GET_OPEN_RECORDS, hDB, hKey, str, buf_size);
5220
5221 std::string result;
5222
5223#ifdef LOCAL_ROUTINES
5224
5225 if (fix)
5226 db_scan_tree(hDB, hKey, 0, db_fix_open_records, &result); // FIXME: should use db_scan_tree_wlocked()
5227 else
5228 db_scan_tree(hDB, hKey, 0, db_find_open_records, &result); // FIXME: should use db_scan_tree_locked()
5229
5230#endif
5231
5232 mstrlcpy(str, result.c_str(), buf_size);
5233
5234 return DB_SUCCESS;
5235}
5236
5238#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5239
5240
5241/********************************************************************/
5266INT db_set_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data,
5267 INT data_size, INT num_values, DWORD type)
5268{
5269 if (rpc_is_remote())
5270 return rpc_call(RPC_DB_SET_VALUE, hDB, hKeyRoot, key_name, data, data_size, num_values, type);
5271
5272#ifdef LOCAL_ROUTINES
5273 {
5274 INT status;
5275
5276 if (num_values == 0)
5277 return DB_INVALID_PARAM;
5278
5280
5282
5283 db_allow_write_locked(&_database[hDB-1], "db_set_value");
5284
5285 db_err_msg* msg = NULL;
5286
5287 KEY* pkey_root = (KEY*)db_get_pkey(pheader, hKeyRoot, &status, "db_set_value", &msg);
5288
5289 if (pkey_root) {
5290 status = db_set_value_wlocked(pheader, hDB, pkey_root, key_name, data, data_size, num_values, type, &msg);
5291 }
5292
5294 if (msg)
5295 db_flush_msg(&msg);
5296
5297 return status;
5298 }
5299#endif /* LOCAL_ROUTINES */
5300
5301 return DB_SUCCESS;
5302}
5303
5304#ifdef LOCAL_ROUTINES
5305static 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)
5306{
5307 INT status;
5308
5309 if (num_values == 0)
5310 return DB_INVALID_PARAM;
5311
5312 KEY* pkey = (KEY*)db_find_pkey_locked(pheader, pkey_root, key_name, &status, msg);
5313
5314 if (!pkey) {
5315 status = db_create_key_wlocked(pheader, pkey_root, key_name, type, &pkey, msg);
5316 if (status != DB_SUCCESS && status != DB_CREATED)
5317 return status;
5318 }
5319
5320 if (data_size == 0) {
5321 db_msg(msg, MERROR, "db_set_value", "zero data size not allowed");
5322 return DB_TYPE_MISMATCH;
5323 }
5324
5325 if (type != TID_STRING && type != TID_LINK && data_size != rpc_tid_size(type) * num_values) {
5326 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);
5327 return DB_TYPE_MISMATCH;
5328 }
5329
5330 status = db_check_set_data_locked(pheader, pkey, data, data_size, num_values, type, "db_set_value", msg);
5331
5332 if (status != DB_SUCCESS)
5333 return status;
5334
5335 status = db_set_data_wlocked(pheader, pkey, data, data_size, num_values, type, "db_set_value", msg);
5336
5337 if (status != DB_SUCCESS)
5338 return status;
5339
5340 db_notify_clients_locked(pheader, hDB, db_pkey_to_hkey(pheader, pkey), -1, TRUE, msg);
5341
5342 return DB_SUCCESS;
5343}
5344#endif /* LOCAL_ROUTINES */
5345
5346/********************************************************************/
5370INT db_set_value_index(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const void *data,
5371 INT data_size, INT idx, DWORD type, BOOL trunc)
5372{
5373 int status;
5374 HNDLE hkey;
5375
5376 status = db_find_key(hDB, hKeyRoot, key_name, &hkey);
5377 if (!hkey) {
5378 status = db_create_key(hDB, hKeyRoot, key_name, type);
5379 status = db_find_key(hDB, hKeyRoot, key_name, &hkey);
5380 if (status != DB_SUCCESS)
5381 return status;
5382 } else
5383 if (trunc) {
5385 if (status != DB_SUCCESS)
5386 return status;
5387 }
5388
5389 return db_set_data_index(hDB, hkey, data, data_size, idx, type);
5390}
5391
5392/********************************************************************/
5420INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT * buf_size, DWORD type, BOOL create)
5421{
5422 if (rpc_is_remote())
5423 return rpc_call(RPC_DB_GET_VALUE, hDB, hKeyRoot, key_name, data, buf_size, type, create);
5424
5425#ifdef LOCAL_ROUTINES
5426 {
5427 INT status;
5428
5429 if (hDB > _database_entries || hDB <= 0) {
5430 cm_msg(MERROR, "db_get_value", "invalid database handle %d", hDB);
5431 return DB_INVALID_HANDLE;
5432 }
5433
5434 if (!_database[hDB - 1].attached) {
5435 cm_msg(MERROR, "db_get_value", "invalid database handle %d", hDB);
5436 return DB_INVALID_HANDLE;
5437 }
5438
5439 /* check if key name contains index */
5440 char keyname[MAX_ODB_PATH];
5441 mstrlcpy(keyname, key_name, sizeof(keyname));
5442 int idx = -1;
5443 if (strchr(keyname, '[') && strchr(keyname, ']')) {
5444 char* p;
5445 for (p = strchr(keyname, '[') + 1; *p && *p != ']'; p++)
5446 if (!isdigit(*p))
5447 break;
5448
5449 if (*p && *p == ']') {
5450 idx = atoi(strchr(keyname, '[') + 1);
5451 *strchr(keyname, '[') = 0;
5452 }
5453 }
5454
5455 /* now lock database */
5457
5459 db_err_msg* msg = NULL;
5460
5461 const KEY* pkey_root = db_get_pkey(pheader, hKeyRoot, &status, "db_get_value", &msg);
5462
5463 if (!pkey_root) {
5465 if (msg)
5466 db_flush_msg(&msg);
5467 return status;
5468 }
5469
5470 const KEY* pkey = db_find_pkey_locked(pheader, pkey_root, keyname, &status, &msg);
5471
5472 if (!pkey) {
5473 if (create) {
5474 db_allow_write_locked(&_database[hDB-1], "db_get_value");
5475 /* set default value if key was created */
5476
5477 /* get string size from data size */
5478 int size;
5479 if (type == TID_STRING || type == TID_LINK)
5480 size = *buf_size;
5481 else
5482 size = rpc_tid_size(type);
5483
5484 status = db_set_value_wlocked(pheader, hDB, (KEY*)pkey_root, keyname, data, *buf_size, *buf_size / size, type, &msg);
5486 if (msg)
5487 db_flush_msg(&msg);
5488 return status;
5489 } else {
5491 if (msg)
5492 db_flush_msg(&msg);
5493 return DB_NO_KEY;
5494 }
5495 }
5496
5497 status = db_get_data_locked(pheader, pkey, idx, data, buf_size, type, &msg);
5498
5500 if (msg)
5501 db_flush_msg(&msg);
5502
5503 return status;
5504 }
5505#endif /* LOCAL_ROUTINES */
5506
5507 return DB_SUCCESS;
5508}
5509
5510#ifdef LOCAL_ROUTINES
5511static INT db_get_data_locked(DATABASE_HEADER* pheader, const KEY* pkey, int idx, void *data, INT * buf_size, DWORD type, db_err_msg** msg)
5512{
5513 /* check for correct type */
5514 if (pkey->type != (type)) {
5515 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));
5516 return DB_TYPE_MISMATCH;
5517 }
5518
5519 /* check for correct type */
5520 if (pkey->type == TID_KEY) {
5521 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));
5522 return DB_TYPE_MISMATCH;
5523 }
5524
5525 /* check for read access */
5526 if (!(pkey->access_mode & MODE_READ)) {
5527 db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" has no read access", db_get_path_locked(pheader, pkey).c_str());
5528 return DB_NO_ACCESS;
5529 }
5530
5531 /* check if buffer is too small */
5532 if ((idx == -1 && pkey->num_values * pkey->item_size > *buf_size) || (idx != -1 && pkey->item_size > *buf_size)) {
5533 memcpy(data, (char *) pheader + pkey->data, *buf_size);
5534 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);
5535 return DB_TRUNCATED;
5536 }
5537
5538 /* check if index in boundaries */
5539 if (idx != -1 && idx >= pkey->num_values) {
5540 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);
5541 return DB_INVALID_PARAM;
5542 }
5543
5544 /* copy key data */
5545 if (idx == -1) {
5546 memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
5547 *buf_size = pkey->num_values * pkey->item_size;
5548 } else {
5549 memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, pkey->item_size);
5550 *buf_size = pkey->item_size;
5551 }
5552
5553 return DB_SUCCESS;
5554}
5555#endif /* LOCAL_ROUTINES */
5556
5557/********************************************************************/
5592{
5593 if (rpc_is_remote())
5595
5596#ifdef LOCAL_ROUTINES
5597 {
5598 DATABASE_HEADER *pheader;
5599 INT i;
5600 char str[256];
5601 HNDLE parent;
5602
5603 if (hDB > _database_entries || hDB <= 0) {
5604 cm_msg(MERROR, "db_enum_key", "invalid database handle");
5605 return DB_INVALID_HANDLE;
5606 }
5607
5608 if (!_database[hDB - 1].attached) {
5609 cm_msg(MERROR, "db_enum_key", "invalid database handle");
5610 return DB_INVALID_HANDLE;
5611 }
5612
5613 *subkey_handle = 0;
5614
5615 /* first lock database */
5617
5618 pheader = _database[hDB - 1].database_header;
5619 if (!hKey)
5620 hKey = pheader->root_key;
5621
5622 /* check if hKey argument is correct */
5623 if (!db_validate_hkey(pheader, hKey)) {
5625 return DB_INVALID_HANDLE;
5626 }
5627
5628 db_err_msg *msg = NULL;
5629 int status;
5630
5631 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_enum_key", &msg);
5632
5633 if (!pkey) {
5635 db_flush_msg(&msg);
5636 return DB_NO_MORE_SUBKEYS;
5637 }
5638
5639 if (pkey->type != TID_KEY) {
5641 return DB_NO_MORE_SUBKEYS;
5642 }
5643
5644 const KEYLIST* pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_enum_key", &msg);
5645
5646 if (!pkeylist) {
5648 db_flush_msg(&msg);
5649 return DB_NO_MORE_SUBKEYS;
5650 }
5651
5652 if (idx >= pkeylist->num_keys) {
5654 return DB_NO_MORE_SUBKEYS;
5655 }
5656
5657 pkey = db_get_pkey(pheader, pkeylist->first_key, &status, "db_enum_key", &msg);
5658
5659 if (!pkey) {
5660 std::string path = db_get_path_locked(pheader, hKey);
5661 HNDLE xfirst_key = pkeylist->first_key;
5663 if (msg)
5664 db_flush_msg(&msg);
5665 cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" invalid first_key %d", hKey, path.c_str(), xfirst_key);
5666 return DB_NO_MORE_SUBKEYS;
5667 }
5668
5669 for (i = 0; i < idx; i++) {
5670 if (pkey->next_key == 0) {
5671 std::string path = db_get_path_locked(pheader, hKey);
5673 cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" unexpected end of key list at index %d", hKey, path.c_str(), i);
5674 return DB_NO_MORE_SUBKEYS;
5675 }
5676
5677 pkey = db_get_pkey(pheader, pkey->next_key, &status, "db_enum_key", &msg);
5678
5679 if (!pkey) {
5680 std::string path = db_get_path_locked(pheader, hKey);
5682 db_flush_msg(&msg);
5683 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);
5684 return DB_NO_MORE_SUBKEYS;
5685 }
5686 }
5687
5688 /* resolve links */
5689 if (pkey->type == TID_LINK) {
5690 strcpy(str, (char *) pheader + pkey->data);
5691
5692 /* no not resolve if link to array index */
5693 if (strlen(str) > 0 && str[strlen(str) - 1] == ']') {
5694 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5696 return DB_SUCCESS;
5697 }
5698
5699 if (*str == '/') {
5700 /* absolute path */
5702 return db_find_key(hDB, 0, str, subkey_handle);
5703 } else {
5704 /* relative path */
5705 if (pkey->parent_keylist) {
5706 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
5707 parent = pkeylist->parent;
5709 return db_find_key(hDB, parent, str, subkey_handle);
5710 } else {
5712 return db_find_key(hDB, 0, str, subkey_handle);
5713 }
5714 }
5715 }
5716
5717 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5719 }
5720#endif /* LOCAL_ROUTINES */
5721
5722 return DB_SUCCESS;
5723}
5724
5726#ifndef DOXYGEN_SHOULD_SKIP_THIS
5727
5728
5729/*------------------------------------------------------------------*/
5731/********************************************************************\
5732
5733 Routine: db_enum_link
5734
5735 Purpose: Enumerate subkeys from a key, don't follow links
5736
5737 Input:
5738 HNDLE hDB Handle to the database
5739 HNDLE hKey Handle of key to enumerate, zero for the
5740 root key
5741 INT idx Subkey index, sould be initially 0, then
5742 incremented in each call until
5743 *subhKey becomes zero and the function
5744 returns DB_NO_MORE_SUBKEYS
5745
5746 Output:
5747 HNDLE *subkey_handle Handle of subkey which can be used in
5748 db_get_key and db_get_data
5749
5750 Function value:
5751 DB_SUCCESS Successful completion
5752 DB_INVALID_HANDLE Database handle is invalid
5753 DB_NO_MORE_SUBKEYS Last subkey reached
5754
5755\********************************************************************/
5756{
5757 if (rpc_is_remote())
5759
5760#ifdef LOCAL_ROUTINES
5761 {
5762 DATABASE_HEADER *pheader;
5764 KEY *pkey;
5765 INT i;
5766
5767 if (hDB > _database_entries || hDB <= 0) {
5768 cm_msg(MERROR, "db_enum_link", "invalid database handle");
5769 return DB_INVALID_HANDLE;
5770 }
5771
5772 if (!_database[hDB - 1].attached) {
5773 cm_msg(MERROR, "db_enum_link", "invalid database handle");
5774 return DB_INVALID_HANDLE;
5775 }
5776
5777 *subkey_handle = 0;
5778
5779 /* first lock database */
5781
5782 pheader = _database[hDB - 1].database_header;
5783 if (!hKey)
5784 hKey = pheader->root_key;
5785
5786 /* check if hKey argument is correct */
5787 if (!db_validate_hkey(pheader, hKey)) {
5789 return DB_INVALID_HANDLE;
5790 }
5791
5792 pkey = (KEY *) ((char *) pheader + hKey);
5793
5794 if (pkey->type != TID_KEY) {
5796 return DB_NO_MORE_SUBKEYS;
5797 }
5798 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
5799
5800 if (idx >= pkeylist->num_keys) {
5802 return DB_NO_MORE_SUBKEYS;
5803 }
5804
5805 // FIXME: validate pkeylist->first_key
5806 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
5807 for (i = 0; i < idx; i++) {
5808 // FIXME: validate pkey->next_key
5809 pkey = (KEY *) ((char *) pheader + pkey->next_key);
5810 }
5811
5812 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5814 }
5815#endif /* LOCAL_ROUTINES */
5816
5817 return DB_SUCCESS;
5818}
5819
5820/*------------------------------------------------------------------*/
5822/********************************************************************\
5823
5824 Routine: db_get_next_link
5825
5826 Purpose: Get next key in ODB after hKey
5827
5828 Input:
5829 HNDLE hDB Handle to the database
5830 HNDLE hKey Handle of key to enumerate, zero for the
5831 root key
5832
5833 Output:
5834 HNDLE *subkey_handle Handle of subkey which can be used in
5835 db_get_key and db_get_data
5836
5837 Function value:
5838 DB_SUCCESS Successful completion
5839 DB_INVALID_HANDLE Database handle is invalid
5840 DB_NO_MORE_SUBKEYS Last subkey reached
5841
5842\********************************************************************/
5843{
5844 if (rpc_is_remote())
5846
5847#ifdef LOCAL_ROUTINES
5848 {
5849 DATABASE_HEADER *pheader;
5851 KEY *pkey;
5852 INT descent;
5853
5854 if (hDB > _database_entries || hDB <= 0) {
5855 cm_msg(MERROR, "db_enum_link", "invalid database handle");
5856 return DB_INVALID_HANDLE;
5857 }
5858
5859 if (!_database[hDB - 1].attached) {
5860 cm_msg(MERROR, "db_enum_link", "invalid database handle");
5861 return DB_INVALID_HANDLE;
5862 }
5863
5864 *subkey_handle = 0;
5865
5866 /* first lock database */
5868
5869 pheader = _database[hDB - 1].database_header;
5870 if (!hKey)
5871 hKey = pheader->root_key;
5872
5873 /* check if hKey argument is correct */
5874 if (!db_validate_hkey(pheader, hKey)) {
5876 return DB_INVALID_HANDLE;
5877 }
5878
5879 pkey = (KEY *) ((char *) pheader + hKey);
5880
5881 descent = TRUE;
5882 do {
5883 if (pkey->type != TID_KEY || !descent) {
5884 if (pkey->next_key) {
5885 /* key has next key, return it */
5886 // FIXME: validate pkey->next_key
5887 pkey = (KEY *) ((char *) pheader + pkey->next_key);
5888
5889 if (pkey->type != TID_KEY) {
5890 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5892 return DB_SUCCESS;
5893 }
5894
5895 /* key has subkeys, so descent on the next iteration */
5896 descent = TRUE;
5897 } else {
5898 if (pkey->parent_keylist == 0) {
5899 /* return if we are back to the root key */
5901 return DB_NO_MORE_SUBKEYS;
5902 }
5903
5904 /* key is last in list, traverse up */
5905 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
5906
5907 // FIXME: validate pkeylist->parent
5908 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
5909 descent = FALSE;
5910 }
5911 } else {
5912 if (descent) {
5913 /* find first subkey */
5914 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
5915
5916 if (pkeylist->num_keys == 0) {
5917 /* if key has no subkeys, look for next key on this level */
5918 descent = FALSE;
5919 } else {
5920 /* get first subkey */
5921 // FIXME: validate pkeylist->first_key
5922 pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
5923
5924 if (pkey->type != TID_KEY) {
5925 *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5927 return DB_SUCCESS;
5928 }
5929 }
5930 }
5931 }
5932
5933 } while (TRUE);
5934 }
5935#endif /* LOCAL_ROUTINES */
5936
5937 return DB_SUCCESS;
5938}
5939
5941#endif /* DOXYGEN_SHOULD_SKIP_THIS */
5942
5943/********************************************************************/
5944
5945#ifdef LOCAL_ROUTINES
5946
5948{
5949 if (!hKey)
5950 hKey = pheader->root_key;
5951
5952 /* check if hKey argument is correct */
5953 if (!db_validate_hkey(pheader, hKey)) {
5954 return DB_INVALID_HANDLE;
5955 }
5956
5957 const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
5958
5959 if (pkey->type < 1 || pkey->type >= TID_LAST) {
5960 int pkey_type = pkey->type;
5961 db_msg(msg, MERROR, "db_get_key", "hkey %d invalid key type %d", hKey, pkey_type);
5962 return DB_INVALID_HANDLE;
5963 }
5964
5965 /* check for link to array index */
5966 if (pkey->type == TID_LINK) {
5967 char link_name[MAX_ODB_PATH];
5968 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
5969 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
5970 if (strchr(link_name, '[') == NULL)
5971 return DB_INVALID_LINK;
5972
5974 if (db_find_key_locked(pheader, 0, link_name, &hkeylink, msg) != DB_SUCCESS)
5975 return DB_INVALID_LINK;
5976 int status = db_get_key_locked(pheader, hkeylink, key, msg);
5977 key->num_values = 1; // fake number of values
5978 return status;
5979 }
5980 }
5981
5982 memcpy(key, pkey, sizeof(KEY));
5983
5984 return DB_SUCCESS;
5985}
5986#endif /* LOCAL_ROUTINES */
5987
5988/********************************************************************/
6025{
6026 if (rpc_is_remote())
6027 return rpc_call(RPC_DB_GET_KEY, hDB, hKey, key);
6028
6029#ifdef LOCAL_ROUTINES
6030 {
6031 DATABASE_HEADER *pheader;
6032 int status;
6033
6034 if (hDB > _database_entries || hDB <= 0) {
6035 cm_msg(MERROR, "db_get_key", "invalid database handle");
6036 return DB_INVALID_HANDLE;
6037 }
6038
6039 if (!_database[hDB - 1].attached) {
6040 cm_msg(MERROR, "db_get_key", "invalid database handle");
6041 return DB_INVALID_HANDLE;
6042 }
6043
6044 if (hKey < (int) sizeof(DATABASE_HEADER) && hKey != 0) {
6045 cm_msg(MERROR, "db_get_key", "invalid key handle");
6046 return DB_INVALID_HANDLE;
6047 }
6048
6049 db_err_msg *msg = NULL;
6050
6052
6053 pheader = _database[hDB - 1].database_header;
6054
6055 status = db_get_key_locked(pheader, hKey, key, &msg);
6056
6058
6059 if (msg)
6060 db_flush_msg(&msg);
6061
6062 return status;
6063 }
6064#endif /* LOCAL_ROUTINES */
6065
6066 return DB_SUCCESS;
6067}
6068
6069/********************************************************************/
6078{
6079 if (rpc_is_remote())
6080 return rpc_call(RPC_DB_GET_LINK, hDB, hKey, key);
6081
6082#ifdef LOCAL_ROUTINES
6083 {
6084 DATABASE_HEADER *pheader;
6085
6086 if (hDB > _database_entries || hDB <= 0) {
6087 cm_msg(MERROR, "db_get_link", "invalid database handle");
6088 return DB_INVALID_HANDLE;
6089 }
6090
6091 if (!_database[hDB - 1].attached) {
6092 cm_msg(MERROR, "db_get_link", "invalid database handle");
6093 return DB_INVALID_HANDLE;
6094 }
6095
6096 if (hKey < (int) sizeof(DATABASE_HEADER) && hKey != 0) {
6097 cm_msg(MERROR, "db_get_link", "invalid key handle");
6098 return DB_INVALID_HANDLE;
6099 }
6100
6101 db_err_msg *msg = NULL;
6102
6104
6105 pheader = _database[hDB - 1].database_header;
6106
6107 int status = DB_SUCCESS;
6108
6109 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_get_link", &msg);
6110 if (pkey) {
6111 memcpy(key, pkey, sizeof(KEY));
6112 } else {
6113 memset(key, 0, sizeof(KEY));
6114 //abort();
6115 }
6116
6118
6119 if (msg)
6120 db_flush_msg(&msg);
6121
6122 return status;
6123 }
6124#endif /* LOCAL_ROUTINES */
6125
6126 return DB_SUCCESS;
6127}
6128
6129/********************************************************************/
6138{
6139 if (rpc_is_remote())
6141
6142#ifdef LOCAL_ROUTINES
6143 {
6144 DATABASE_HEADER *pheader;
6145 KEY *pkey;
6146
6147 if (hDB > _database_entries || hDB <= 0) {
6148 cm_msg(MERROR, "db_get_key", "invalid database handle");
6149 return DB_INVALID_HANDLE;
6150 }
6151
6152 if (!_database[hDB - 1].attached) {
6153 cm_msg(MERROR, "db_get_key", "invalid database handle");
6154 return DB_INVALID_HANDLE;
6155 }
6156
6157 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6158 cm_msg(MERROR, "db_get_key", "invalid key handle");
6159 return DB_INVALID_HANDLE;
6160 }
6161
6163
6164 pheader = _database[hDB - 1].database_header;
6165
6166 /* check if hKey argument is correct */
6167 if (!db_validate_hkey(pheader, hKey)) {
6169 return DB_INVALID_HANDLE;
6170 }
6171
6172 pkey = (KEY *) ((char *) pheader + hKey);
6173
6174 *delta = ss_time() - pkey->last_written;
6175
6177
6178 }
6179#endif /* LOCAL_ROUTINES */
6180
6181 return DB_SUCCESS;
6182}
6183
6184/********************************************************************/
6196INT db_get_key_info(HNDLE hDB, HNDLE hKey, char *name, INT name_size, INT * type, INT * num_values, INT * item_size)
6197{
6198 if (rpc_is_remote())
6199 return rpc_call(RPC_DB_GET_KEY_INFO, hDB, hKey, name, name_size, type, num_values, item_size);
6200
6201#ifdef LOCAL_ROUTINES
6202 {
6203 DATABASE_HEADER *pheader;
6204 KEY *pkey;
6206
6207 if (hDB > _database_entries || hDB <= 0) {
6208 cm_msg(MERROR, "db_get_key_info", "invalid database handle");
6209 return DB_INVALID_HANDLE;
6210 }
6211
6212 if (!_database[hDB - 1].attached) {
6213 cm_msg(MERROR, "db_get_key_info", "invalid database handle");
6214 return DB_INVALID_HANDLE;
6215 }
6216
6217 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6218 cm_msg(MERROR, "db_get_key_info", "invalid key handle");
6219 return DB_INVALID_HANDLE;
6220 }
6221
6223
6224 pheader = _database[hDB - 1].database_header;
6225
6226 /* check if hKey argument is correct */
6227 if (!db_validate_hkey(pheader, hKey)) {
6229 return DB_INVALID_HANDLE;
6230 }
6231
6232 pkey = (KEY *) ((char *) pheader + hKey);
6233
6234 if ((INT) strlen(pkey->name) + 1 > name_size) {
6235 /* truncate name */
6236 memcpy(name, pkey->name, name_size - 1);
6237 name[name_size] = 0;
6238 } else
6239 strcpy(name, pkey->name);
6240
6241 /* convert "root" to "/" */
6242 if (strcmp(name, "root") == 0)
6243 strcpy(name, "/");
6244
6245 *type = pkey->type;
6246 *num_values = pkey->num_values;
6247 *item_size = pkey->item_size;
6248
6249 if (pkey->type == TID_KEY) {
6250 pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
6251 *num_values = pkeylist->num_keys;
6252 }
6253
6255 }
6256#endif /* LOCAL_ROUTINES */
6257
6258 return DB_SUCCESS;
6259}
6260
6262#ifndef DOXYGEN_SHOULD_SKIP_THIS
6263
6264
6265/*------------------------------------------------------------------*/
6267/********************************************************************\
6268
6269 Routine: db_get_key
6270
6271 Purpose: Rename a key
6272
6273 Input:
6274 HNDLE hDB Handle to the database
6275 HNDLE hKey Handle of key
6276 char *name New key name
6277
6278 Output:
6279 <none>
6280
6281 Function value:
6282 DB_SUCCESS Successful completion
6283 DB_INVALID_HANDLE Database handle is invalid
6284 DB_INVALID_NAME Key name contains '/'
6285
6286\********************************************************************/
6287{
6288 if (rpc_is_remote())
6290
6291#ifdef LOCAL_ROUTINES
6292 {
6293 DATABASE_HEADER *pheader;
6294 KEY *pkey;
6295 int status;
6296
6297 if (hDB > _database_entries || hDB <= 0) {
6298 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6299 return DB_INVALID_HANDLE;
6300 }
6301
6302 if (!_database[hDB - 1].attached) {
6303 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6304 return DB_INVALID_HANDLE;
6305 }
6306
6307 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6308 cm_msg(MERROR, "db_rename_key", "invalid key handle");
6309 return DB_INVALID_HANDLE;
6310 }
6311
6312 db_err_msg* msg = NULL;
6313 status = db_validate_name(name, FALSE, "db_rename_key", &msg);
6314 if (msg)
6315 db_flush_msg(&msg);
6316 if (status != DB_SUCCESS)
6317 return status;
6318
6319 if (name == NULL) {
6320 cm_msg(MERROR, "db_rename_key", "key name is NULL");
6321 return DB_INVALID_NAME;
6322 }
6323
6324 if (strlen(name) < 1) {
6325 cm_msg(MERROR, "db_rename_key", "key name is too short");
6326 return DB_INVALID_NAME;
6327 }
6328
6329 if (strchr(name, '/')) {
6330 cm_msg(MERROR, "db_rename_key", "key name may not contain \"/\"");
6331 return DB_INVALID_NAME;
6332 }
6333
6335
6336 pheader = _database[hDB - 1].database_header;
6337
6338 /* check if hKey argument is correct */
6339 if (!db_validate_hkey(pheader, hKey)) {
6341 return DB_INVALID_HANDLE;
6342 }
6343
6344 pkey = (KEY *) ((char *) pheader + hKey);
6345
6346 if (!pkey->type) {
6347 int pkey_type = pkey->type;
6349 cm_msg(MERROR, "db_rename_key", "hkey %d invalid key type %d", hKey, pkey_type);
6350 return DB_INVALID_HANDLE;
6351 }
6352
6353 db_allow_write_locked(&_database[hDB - 1], "db_rename_key");
6354
6355 mstrlcpy(pkey->name, name, NAME_LENGTH);
6356
6358
6359 }
6360#endif /* LOCAL_ROUTINES */
6361
6362 return DB_SUCCESS;
6363}
6364
6365/*------------------------------------------------------------------*/
6367/********************************************************************\
6368
6369 Routine: db_reorder_key
6370
6371 Purpose: Reorder key so that key hKey apprears at position 'index'
6372 in keylist (or at bottom if index<0)
6373
6374 Input:
6375 HNDLE hDB Handle to the database
6376 HNDLE hKey Handle of key
6377 INT idx New positio of key in keylist
6378
6379 Output:
6380 <none>
6381
6382 Function value:
6383 DB_SUCCESS Successful completion
6384 DB_INVALID_HANDLE Database handle is invalid
6385 DB_NO_ACCESS Key is locked for write
6386 DB_OPEN_RECORD Key, subkey or parent key is open
6387
6388\********************************************************************/
6389{
6390 if (rpc_is_remote())
6392
6393#ifdef LOCAL_ROUTINES
6394 {
6395 DATABASE_HEADER *pheader;
6398 INT i;
6399
6400 if (hDB > _database_entries || hDB <= 0) {
6401 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6402 return DB_INVALID_HANDLE;
6403 }
6404
6405 if (!_database[hDB - 1].attached) {
6406 cm_msg(MERROR, "db_rename_key", "invalid database handle");
6407 return DB_INVALID_HANDLE;
6408 }
6409
6410 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6411 cm_msg(MERROR, "db_rename_key", "invalid key handle");
6412 return DB_INVALID_HANDLE;
6413 }
6414
6416
6417 pheader = _database[hDB - 1].database_header;
6418
6419 /* check if hKey argument is correct */
6420 if (!db_validate_hkey(pheader, hKey)) {
6422 return DB_INVALID_HANDLE;
6423 }
6424
6425 pkey = (KEY *) ((char *) pheader + hKey);
6426
6427 if (!pkey->type) {
6428 int pkey_type = pkey->type;
6430 cm_msg(MERROR, "db_reorder_key", "hkey %d invalid key type %d", hKey, pkey_type);
6431 return DB_INVALID_HANDLE;
6432 }
6433
6434 if (!(pkey->access_mode & MODE_WRITE)) {
6436 return DB_NO_ACCESS;
6437 }
6438
6439 /* check if someone has opened key or parent */
6440 do {
6441#ifdef CHECK_OPEN_RECORD
6442 if (pkey->notify_count) {
6444 return DB_OPEN_RECORD;
6445 }
6446#endif
6447 if (pkey->parent_keylist == 0)
6448 break;
6449
6450 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
6451 // FIXME: validate pkeylist->parent
6452 pkey = (KEY *) ((char *) pheader + pkeylist->parent);
6453 } while (TRUE);
6454
6455 db_allow_write_locked(&_database[hDB - 1], "db_reorder_key");
6456
6457 pkey = (KEY *) ((char *) pheader + hKey); // NB: hKey is already validated
6458 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
6459
6460 /* first remove key from list */
6461 pnext_key = (KEY *) (POINTER_T) pkey->next_key; // FIXME: what is this pointer cast?
6462
6463 if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
6464 /* key is first in list */
6465 pkeylist->first_key = (POINTER_T) pnext_key;
6466 } else {
6467 /* find predecessor */
6468 // FIXME: validate pkeylist->first_key
6469 pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
6470 while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey) {
6471 // FIXME: validate pkey_tmp->next_key
6472 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
6473 }
6474 pkey_tmp->next_key = (POINTER_T) pnext_key;
6475 }
6476
6477 /* add key to list at proper index */
6478 // FIXME: validate pkeylist->first_key
6479 pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
6480 if (idx < 0 || idx >= pkeylist->num_keys - 1) {
6481 /* add at bottom */
6482
6483 /* find last key */
6484 for (i = 0; i < pkeylist->num_keys - 2; i++) {
6485 // FIXME: validate pkey_tmp->next_key
6486 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
6487 }
6488
6489 pkey_tmp->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
6490 pkey->next_key = 0;
6491 } else {
6492 if (idx == 0) {
6493 /* add at top */
6494 pkey->next_key = pkeylist->first_key;
6495 pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
6496 } else {
6497 /* add at position index */
6498 for (i = 0; i < idx - 1; i++) {
6499 // FIXME: validate pkey_tmp->next_key
6500 pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
6501 }
6502
6503 pkey->next_key = pkey_tmp->next_key;
6504 pkey_tmp->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
6505 }
6506 }
6507
6509
6510 }
6511#endif /* LOCAL_ROUTINES */
6512
6513 return DB_SUCCESS;
6514}
6515
6517#endif /* DOXYGEN_SHOULD_SKIP_THIS */
6518
6519
6520/********************************************************************/
6545{
6546 if (rpc_is_remote())
6547 return rpc_call(RPC_DB_GET_DATA, hDB, hKey, data, buf_size, type);
6548
6549#ifdef LOCAL_ROUTINES
6550 {
6551 int status;
6552
6553 if (hDB > _database_entries || hDB <= 0) {
6554 cm_msg(MERROR, "db_get_data", "Invalid database handle");
6555 return DB_INVALID_HANDLE;
6556 }
6557
6558 if (!_database[hDB - 1].attached) {
6559 cm_msg(MERROR, "db_get_data", "invalid database handle");
6560 return DB_INVALID_HANDLE;
6561 }
6562
6563 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6564 cm_msg(MERROR, "db_get_data", "invalid key handle");
6565 return DB_INVALID_HANDLE;
6566 }
6567
6569
6571 db_err_msg* msg = NULL;
6572
6573 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_get_data", &msg);
6574
6575 if (!pkey) {
6577 if (msg)
6578 db_flush_msg(&msg);
6579 return status;
6580 }
6581
6582 /* check for read access */
6583 if (!(pkey->access_mode & MODE_READ)) {
6585 if (msg)
6586 db_flush_msg(&msg);
6587 return DB_NO_ACCESS;
6588 }
6589
6590 /* follow links to array index */
6591 if (pkey->type == TID_LINK) {
6592 std::string link_name = (char *) pheader + pkey->data;
6593 if (link_name.length() > 0 && link_name.back() == ']') {
6594 size_t pos = link_name.rfind("[");
6595 if (pos == std::string::npos) {
6596 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());
6598 if (msg)
6599 db_flush_msg(&msg);
6600 return DB_INVALID_LINK;
6601 }
6602 int idx = atoi(link_name.c_str()+pos+1);
6603 link_name.resize(pos);
6604 //printf("link name [%s] idx %d\n", link_name.c_str(), idx);
6605
6606 // relative symlinks did not work in the old db_get_data(), make sure they do not work now. K.O.
6607 if (link_name[0] != '/') {
6608 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());
6610 if (msg)
6611 db_flush_msg(&msg);
6612 return DB_INVALID_LINK;
6613 }
6614
6615 const KEY* pkey = db_find_pkey_locked(pheader, NULL, link_name.c_str(), &status, &msg);
6616
6617 if (!pkey) {
6619 if (msg)
6620 db_flush_msg(&msg);
6621 return status;
6622 }
6623
6624 //printf("db_get_data [%s] type [%s] idx %d\n", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(type), idx);
6625
6626 status = db_get_data_locked(pheader, pkey, idx, data, buf_size, type, &msg);
6627
6629 if (msg)
6630 db_flush_msg(&msg);
6631 return status;
6632 }
6633 }
6634
6635 //printf("db_get_data [%s] type [%s]\n", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(type));
6636
6637 status = db_get_data_locked(pheader, pkey, -1, data, buf_size, type, &msg);
6638
6640 if (msg)
6641 db_flush_msg(&msg);
6642
6643 return status;
6644 }
6645#endif /* LOCAL_ROUTINES */
6646
6647 return DB_SUCCESS;
6648}
6649
6650/********************************************************************/
6662{
6663 if (rpc_is_remote())
6664 return rpc_call(RPC_DB_GET_LINK_DATA, hDB, hKey, data, buf_size, type);
6665
6666#ifdef LOCAL_ROUTINES
6667 {
6668 DATABASE_HEADER *pheader;
6669 KEY *pkey;
6670
6671 if (hDB > _database_entries || hDB <= 0) {
6672 cm_msg(MERROR, "db_get_data", "Invalid database handle");
6673 return DB_INVALID_HANDLE;
6674 }
6675
6676 if (!_database[hDB - 1].attached) {
6677 cm_msg(MERROR, "db_get_data", "invalid database handle");
6678 return DB_INVALID_HANDLE;
6679 }
6680
6681 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6682 cm_msg(MERROR, "db_get_data", "invalid key handle");
6683 return DB_INVALID_HANDLE;
6684 }
6685
6687
6688 pheader = _database[hDB - 1].database_header;
6689
6690 /* check if hKey argument is correct */
6691 if (!db_validate_hkey(pheader, hKey)) {
6693 return DB_INVALID_HANDLE;
6694 }
6695
6696 pkey = (KEY *) ((char *) pheader + hKey);
6697
6698 /* check for read access */
6699 if (!(pkey->access_mode & MODE_READ)) {
6701 return DB_NO_ACCESS;
6702 }
6703
6704 if (!pkey->type) {
6705 int pkey_type = pkey->type;
6707 cm_msg(MERROR, "db_get_data", "hkey %d invalid key type %d", hKey, pkey_type);
6708 return DB_INVALID_HANDLE;
6709 }
6710
6711 if (pkey->type != type) {
6712 int pkey_type = pkey->type;
6713 char pkey_name[NAME_LENGTH];
6714 mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
6716 cm_msg(MERROR, "db_get_data", "\"%s\" is of type %s, not %s", pkey_name, rpc_tid_name(pkey_type), rpc_tid_name(type));
6717 return DB_TYPE_MISMATCH;
6718 }
6719
6720 /* keys cannot contain data */
6721 if (pkey->type == TID_KEY) {
6723 cm_msg(MERROR, "db_get_data", "Key cannot contain data");
6724 return DB_TYPE_MISMATCH;
6725 }
6726
6727 /* check if key has data */
6728 if (pkey->data == 0) {
6729 memset(data, 0, *buf_size);
6730 *buf_size = 0;
6732 return DB_SUCCESS;
6733 }
6734
6735 /* check if buffer is too small */
6736 if (pkey->num_values * pkey->item_size > *buf_size) {
6737 int pkey_size = pkey->num_values * pkey->item_size;
6738 memcpy(data, (char *) pheader + pkey->data, *buf_size);
6740 std::string path = db_get_path(hDB, hKey);
6741 cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
6742 return DB_TRUNCATED;
6743 }
6744
6745 /* copy key data */
6746 memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
6747 *buf_size = pkey->num_values * pkey->item_size;
6748
6750
6751 }
6752#endif /* LOCAL_ROUTINES */
6753
6754 return DB_SUCCESS;
6755}
6756
6758#ifndef DOXYGEN_SHOULD_SKIP_THIS
6759
6760/*------------------------------------------------------------------*/
6761INT db_get_data1(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, DWORD type, INT * num_values)
6762/********************************************************************\
6763
6764 Routine: db_get_data1
6765
6766 Purpose: Get key data from a handle, return number of values
6767
6768 Input:
6769 HNDLE hDB Handle to the database
6770 HNDLE hKey Handle of key
6771 INT *buf_size Size of data buffer
6772 DWORD type Type of data
6773
6774 Output:
6775 void *data Key data
6776 INT *buf_size Size of key data
6777 INT *num_values Number of values
6778
6779 Function value:
6780 DB_SUCCESS Successful completion
6781 DB_INVALID_HANDLE Database handle is invalid
6782 DB_TRUNCATED Return buffer is smaller than key data
6783 DB_TYPE_MISMATCH Type mismatch
6784
6785\********************************************************************/
6786{
6787 if (rpc_is_remote())
6788 return rpc_call(RPC_DB_GET_DATA1, hDB, hKey, data, buf_size, type, num_values);
6789
6790#ifdef LOCAL_ROUTINES
6791 {
6792 DATABASE_HEADER *pheader;
6793 KEY *pkey;
6794
6795 if (hDB > _database_entries || hDB <= 0) {
6796 cm_msg(MERROR, "db_get_data", "Invalid database handle");
6797 return DB_INVALID_HANDLE;
6798 }
6799
6800 if (!_database[hDB - 1].attached) {
6801 cm_msg(MERROR, "db_get_data", "invalid database handle");
6802 return DB_INVALID_HANDLE;
6803 }
6804
6805 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6806 cm_msg(MERROR, "db_get_data", "invalid key handle");
6807 return DB_INVALID_HANDLE;
6808 }
6809
6811
6812 pheader = _database[hDB - 1].database_header;
6813
6814 /* check if hKey argument is correct */
6815 if (!db_validate_hkey(pheader, hKey)) {
6817 return DB_INVALID_HANDLE;
6818 }
6819
6820 pkey = (KEY *) ((char *) pheader + hKey);
6821
6822 /* check for read access */
6823 if (!(pkey->access_mode & MODE_READ)) {
6825 return DB_NO_ACCESS;
6826 }
6827
6828 if (!pkey->type) {
6829 int pkey_type = pkey->type;
6831 cm_msg(MERROR, "db_get_data", "hkey %d invalid key type %d", hKey, pkey_type);
6832 return DB_INVALID_HANDLE;
6833 }
6834
6835 if (pkey->type != type) {
6836 int pkey_type = pkey->type;
6837 char pkey_name[NAME_LENGTH];
6838 mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
6840 cm_msg(MERROR, "db_get_data", "\"%s\" is of type %s, not %s", pkey_name, rpc_tid_name(pkey_type), rpc_tid_name(type));
6841 return DB_TYPE_MISMATCH;
6842 }
6843
6844 /* keys cannot contain data */
6845 if (pkey->type == TID_KEY) {
6847 cm_msg(MERROR, "db_get_data", "Key cannot contain data");
6848 return DB_TYPE_MISMATCH;
6849 }
6850
6851 /* check if key has data */
6852 if (pkey->data == 0) {
6853 memset(data, 0, *buf_size);
6854 *buf_size = 0;
6856 return DB_SUCCESS;
6857 }
6858
6859 /* check if buffer is too small */
6860 if (pkey->num_values * pkey->item_size > *buf_size) {
6861 int pkey_size = pkey->num_values * pkey->item_size;
6862 memcpy(data, (char *) pheader + pkey->data, *buf_size);
6864 std::string path = db_get_path(hDB, hKey);
6865 cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
6866 return DB_TRUNCATED;
6867 }
6868
6869 /* copy key data */
6870 memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
6871 *buf_size = pkey->num_values * pkey->item_size;
6872 *num_values = pkey->num_values;
6873
6875
6876 }
6877#endif /* LOCAL_ROUTINES */
6878
6879 return DB_SUCCESS;
6880}
6881
6883#endif /* DOXYGEN_SHOULD_SKIP_THIS */
6884
6885/********************************************************************/
6899{
6900 if (rpc_is_remote())
6901 return rpc_call(RPC_DB_GET_DATA_INDEX, hDB, hKey, data, buf_size, idx, type);
6902
6903#ifdef LOCAL_ROUTINES
6904 {
6905 DATABASE_HEADER *pheader;
6906 KEY *pkey;
6907
6908 if (hDB > _database_entries || hDB <= 0) {
6909 cm_msg(MERROR, "db_get_data", "Invalid database handle");
6910 return DB_INVALID_HANDLE;
6911 }
6912
6913 if (!_database[hDB - 1].attached) {
6914 cm_msg(MERROR, "db_get_data", "invalid database handle");
6915 return DB_INVALID_HANDLE;
6916 }
6917
6918 if (hKey < (int) sizeof(DATABASE_HEADER)) {
6919 cm_msg(MERROR, "db_get_data", "invalid key handle");
6920 return DB_INVALID_HANDLE;
6921 }
6922
6924
6925 pheader = _database[hDB - 1].database_header;
6926
6927 /* check if hKey argument is correct */
6928 if (!db_validate_hkey(pheader, hKey)) {
6930 return DB_INVALID_HANDLE;
6931 }
6932
6933 pkey = (KEY *) ((char *) pheader + hKey);
6934
6935 /* check for read access */
6936 if (!(pkey->access_mode & MODE_READ)) {
6938 return DB_NO_ACCESS;
6939 }
6940
6941 if (!pkey->type) {
6942 int pkey_type = pkey->type;
6944 cm_msg(MERROR, "db_get_data_index", "hkey %d invalid key type %d", hKey, pkey_type);
6945 return DB_INVALID_HANDLE;
6946 }
6947
6948 if (pkey->type != type) {
6949 int pkey_type = pkey->type;
6950 char pkey_name[NAME_LENGTH];
6951 mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
6953 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));
6954 return DB_TYPE_MISMATCH;
6955 }
6956
6957 /* keys cannot contain data */
6958 if (pkey->type == TID_KEY) {
6960 cm_msg(MERROR, "db_get_data_index", "Key cannot contain data");
6961 return DB_TYPE_MISMATCH;
6962 }
6963
6964 /* check if key has data */
6965 if (pkey->data == 0) {
6966 memset(data, 0, *buf_size);
6967 *buf_size = 0;
6969 return DB_SUCCESS;
6970 }
6971
6972 /* check if index in range */
6973 if (idx < 0 || idx >= pkey->num_values) {
6974 int pkey_num_values = pkey->num_values;
6975 memset(data, 0, *buf_size);
6977
6978 std::string path = db_get_path(hDB, hKey);
6979 cm_msg(MERROR, "db_get_data_index", "index (%d) exceeds array length (%d) for key \"%s\"", idx, pkey_num_values, path.c_str());
6980 return DB_OUT_OF_RANGE;
6981 }
6982
6983 /* check if buffer is too small */
6984 if (pkey->item_size > *buf_size) {
6985 int pkey_size = pkey->item_size;
6986 /* copy data */
6987 memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, *buf_size);
6989 std::string path = db_get_path(hDB, hKey);
6990 cm_msg(MERROR, "db_get_data_index", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
6991 return DB_TRUNCATED;
6992 }
6993
6994 /* copy key data */
6995 memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, pkey->item_size);
6996 *buf_size = pkey->item_size;
6997
6999
7000 }
7001#endif /* LOCAL_ROUTINES */
7002
7003 return DB_SUCCESS;
7004}
7005
7006#ifdef LOCAL_ROUTINES
7007/********************************************************************/
7019static 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)
7020{
7021 /* if no buf_size given (Java!), calculate it */
7022 if (data_size == 0)
7023 data_size = pkey->item_size * num_values;
7024
7025 /* resize data size if necessary */
7026 if (pkey->total_size != data_size) {
7027 // FIXME: validate pkey->data!
7028 pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, data_size, caller);
7029
7030 if (pkey->data == 0) {
7031 pkey->total_size = 0;
7032 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);
7033 return DB_FULL;
7034 }
7035
7036 pkey->data -= (POINTER_T) pheader;
7037 pkey->total_size = data_size;
7038 }
7039
7040 /* set number of values */
7041 pkey->num_values = num_values;
7042
7043 if (type == TID_STRING || type == TID_LINK)
7044 pkey->item_size = data_size / num_values;
7045 else
7046 pkey->item_size = rpc_tid_size(type);
7047
7048 if ((type == TID_STRING || type == TID_LINK) && pkey->num_values == 1) {
7049 /* copy string up to NUL termination */
7050 mstrlcpy((char *) pheader + pkey->data, (const char*)data, data_size);
7051 } else {
7052 /* copy data */
7053 memcpy((char *) pheader + pkey->data, data, data_size);
7054 }
7055
7056 /* update time */
7057 pkey->last_written = ss_time();
7058
7059 return DB_SUCCESS;
7060}
7061
7062static 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)
7063{
7064 /* increase key size if necessary */
7065 if (idx >= pkey->num_values || pkey->item_size == 0) {
7066 // FIXME: validate pkey->data
7067 pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, data_size * (idx + 1), caller);
7068
7069 if (pkey->data == 0) {
7070 pkey->total_size = 0;
7071 pkey->num_values = 0;
7072 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));
7073 return DB_FULL;
7074 }
7075
7076 pkey->data -= (POINTER_T) pheader;
7077 if (!pkey->item_size)
7078 pkey->item_size = data_size;
7079 pkey->total_size = data_size * (idx + 1);
7080 pkey->num_values = idx + 1;
7081 }
7082
7083#if 0
7084 /* cut strings which are too long */
7085 if ((type == TID_STRING || type == TID_LINK) && (int) strlen((char *) data) + 1 > pkey->item_size)
7086 *((char *) data + pkey->item_size - 1) = 0;
7087
7088 /* copy data */
7089 memcpy((char *) pheader + pkey->data + idx * pkey->item_size, data, pkey->item_size);
7090#endif
7091
7092 if ((type == TID_STRING || type == TID_LINK)) {
7093 /* cut strings which are too long */
7094 mstrlcpy((char *) pheader + pkey->data + idx * pkey->item_size, (char*)data, pkey->item_size);
7095 } else {
7096 /* copy data */
7097 memcpy((char *) pheader + pkey->data + idx * pkey->item_size, data, pkey->item_size);
7098 }
7099
7100 /* update time */
7101 pkey->last_written = ss_time();
7102
7103 return DB_SUCCESS;
7104}
7105
7106static 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)
7107{
7108 /* check for write access */
7109 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7110 return DB_NO_ACCESS;
7111 }
7112
7113 if (pkey->type != type) {
7114 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));
7115 return DB_TYPE_MISMATCH;
7116 }
7117
7118 /* keys cannot contain data */
7119 if (pkey->type == TID_KEY) {
7120 db_msg(msg, MERROR, caller, "\"%s\" of type TID_KEY cannot contain data", db_get_path_locked(pheader, pkey).c_str());
7121 return DB_TYPE_MISMATCH;
7122 }
7123
7124 if (type == TID_STRING || type == TID_LINK) {
7125 if (num_values > 1) {
7126 int item_size = pkey->item_size;
7127 if (data_size > 0 && num_values > 0)
7128 item_size = data_size/num_values;
7129 //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);
7130 for (int i=0; i<num_values; i++) {
7131 const char* value = ((const char*)data) + i * item_size;
7132 //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);
7133 if (!is_utf8(value)) {
7134 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);
7135 // just a warning for now. K.O.
7136 //return DB_TYPE_MISMATCH;
7137 }
7138 }
7139 } else {
7140 const char* value = (const char*)data;
7141 //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);
7142 if (!is_utf8(value)) {
7143 db_msg(msg, MERROR, caller, "\"%s\" set to invalid UTF-8 Unicode string value \"%s\"", db_get_path_locked(pheader, pkey).c_str(), value);
7144 // just a warning for now. K.O.
7145 //return DB_TYPE_MISMATCH;
7146 }
7147 }
7148 }
7149
7150 return DB_SUCCESS;
7151}
7152
7153static 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)
7154{
7155 /* check for write access */
7156 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7157 return DB_NO_ACCESS;
7158 }
7159
7160 if (pkey->type != type) {
7161 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));
7162 return DB_TYPE_MISMATCH;
7163 }
7164
7165 /* keys cannot contain data */
7166 if (pkey->type == TID_KEY) {
7167 db_msg(msg, MERROR, "db_set_data_index", "\"%s\" of type TID_KEY cannot contain data", db_get_path_locked(pheader, pkey).c_str());
7168 return DB_TYPE_MISMATCH;
7169 }
7170
7171 /* check utf-8 encoding */
7172 if (pkey->type == TID_STRING || pkey->type == TID_LINK) {
7173 //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);
7174 const char* value = (const char*)data;
7175 if (!is_utf8(value)) {
7176 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);
7177 // just a warning for now. K.O.
7178 //return DB_TYPE_MISMATCH;
7179 }
7180 }
7181
7182 /* check for valid idx */
7183 if (idx < 0) {
7184 db_msg(msg, MERROR, caller, "\%s\" given invalid index %d", db_get_path_locked(pheader, pkey).c_str(), idx);
7185 return DB_INVALID_PARAM;
7186 }
7187
7188 /* check for valid array element size: if new element size
7189 is different from existing size, ODB becomes corrupted */
7190 if (pkey->item_size != 0 && data_size != pkey->item_size) {
7191 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);
7192 return DB_TYPE_MISMATCH;
7193 }
7194
7195 return DB_SUCCESS;
7196}
7197
7198#endif // LOCAL_ROUTINES
7199
7200/********************************************************************/
7220INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7221{
7222 if (rpc_is_remote())
7223 return rpc_call(RPC_DB_SET_DATA, hDB, hKey, data, buf_size, num_values, type);
7224
7225#ifdef LOCAL_ROUTINES
7226 {
7227 DATABASE_HEADER *pheader;
7228 KEY *pkey;
7230 int link_idx;
7231 char link_name[256];
7232 int status;
7233
7234 if (hDB > _database_entries || hDB <= 0) {
7235 cm_msg(MERROR, "db_set_data", "invalid database handle");
7236 return DB_INVALID_HANDLE;
7237 }
7238
7239 if (!_database[hDB - 1].attached) {
7240 cm_msg(MERROR, "db_set_data", "invalid database handle");
7241 return DB_INVALID_HANDLE;
7242 }
7243
7244 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7245 cm_msg(MERROR, "db_set_data", "invalid key handle");
7246 return DB_INVALID_HANDLE;
7247 }
7248
7249 if (num_values == 0)
7250 return DB_INVALID_PARAM;
7251
7253 db_err_msg* msg = NULL;
7254
7255 pheader = _database[hDB - 1].database_header;
7256
7257 /* check if hKey argument is correct */
7258 if (!db_validate_hkey(pheader, hKey)) {
7260 return DB_INVALID_HANDLE;
7261 }
7262
7263 pkey = (KEY *) ((char *) pheader + hKey);
7264
7265 /* check for write access */
7266 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7268 return DB_NO_ACCESS;
7269 }
7270
7271 /* check for link to array index */
7272 if (pkey->type == TID_LINK) {
7273 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7274 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7276 if (strchr(link_name, '[') == NULL)
7277 return DB_INVALID_LINK;
7278 link_idx = atoi(strchr(link_name, '[') + 1);
7279 *strchr(link_name, '[') = 0;
7281 return DB_INVALID_LINK;
7282 return db_set_data_index(hDB, hkeylink, data, buf_size, link_idx, type);
7283 }
7284 }
7285
7286 status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_data", &msg);
7287
7288 if (status != DB_SUCCESS) {
7290 if (msg)
7291 db_flush_msg(&msg);
7292 return status;
7293 }
7294
7295 db_allow_write_locked(&_database[hDB-1], "db_set_data");
7296
7297 status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_data", &msg);
7298
7299 if (status != DB_SUCCESS) {
7301 if (msg)
7302 db_flush_msg(&msg);
7303 return status;
7304 }
7305
7306 db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
7308 if (msg)
7309 db_flush_msg(&msg);
7310
7311
7312 }
7313#endif /* LOCAL_ROUTINES */
7314
7315 return DB_SUCCESS;
7316}
7317
7318INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7319/*
7320
7321 Same as db_set_data(), but do not notify hot-linked clients
7322
7323 */
7324{
7325 if (rpc_is_remote())
7326 return rpc_call(RPC_DB_SET_DATA1, hDB, hKey, data, buf_size, num_values, type);
7327
7328#ifdef LOCAL_ROUTINES
7329 {
7330 DATABASE_HEADER *pheader;
7331 KEY *pkey;
7333 int link_idx;
7334 char link_name[256];
7335 int status;
7336
7337 if (hDB > _database_entries || hDB <= 0) {
7338 cm_msg(MERROR, "db_set_data1", "invalid database handle");
7339 return DB_INVALID_HANDLE;
7340 }
7341
7342 if (!_database[hDB - 1].attached) {
7343 cm_msg(MERROR, "db_set_data1", "invalid database handle");
7344 return DB_INVALID_HANDLE;
7345 }
7346
7347 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7348 cm_msg(MERROR, "db_set_data1", "invalid key handle");
7349 return DB_INVALID_HANDLE;
7350 }
7351
7352 if (num_values == 0)
7353 return DB_INVALID_PARAM;
7354
7356 db_err_msg* msg = NULL;
7357
7358 pheader = _database[hDB - 1].database_header;
7359
7360 /* check if hKey argument is correct */
7361 if (!db_validate_hkey(pheader, hKey)) {
7363 return DB_INVALID_HANDLE;
7364 }
7365
7366 pkey = (KEY *) ((char *) pheader + hKey);
7367
7368 /* check for write access */
7369 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7371 return DB_NO_ACCESS;
7372 }
7373
7374 /* check for link to array index */
7375 if (pkey->type == TID_LINK) {
7376 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7377 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7379 if (strchr(link_name, '[') == NULL)
7380 return DB_INVALID_LINK;
7381 link_idx = atoi(strchr(link_name, '[') + 1);
7382 *strchr(link_name, '[') = 0;
7384 return DB_INVALID_LINK;
7385 return db_set_data_index1(hDB, hkeylink, data, buf_size, link_idx, type, FALSE);
7386 }
7387 }
7388
7389 status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_data1", &msg);
7390
7391 if (status != DB_SUCCESS) {
7393 if (msg)
7394 db_flush_msg(&msg);
7395 return status;
7396 }
7397
7398 db_allow_write_locked(&_database[hDB - 1], "db_set_data1");
7399
7400 status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_data1", &msg);
7401
7402 if (status != DB_SUCCESS) {
7404 if (msg)
7405 db_flush_msg(&msg);
7406 return status;
7407 }
7408
7410 if (msg)
7411 db_flush_msg(&msg);
7412
7413 }
7414#endif /* LOCAL_ROUTINES */
7415
7416 return DB_SUCCESS;
7417}
7418
7419/********************************************************************/
7430INT db_set_link_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7431{
7432 if (rpc_is_remote())
7433 return rpc_call(RPC_DB_SET_LINK_DATA, hDB, hKey, data, buf_size, num_values, type);
7434
7435#ifdef LOCAL_ROUTINES
7436 {
7437 DATABASE_HEADER *pheader;
7438 KEY *pkey;
7439 int status;
7440
7441 if (hDB > _database_entries || hDB <= 0) {
7442 cm_msg(MERROR, "db_set_data", "invalid database handle");
7443 return DB_INVALID_HANDLE;
7444 }
7445
7446 if (!_database[hDB - 1].attached) {
7447 cm_msg(MERROR, "db_set_data", "invalid database handle");
7448 return DB_INVALID_HANDLE;
7449 }
7450
7451 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7452 cm_msg(MERROR, "db_set_data", "invalid key handle");
7453 return DB_INVALID_HANDLE;
7454 }
7455
7456 if (num_values == 0)
7457 return DB_INVALID_PARAM;
7458
7460 db_err_msg* msg = NULL;
7461
7462 pheader = _database[hDB - 1].database_header;
7463
7464 /* check if hKey argument is correct */
7465 if (!db_validate_hkey(pheader, hKey)) {
7467 return DB_INVALID_HANDLE;
7468 }
7469
7470 pkey = (KEY *) ((char *) pheader + hKey);
7471
7472 status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_link_data", &msg);
7473
7474 if (status != DB_SUCCESS) {
7476 if (msg)
7477 db_flush_msg(&msg);
7478 return status;
7479 }
7480
7481 db_allow_write_locked(&_database[hDB - 1], "db_set_link_data");
7482
7483 status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_link_data", &msg);
7484
7485 if (status != DB_SUCCESS) {
7487 if (msg)
7488 db_flush_msg(&msg);
7489 return status;
7490 }
7491
7492 db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
7494 if (msg)
7495 db_flush_msg(&msg);
7496
7497 }
7498#endif /* LOCAL_ROUTINES */
7499
7500 return DB_SUCCESS;
7501}
7502
7504#ifndef DOXYGEN_SHOULD_SKIP_THIS
7505
7506/*------------------------------------------------------------------*/
7508/********************************************************************\
7509
7510 Routine: db_set_num_values
7511
7512 Purpose: Set number of values in a key. Extend with zeros or truncate.
7513
7514 Input:
7515 HNDLE hDB Handle to the database
7516 HNDLE hKey Handle of key
7517 INT num_values Number of data values
7518
7519 Output:
7520 none
7521
7522 Function value:
7523 DB_SUCCESS Successful completion
7524 DB_INVALID_HANDLE Database handle is invalid
7525
7526\********************************************************************/
7527{
7528 if (rpc_is_remote())
7529 return rpc_call(RPC_DB_SET_NUM_VALUES, hDB, hKey, num_values);
7530
7531#ifdef LOCAL_ROUTINES
7532 {
7533 DATABASE_HEADER *pheader;
7534 KEY *pkey;
7535 INT new_size;
7536
7537 if (hDB > _database_entries || hDB <= 0) {
7538 cm_msg(MERROR, "db_set_num_values", "invalid database handle");
7539 return DB_INVALID_HANDLE;
7540 }
7541
7542 if (!_database[hDB - 1].attached) {
7543 cm_msg(MERROR, "db_set_num_values", "invalid database handle");
7544 return DB_INVALID_HANDLE;
7545 }
7546
7547 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7548 cm_msg(MERROR, "db_set_num_values", "invalid key handle");
7549 return DB_INVALID_HANDLE;
7550 }
7551
7552 if (num_values <= 0) {
7553 cm_msg(MERROR, "db_set_num_values", "invalid num_values %d", num_values);
7554 return DB_INVALID_PARAM;
7555 }
7556
7557 if (num_values == 0)
7558 return DB_INVALID_PARAM;
7559
7561 db_err_msg* msg = NULL;
7562
7563 pheader = _database[hDB - 1].database_header;
7564
7565 /* check if hKey argument is correct */
7566 if (!db_validate_hkey(pheader, hKey)) {
7568 return DB_INVALID_HANDLE;
7569 }
7570
7571 pkey = (KEY *) ((char *) pheader + hKey);
7572
7573 /* check for write access */
7574 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7576 return DB_NO_ACCESS;
7577 }
7578
7579 /* keys cannot contain data */
7580 if (pkey->type == TID_KEY) {
7582 cm_msg(MERROR, "db_set_num_values", "Key cannot contain data");
7583 return DB_TYPE_MISMATCH;
7584 }
7585
7586 if (pkey->total_size != pkey->item_size * pkey->num_values) {
7588 cm_msg(MERROR, "db_set_num_values", "Corrupted key");
7589 return DB_CORRUPTED;
7590 }
7591
7592 if (pkey->item_size == 0) {
7594 cm_msg(MERROR, "db_set_num_values", "Cannot resize array with item_size equal to zero");
7595 return DB_INVALID_PARAM;
7596 }
7597
7598 db_allow_write_locked(&_database[hDB - 1], "db_set_num_values");
7599
7600 /* resize data size if necessary */
7601 if (pkey->num_values != num_values) {
7602 new_size = pkey->item_size * num_values;
7603
7604 pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, new_size, "db_set_num_values");
7605
7606 if (pkey->data == 0) {
7607 pkey->total_size = 0;
7608 pkey->num_values = 0;
7609 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);
7611 if (msg)
7612 db_flush_msg(&msg);
7613 return DB_FULL;
7614 }
7615
7616 pkey->data -= (POINTER_T) pheader;
7617 pkey->total_size = new_size;
7618 pkey->num_values = num_values;
7619 }
7620
7621 /* update time */
7622 pkey->last_written = ss_time();
7623
7624 db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
7626 if (msg)
7627 db_flush_msg(&msg);
7628
7629 }
7630#endif /* LOCAL_ROUTINES */
7631
7632 return DB_SUCCESS;
7633}
7634
7636#endif /* DOXYGEN_SHOULD_SKIP_THIS */
7637
7638/********************************************************************/
7654{
7655 if (rpc_is_remote())
7656 return rpc_call(RPC_DB_SET_DATA_INDEX, hDB, hKey, data, data_size, idx, type);
7657
7658#ifdef LOCAL_ROUTINES
7659 {
7660 DATABASE_HEADER *pheader;
7661 KEY *pkey;
7662 char link_name[256];
7663 int link_idx;
7665 int status;
7666
7667 if (hDB > _database_entries || hDB <= 0) {
7668 cm_msg(MERROR, "db_set_data_index", "invalid database handle");
7669 return DB_INVALID_HANDLE;
7670 }
7671
7672 if (!_database[hDB - 1].attached) {
7673 cm_msg(MERROR, "db_set_data_index", "invalid database handle");
7674 return DB_INVALID_HANDLE;
7675 }
7676
7677 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7678 cm_msg(MERROR, "db_set_data_index", "invalid key handle");
7679 return DB_INVALID_HANDLE;
7680 }
7681
7683 db_err_msg* msg = NULL;
7684
7685 pheader = _database[hDB - 1].database_header;
7686
7687 /* check if hKey argument is correct */
7688 if (!db_validate_hkey(pheader, hKey)) {
7690 return DB_INVALID_HANDLE;
7691 }
7692
7693 pkey = (KEY *) ((char *) pheader + hKey);
7694
7695 /* check for write access */
7696 if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7698 return DB_NO_ACCESS;
7699 }
7700
7701 /* check for link to array index */
7702 if (pkey->type == TID_LINK) {
7703 mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7704 if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7706 if (strchr(link_name, '[') == NULL)
7707 return DB_INVALID_LINK;
7708 link_idx = atoi(strchr(link_name, '[') + 1);
7709 *strchr(link_name, '[') = 0;
7711 return DB_INVALID_LINK;
7712 return db_set_data_index(hDB, hkeylink, data, data_size, link_idx, type);
7713 }
7714 }
7715
7716 status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_data_index", &msg);
7717
7718 if (status != DB_SUCCESS) {
7720 if (msg)
7721 db_flush_msg(&msg);
7722 return status;
7723 }
7724
7725 db_allow_write_locked(&_database[hDB-1], "db_set_data_index");
7726
7727 status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_data_index", &msg);
7728
7729 if (status != DB_SUCCESS) {
7731 if (msg)
7732 db_flush_msg(&msg);
7733 return status;
7734 }
7735
7736 db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
7738 if (msg)
7739 db_flush_msg(&msg);
7740
7741 }
7742#endif /* LOCAL_ROUTINES */
7743
7744 return DB_SUCCESS;
7745}
7746
7747/********************************************************************/
7760{
7761 if (rpc_is_remote())
7762 return rpc_call(RPC_DB_SET_LINK_DATA_INDEX, hDB, hKey, data, data_size, idx, type);
7763
7764#ifdef LOCAL_ROUTINES
7765 {
7766 DATABASE_HEADER *pheader;
7767 KEY *pkey;
7768 int status;
7769
7770 if (hDB > _database_entries || hDB <= 0) {
7771 cm_msg(MERROR, "db_set_link_data_index", "invalid database handle");
7772 return DB_INVALID_HANDLE;
7773 }
7774
7775 if (!_database[hDB - 1].attached) {
7776 cm_msg(MERROR, "db_set_link_data_index", "invalid database handle");
7777 return DB_INVALID_HANDLE;
7778 }
7779
7780 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7781 cm_msg(MERROR, "db_set_link_data_index", "invalid key handle");
7782 return DB_INVALID_HANDLE;
7783 }
7784
7786 db_err_msg* msg = NULL;
7787
7788 pheader = _database[hDB - 1].database_header;
7789
7790 /* check if hKey argument is correct */
7791 if (!db_validate_hkey(pheader, hKey)) {
7793 return DB_INVALID_HANDLE;
7794 }
7795
7796 pkey = (KEY *) ((char *) pheader + hKey);
7797
7798 status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_link_data_index", &msg);
7799
7800 if (status != DB_SUCCESS) {
7802 if (msg)
7803 db_flush_msg(&msg);
7804 return status;
7805 }
7806
7807 db_allow_write_locked(&_database[hDB - 1], "db_set_link_data_index");
7808
7809 status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_link_data_index", &msg);
7810
7811 if (status != DB_SUCCESS) {
7813 if (msg)
7814 db_flush_msg(&msg);
7815 return status;
7816 }
7817
7818 db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
7820 if (msg)
7821 db_flush_msg(&msg);
7822
7823 }
7824#endif /* LOCAL_ROUTINES */
7825
7826 return DB_SUCCESS;
7827}
7828
7830#ifndef DOXYGEN_SHOULD_SKIP_THIS
7831
7832/*------------------------------------------------------------------*/
7834/********************************************************************\
7835
7836 Routine: db_set_data_index1
7837
7838 Purpose: Set key data for a key which contains an array of values.
7839 Optionally notify clients which have key open.
7840
7841 Input:
7842 HNDLE hDB Handle to the database
7843 HNDLE hKey Handle of key to enumerate
7844 void *data Pointer to single value of data
7845 INT data_size Size of single data element
7846 INT idx Index of array to change [0..n-1]
7847 DWORD type Type of data
7848 BOOL bNotify If TRUE, notify clients
7849
7850 Output:
7851 none
7852
7853 Function value:
7854 DB_SUCCESS Successful completion
7855 DB_INVALID_HANDLE Database handle is invalid
7856 DB_TYPE_MISMATCH Key was created with different type
7857 DB_NO_ACCESS No write access
7858
7859\********************************************************************/
7860{
7861 if (rpc_is_remote())
7862 return rpc_call(RPC_DB_SET_DATA_INDEX1, hDB, hKey, data, data_size, idx, type, bNotify);
7863
7864#ifdef LOCAL_ROUTINES
7865 {
7866 DATABASE_HEADER *pheader;
7867 KEY *pkey;
7868 int status;
7869
7870 if (hDB > _database_entries || hDB <= 0) {
7871 cm_msg(MERROR, "db_set_data_index1", "invalid database handle");
7872 return DB_INVALID_HANDLE;
7873 }
7874
7875 if (!_database[hDB - 1].attached) {
7876 cm_msg(MERROR, "db_set_data_index1", "invalid database handle");
7877 return DB_INVALID_HANDLE;
7878 }
7879
7880 if (hKey < (int) sizeof(DATABASE_HEADER)) {
7881 cm_msg(MERROR, "db_set_data_index1", "invalid key handle");
7882 return DB_INVALID_HANDLE;
7883 }
7884
7886 db_err_msg* msg = NULL;
7887
7888 pheader = _database[hDB - 1].database_header;
7889
7890 /* check if hKey argument is correct */
7891 if (!db_validate_hkey(pheader, hKey)) {
7893 return DB_INVALID_HANDLE;
7894 }
7895
7896 pkey = (KEY *) ((char *) pheader + hKey);
7897
7898 status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_data_index1", &msg);
7899
7900 if (status != DB_SUCCESS) {
7902 if (msg)
7903 db_flush_msg(&msg);
7904 return status;
7905 }
7906
7907 db_allow_write_locked(&_database[hDB - 1], "db_set_data_index1");
7908
7909 status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_data_index1", &msg);
7910
7911 if (status != DB_SUCCESS) {
7913 if (msg)
7914 db_flush_msg(&msg);
7915 return status;
7916 }
7917
7918 if (bNotify)
7919 db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
7920
7922 if (msg)
7923 db_flush_msg(&msg);
7924
7925 }
7926#endif /* LOCAL_ROUTINES */
7927
7928 return DB_SUCCESS;
7929}
7930
7931/*----------------------------------------------------------------------------*/
7932
7933INT db_merge_data(HNDLE hDB, HNDLE hKeyRoot, const char *name, void *data, INT data_size, INT num_values, INT type)
7934/********************************************************************\
7935
7936 Routine: db_merge_data
7937
7938 Purpose: Merge an array with an ODB array. If the ODB array doesn't
7939 exist, create it and fill it with the array. If it exists,
7940 load it in the array. Adjust ODB array size if necessary.
7941
7942 Input:
7943 HNDLE hDB Handle to the database
7944 HNDLE hKeyRoot Key handle to start with, 0 for root
7945 cha *name Key name relative to hKeyRoot
7946 void *data Pointer to data array
7947 INT data_size Size of data array
7948 INT num_values Number of values in array
7949 DWORD type Type of data
7950
7951 Output:
7952 none
7953
7954 Function value:
7955 <same as db_set_data>
7956
7957\********************************************************************/
7958{
7959 HNDLE hKey;
7961
7962 if (num_values == 0)
7963 return DB_INVALID_PARAM;
7964
7966 if (status != DB_SUCCESS) {
7969 if (status != DB_SUCCESS)
7970 return status;
7971 status = db_set_data(hDB, hKey, data, data_size, num_values, type);
7972 } else {
7973 old_size = data_size;
7975 status = db_set_data(hDB, hKey, data, data_size, num_values, type);
7976 }
7977
7978 return status;
7979}
7980
7981#ifdef LOCAL_ROUTINES
7982
7983/*------------------------------------------------------------------*/
7985/********************************************************************\
7986
7987 Routine: db_set_mode_wlocked()
7988
7989 Purpose: Set access mode of key
7990
7991 Input:
7992 pheader Database
7993 pkey Key
7994 DWORD mode Access mode, any or'ed combination of
7995 MODE_READ, MODE_WRITE, MODE_EXCLUSIVE
7996 and MODE_DELETE
7997 recurse Value of 0: do not recurse subtree,
7998 value of 1: recurse subtree, becomes recurse level
7999
8000 Function value:
8001 DB_SUCCESS Successful completion
8002
8003\********************************************************************/
8004{
8005 /* resolve links */
8006 if (pkey->type == TID_LINK) {
8007 int status;
8008 pkey = (KEY*)db_resolve_link_locked(pheader, pkey, &status, msg);
8009 if (!pkey) {
8010 return status;
8011 }
8012 }
8013
8014 if (pkey->type == TID_KEY && recurse) {
8015 // drop "const" from KEY* we are permitted to write to ODB!
8016 KEY* psubkey = (KEY*)db_enum_first_locked(pheader, pkey, msg);
8017 while (psubkey) {
8018 db_set_mode_wlocked(pheader, psubkey, mode, recurse+1, msg);
8019 psubkey = (KEY*)db_enum_next_locked(pheader, pkey, psubkey, msg);
8020 }
8021 }
8022
8023 /* now set mode */
8025
8026 return DB_SUCCESS;
8027}
8028
8029#endif
8030
8031/*------------------------------------------------------------------*/
8033/********************************************************************\
8034
8035 Routine: db_set_mode
8036
8037 Purpose: Set access mode of key
8038
8039 Input:
8040 HNDLE hDB Handle to the database
8041 HNDLE hKey Key handle
8042 DWORD mode Access mode, any or'ed combination of
8043 MODE_READ, MODE_WRITE, MODE_EXCLUSIVE
8044 and MODE_DELETE
8045 BOOL recurse Value of 0 (FALSE): do not recurse subtree,
8046 value of 1 (TRUE): recurse subtree,
8047 value of 2: recurse subtree, assume database is locked by caller.
8048
8049 Output:
8050 none
8051
8052 Function value:
8053 DB_SUCCESS Successful completion
8054 DB_INVALID_HANDLE Database handle is invalid
8055
8056\********************************************************************/
8057{
8058 if (rpc_is_remote())
8060
8061#ifdef LOCAL_ROUTINES
8062 {
8063 DATABASE_HEADER *pheader;
8064 BOOL locked = FALSE;
8065
8066 if (hDB > _database_entries || hDB <= 0) {
8067 cm_msg(MERROR, "db_set_mode", "invalid database handle");
8068 return DB_INVALID_HANDLE;
8069 }
8070
8071 if (!_database[hDB - 1].attached) {
8072 cm_msg(MERROR, "db_set_mode", "invalid database handle");
8073 return DB_INVALID_HANDLE;
8074 }
8075
8076 if (recurse < 2) {
8078 locked = TRUE;
8079 }
8080
8081 pheader = _database[hDB - 1].database_header;
8082
8083 db_err_msg* msg = NULL;
8084 int status = 0;
8085
8086 KEY *pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_set_mode", &msg);
8087
8088 if (!pkey) {
8089 if (locked) {
8091 if (msg)
8092 db_flush_msg(&msg);
8093 return status;
8094 }
8095 }
8096
8097 db_allow_write_locked(&_database[hDB-1], "db_set_mode");
8098
8099 status = db_set_mode_wlocked(pheader, pkey, mode, recurse, &msg);
8100
8101 if (locked) {
8103 if (msg)
8104 db_flush_msg(&msg);
8105 }
8106
8107 return status;
8108 }
8109#endif /* LOCAL_ROUTINES */
8110
8111 return DB_SUCCESS;
8112}
8113
8115#endif /* DOXYGEN_SHOULD_SKIP_THIS */
8116
8117/********************************************************************/
8131INT db_load(HNDLE hDB, HNDLE hKeyRoot, const char *filename, BOOL bRemote)
8132{
8133 struct stat stat_buf;
8134 INT hfile, size, n, i, status;
8135 char *buffer;
8136
8137 if (rpc_is_remote() && bRemote)
8138 return rpc_call(RPC_DB_LOAD, hDB, hKeyRoot, filename);
8139
8140 /* open file */
8141 hfile = open(filename, O_RDONLY | O_TEXT, 0644);
8142 if (hfile == -1) {
8143 cm_msg(MERROR, "db_load", "file \"%s\" not found", filename);
8144 return DB_FILE_ERROR;
8145 }
8146
8147 /* allocate buffer with file size */
8148 fstat(hfile, &stat_buf);
8149 size = stat_buf.st_size;
8150 buffer = (char *) malloc(size + 1);
8151
8152 if (buffer == NULL) {
8153 cm_msg(MERROR, "db_load", "cannot allocate ODB load buffer");
8154 close(hfile);
8155 return DB_NO_MEMORY;
8156 }
8157
8158 n = 0;
8159
8160 do {
8161 i = read(hfile, buffer + n, size - n);
8162 if (i <= 0)
8163 break;
8164 n += i;
8165 } while (TRUE);
8166
8167 buffer[n] = 0;
8168
8169 if (strncmp(buffer, "<?xml version=\"1.0\"", 19) == 0) {
8170 status = db_paste_xml(hDB, hKeyRoot, buffer);
8171 if (status != DB_SUCCESS)
8172 printf("Error in file \"%s\"\n", filename);
8173 } else if( buffer[0] == '{'){
8174 if(strrchr(buffer, '}')){
8175 status = db_paste_json(hDB, hKeyRoot, buffer);
8176 } else {
8178 }
8179 } else
8180 status = db_paste(hDB, hKeyRoot, buffer);
8181
8182 close(hfile);
8183 free(buffer);
8184
8185 return status;
8186}
8187
8188/********************************************************************/
8222INT db_copy(HNDLE hDB, HNDLE hKey, char *buffer, INT * buffer_size, const char *path)
8223{
8224 INT i, j, size, status;
8225 KEY key;
8226 HNDLE hSubkey;
8227 char full_path[MAX_ODB_PATH];
8228 char *data;
8229 char line[MAX_STRING_LENGTH * 2];
8230 BOOL bWritten;
8231
8232 mstrlcpy(full_path, path, sizeof(full_path));
8233
8234 bWritten = FALSE;
8235
8236 /* first enumerate this level */
8237 for (i = 0;; i++) {
8239
8240 if (i == 0 && !hSubkey) {
8241 /* If key has no subkeys, just write this key */
8243 if (status != DB_SUCCESS)
8244 continue;
8245 size = key.total_size;
8246 data = (char *) malloc(size);
8247 if (data == NULL) {
8248 cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
8249 return DB_NO_MEMORY;
8250 }
8251 line[0] = 0;
8252
8253 if (key.type != TID_KEY) {
8255 if (status != DB_SUCCESS)
8256 continue;
8257 if (key.num_values == 1) {
8258 sprintf(line, "%s = %s : ", key.name, rpc_tid_name(key.type));
8259
8260 if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
8261 /* multiline string */
8262 sprintf(line + strlen(line), "[====#$@$#====]\n");
8263
8264 /* copy line to buffer */
8265 if ((INT) (strlen(line) + 1) > *buffer_size) {
8266 free(data);
8267 return DB_TRUNCATED;
8268 }
8269
8270 strcpy(buffer, line);
8271 buffer += strlen(line);
8272 *buffer_size -= strlen(line);
8273
8274 /* copy multiple lines to buffer */
8275 if (key.item_size > *buffer_size) {
8276 free(data);
8277 return DB_TRUNCATED;
8278 }
8279
8280 strcpy(buffer, data);
8281 buffer += strlen(data);
8282 *buffer_size -= strlen(data);
8283
8284 strcpy(line, "\n====#$@$#====\n");
8285 } else {
8286 std::string str = db_sprintf(data, key.item_size, 0, key.type);
8287
8288 if (key.type == TID_STRING || key.type == TID_LINK)
8289 sprintf(line + strlen(line), "[%d] ", key.item_size);
8290
8291 sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8292 }
8293 } else {
8294 sprintf(line, "%s = %s[%d] :\n", key.name, rpc_tid_name(key.type), key.num_values);
8295
8296 for (j = 0; j < key.num_values; j++) {
8297 if (key.type == TID_STRING || key.type == TID_LINK)
8298 sprintf(line + strlen(line), "[%d] ", key.item_size);
8299 else
8300 sprintf(line + strlen(line), "[%d] ", j);
8301
8302 std::string str = db_sprintf(data, key.item_size, j, key.type);
8303 sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8304
8305 /* copy line to buffer */
8306 if ((INT) (strlen(line) + 1) > *buffer_size) {
8307 free(data);
8308 return DB_TRUNCATED;
8309 }
8310
8311 strcpy(buffer, line);
8312 buffer += strlen(line);
8313 *buffer_size -= strlen(line);
8314 line[0] = 0;
8315 }
8316 }
8317 }
8318
8319 /* copy line to buffer */
8320 if ((INT) (strlen(line) + 1) > *buffer_size) {
8321 free(data);
8322 return DB_TRUNCATED;
8323 }
8324
8325 strcpy(buffer, line);
8326 buffer += strlen(line);
8327 *buffer_size -= strlen(line);
8328
8329 free(data);
8330 data = NULL;
8331 }
8332
8333 if (!hSubkey)
8334 break;
8335
8337 if (status != DB_SUCCESS)
8338 continue;
8339
8340 if (strcmp(key.name, "arr2") == 0)
8341 printf("\narr2\n");
8342 size = key.total_size;
8343 data = (char *) malloc(size);
8344 if (data == NULL) {
8345 cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
8346 return DB_NO_MEMORY;
8347 }
8348
8349 line[0] = 0;
8350
8351 if (key.type == TID_KEY) {
8352 char str[MAX_ODB_PATH];
8353
8354 /* new line */
8355 if (bWritten) {
8356 if (*buffer_size < 2) {
8357 free(data);
8358 return DB_TRUNCATED;
8359 }
8360
8361 strcpy(buffer, "\n");
8362 buffer += 1;
8363 *buffer_size -= 1;
8364 }
8365
8366 strcpy(str, full_path);
8367 if (str[0] && str[strlen(str) - 1] != '/')
8368 strcat(str, "/");
8369 strcat(str, key.name);
8370
8371 /* recurse */
8372 status = db_copy(hDB, hSubkey, buffer, buffer_size, str);
8373 if (status != DB_SUCCESS) {
8374 free(data);
8375 return status;
8376 }
8377
8378 buffer += strlen(buffer);
8379 bWritten = FALSE;
8380 } else {
8382 if (status != DB_SUCCESS)
8383 continue;
8384
8385 if (!bWritten) {
8386 if (path[0] == 0)
8387 sprintf(line, "[.]\n");
8388 else
8389 sprintf(line, "[%s]\n", path);
8390 bWritten = TRUE;
8391 }
8392
8393 if (key.num_values == 1) {
8394 sprintf(line + strlen(line), "%s = %s : ", key.name, rpc_tid_name(key.type));
8395
8396 if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
8397 /* multiline string */
8398 sprintf(line + strlen(line), "[====#$@$#====]\n");
8399
8400 /* ensure string limiter */
8401 data[size - 1] = 0;
8402
8403 /* copy line to buffer */
8404 if ((INT) (strlen(line) + 1) > *buffer_size) {
8405 free(data);
8406 return DB_TRUNCATED;
8407 }
8408
8409 strcpy(buffer, line);
8410 buffer += strlen(line);
8411 *buffer_size -= strlen(line);
8412
8413 /* copy multiple lines to buffer */
8414 if (key.item_size > *buffer_size) {
8415 free(data);
8416 return DB_TRUNCATED;
8417 }
8418
8419 strcpy(buffer, data);
8420 buffer += strlen(data);
8421 *buffer_size -= strlen(data);
8422
8423 strcpy(line, "\n====#$@$#====\n");
8424 } else {
8425 std::string str = db_sprintf(data, key.item_size, 0, key.type);
8426
8427 if (key.type == TID_STRING || key.type == TID_LINK)
8428 sprintf(line + strlen(line), "[%d] ", key.item_size);
8429
8430 sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8431 }
8432 } else {
8433 sprintf(line + strlen(line), "%s = %s[%d] :\n", key.name, rpc_tid_name(key.type), key.num_values);
8434
8435 for (j = 0; j < key.num_values; j++) {
8436 if (key.type == TID_STRING || key.type == TID_LINK)
8437 sprintf(line + strlen(line), "[%d] ", key.item_size);
8438 else
8439 sprintf(line + strlen(line), "[%d] ", j);
8440
8441 std::string str = db_sprintf(data, key.item_size, j, key.type);
8442 sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8443
8444 /* copy line to buffer */
8445 if ((INT) (strlen(line) + 1) > *buffer_size) {
8446 free(data);
8447 return DB_TRUNCATED;
8448 }
8449
8450 strcpy(buffer, line);
8451 buffer += strlen(line);
8452 *buffer_size -= strlen(line);
8453 line[0] = 0;
8454 }
8455 }
8456
8457 /* copy line to buffer */
8458 if ((INT) (strlen(line) + 1) > *buffer_size) {
8459 free(data);
8460 return DB_TRUNCATED;
8461 }
8462
8463 strcpy(buffer, line);
8464 buffer += strlen(line);
8465 *buffer_size -= strlen(line);
8466 }
8467
8468 free(data);
8469 data = NULL;
8470 }
8471
8472 if (bWritten) {
8473 if (*buffer_size < 2)
8474 return DB_TRUNCATED;
8475
8476 strcpy(buffer, "\n");
8477 buffer += 1;
8478 *buffer_size -= 1;
8479 }
8480
8481 return DB_SUCCESS;
8482}
8483
8484/********************************************************************/
8492INT db_paste(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
8493{
8494 char title[MAX_STRING_LENGTH];
8495 char *data;
8496 const char *pold;
8497 INT data_size, index;
8498 INT tid, i, j, n_data, string_length, status, size;
8499 HNDLE hKey;
8500 KEY root_key;
8501
8502 title[0] = 0;
8503
8504 if (hKeyRoot == 0)
8506
8507 db_get_key(hDB, hKeyRoot, &root_key);
8508
8509 /* initial data size */
8510 data_size = 1000;
8511 data = (char *) malloc(data_size);
8512 if (data == NULL) {
8513 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8514 return DB_NO_MEMORY;
8515 }
8516
8517 do {
8518 char line[10*MAX_STRING_LENGTH];
8519
8520 if (*buffer == 0)
8521 break;
8522
8523 for (i = 0; *buffer != '\n' && *buffer && i < 10*MAX_STRING_LENGTH; i++)
8524 line[i] = *buffer++;
8525
8526 if (i == 10*MAX_STRING_LENGTH) {
8527 line[10*MAX_STRING_LENGTH-1] = 0;
8528 cm_msg(MERROR, "db_paste", "line too long: %s...", line);
8529 free(data);
8530 return DB_TRUNCATED;
8531 }
8532
8533 line[i] = 0;
8534 if (*buffer == '\n')
8535 buffer++;
8536
8537 /* check if it is a section title */
8538 if (line[0] == '[') {
8539 /* extract title and append '/' */
8540 mstrlcpy(title, line + 1, sizeof(title));
8541 if (strchr(title, ']'))
8542 *strchr(title, ']') = 0;
8543 if (title[0] && title[strlen(title) - 1] != '/')
8544 mstrlcat(title, "/", sizeof(title));
8545 } else {
8546 /* valid data line if it includes '=' and no ';' */
8547 if (strchr(line, '=') && line[0] != ';') {
8548 char key_name[MAX_ODB_PATH];
8549 char test_str[MAX_ODB_PATH];
8550 char data_str[10*MAX_STRING_LENGTH];
8551
8552 /* copy type info and data */
8553 char* pline = strrchr(line, '=') + 1;
8554 while (strstr(line, ": [") != NULL && strstr(line, ": [") < pline) {
8555 pline -= 2;
8556 while (*pline != '=' && pline > line)
8557 pline--;
8558 pline++;
8559 }
8560 while (*pline == ' ')
8561 pline++;
8562 mstrlcpy(data_str, pline, sizeof(data_str));
8563
8564 /* extract key name */
8565 *strrchr(line, '=') = 0;
8566 while (strstr(line, ": [") && strchr(line, '='))
8567 *strrchr(line, '=') = 0;
8568
8569 pline = &line[strlen(line) - 1];
8570 while (*pline == ' ')
8571 *pline-- = 0;
8572
8573 key_name[0] = 0;
8574 if (title[0] != '.')
8575 mstrlcpy(key_name, title, sizeof(key_name));
8576
8577 mstrlcat(key_name, line, sizeof(key_name));
8578
8579 /* evaluate type info */
8580 mstrlcpy(line, data_str, sizeof(line));
8581 if (strchr(line, ' '))
8582 *strchr(line, ' ') = 0;
8583
8584 n_data = 1;
8585 if (strchr(line, '[')) {
8586 n_data = atol(strchr(line, '[') + 1);
8587 *strchr(line, '[') = 0;
8588 }
8589
8590 for (tid = 0; tid < TID_LAST; tid++)
8591 if (strcmp(rpc_tid_name(tid), line) == 0)
8592 break;
8593 if (tid == TID_LAST) {
8594 for (tid = 0; tid < TID_LAST; tid++)
8595 if (strcmp(rpc_tid_name_old(tid), line) == 0)
8596 break;
8597 }
8598
8599 string_length = 0;
8600
8601 if (tid == TID_LAST)
8602 cm_msg(MERROR, "db_paste", "found unknown data type \"%s\" in ODB file", line);
8603 else {
8604 /* skip type info */
8605 char* pc = data_str;
8606 while (*pc != ' ' && *pc)
8607 pc++;
8608 while ((*pc == ' ' || *pc == ':') && *pc)
8609 pc++;
8610
8611 //mstrlcpy(data_str, pc, sizeof(data_str)); // MacOS 10.9 does not permit mstrlcpy() of overlapping strings
8612 assert(strlen(pc) < sizeof(data_str)); // "pc" points at a substring inside "data_str"
8613 memmove(data_str, pc, strlen(pc)+1);
8614
8615 if (n_data > 1) {
8616 data_str[0] = 0;
8617 if (!*buffer)
8618 break;
8619
8620 for (j = 0; *buffer != '\n' && *buffer; j++)
8621 data_str[j] = *buffer++;
8622 data_str[j] = 0;
8623 if (*buffer == '\n')
8624 buffer++;
8625 }
8626
8627 for (i = 0; i < n_data; i++) {
8628 /* strip trailing \n */
8629 char* pc = &data_str[strlen(data_str) - 1];
8630 while (*pc == '\n' || *pc == '\r')
8631 *pc-- = 0;
8632
8633 if (tid == TID_STRING || tid == TID_LINK) {
8634 if (!string_length) {
8635 if (data_str[1] == '=')
8636 string_length = -1;
8637 else
8641 cm_msg(MERROR, "db_paste", "found string exceeding MAX_STRING_LENGTH, odb path \"%s\"", key_name);
8642 }
8643 if (string_length == 0) {
8644 string_length = 32;
8645 cm_msg(MERROR, "db_paste", "found string length of zero, set to 32, odb path \"%s\"", key_name);
8646 }
8647 }
8648
8649 if (string_length == -1) {
8650 /* multi-line string */
8651 if (strstr(buffer, "\n====#$@$#====\n") != NULL) {
8652 string_length = (POINTER_T) strstr(buffer, "\n====#$@$#====\n") - (POINTER_T) buffer + 1;
8653
8654 if (string_length >= data_size) {
8655 data_size += string_length + 100;
8656 data = (char *) realloc(data, data_size);
8657 if (data == NULL) {
8658 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8659 return DB_NO_MEMORY;
8660 }
8661 }
8662
8663 memset(data, 0, data_size);
8664 strncpy(data, buffer, string_length);
8665 data[string_length - 1] = 0;
8666 buffer = strstr(buffer, "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
8667 } else
8668 cm_msg(MERROR, "db_paste", "found multi-line string without termination sequence");
8669 } else {
8670 char* pc = data_str + 2;
8671 while (*pc && *pc != ' ')
8672 pc++;
8673
8674 // skip one space (needed for strings starting with spaces)
8675 if (*pc)
8676 pc++;
8677
8678 /* limit string size */
8679 *(pc + string_length - 1) = 0;
8680
8681 /* increase data buffer if necessary */
8682 if (string_length * (i + 1) >= data_size) {
8683 data_size += 1000;
8684 data = (char *) realloc(data, data_size);
8685 if (data == NULL) {
8686 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8687 return DB_NO_MEMORY;
8688 }
8689 }
8690
8692 }
8693 } else {
8694 char* pc = data_str;
8695
8696 if (n_data > 1 && data_str[0] == '[') {
8697 index = atoi(data_str+1);
8698 pc = strchr(data_str, ']') + 1;
8699 while (*pc && *pc == ' ')
8700 pc++;
8701 } else
8702 index = 0;
8703
8704 /* increase data buffer if necessary */
8705 if (rpc_tid_size(tid) * (index + 1) >= data_size) {
8706 data_size += 1000;
8707 data = (char *) realloc(data, data_size);
8708 if (data == NULL) {
8709 cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8710 return DB_NO_MEMORY;
8711 }
8712 }
8713
8714 db_sscanf(pc, data, &size, index, tid);
8715 }
8716
8717 if (i < n_data - 1) {
8718 data_str[0] = 0;
8719 if (!*buffer)
8720 break;
8721
8722 pold = buffer;
8723
8724 for (j = 0; *buffer != '\n' && *buffer; j++)
8725 data_str[j] = *buffer++;
8726 data_str[j] = 0;
8727 if (*buffer == '\n')
8728 buffer++;
8729
8730 /* test if valid data */
8731 if (tid != TID_STRING && tid != TID_LINK) {
8732 if (data_str[0] == 0 || (strchr(data_str, '=')
8733 && strchr(data_str, ':')))
8734 buffer = pold;
8735 }
8736 }
8737 }
8738
8739 /* skip system client entries */
8740 mstrlcpy(test_str, key_name, sizeof(test_str));
8741 test_str[15] = 0;
8742
8743 if (!equal_ustring(test_str, "/System/Clients")) {
8744 if (root_key.type != TID_KEY) {
8745 /* root key is destination key */
8746 hKey = hKeyRoot;
8747 } else {
8748 /* create key and set value */
8749 if (key_name[0] == '/') {
8750 status = db_find_link(hDB, 0, key_name, &hKey);
8751 if (status == DB_NO_KEY) {
8752 db_create_key(hDB, 0, key_name, tid);
8753 status = db_find_link(hDB, 0, key_name, &hKey);
8754 }
8755 } else {
8756 status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
8757 if (status == DB_NO_KEY) {
8758 db_create_key(hDB, hKeyRoot, key_name, tid);
8759 status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
8760 }
8761 }
8762 }
8763
8764 /* set key data if created successfully */
8765 if (hKey) {
8766 if (tid == TID_STRING || tid == TID_LINK)
8767 db_set_link_data(hDB, hKey, data, string_length * n_data, n_data, tid);
8768 else
8769 db_set_link_data(hDB, hKey, data, rpc_tid_size(tid) * n_data, n_data, tid);
8770 }
8771 }
8772 }
8773 }
8774 }
8775 } while (TRUE);
8776
8777 free(data);
8778 return DB_SUCCESS;
8779}
8780
8781/********************************************************************/
8782/*
8783 Only internally used by db_paste_xml
8784*/
8786{
8787 int status;
8788
8789 if (strcmp(mxml_get_name(node), "odb") == 0) {
8790 for (int i = 0; i < mxml_get_number_of_children(node); i++) {
8792 if (status != DB_SUCCESS)
8793 return status;
8794 }
8795 } else if (strcmp(mxml_get_name(node), "dir") == 0) {
8796 const char* name = mxml_get_attribute(node, "name");
8797
8798 if (name == NULL) {
8799 cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no name in XML data", mxml_get_name(node));
8800 return DB_TYPE_MISMATCH;
8801 }
8802
8803 HNDLE hKey;
8805
8806 if (status == DB_NO_KEY) {
8808 if (status == DB_NO_ACCESS) {
8809 cm_msg(MINFO, "db_paste_node", "cannot load key \"%s\": write protected", name);
8810 return DB_SUCCESS; /* key or tree is locked, just skip it */
8811 }
8812
8813 if (status != DB_SUCCESS && status != DB_KEY_EXIST) {
8814 cm_msg(MERROR, "db_paste_node", "cannot create key \"%s\" in ODB, status = %d", name, status);
8815 return status;
8816 }
8818 if (status != DB_SUCCESS) {
8819 cm_msg(MERROR, "db_paste_node", "cannot find key \"%s\" in ODB", name);
8820 return status;
8821 }
8822 }
8823
8824 std::string path = db_get_path(hDB, hKey);
8825 if (!equal_ustring(path.c_str(), "/System/Clients")) {
8826 for (int i = 0; i < mxml_get_number_of_children(node); i++) {
8828 if (status != DB_SUCCESS)
8829 return status;
8830 }
8831 }
8832 } else if (strcmp(mxml_get_name(node), "key") == 0 || strcmp(mxml_get_name(node), "keyarray") == 0) {
8833
8834 const char* name = mxml_get_attribute(node, "name");
8835
8836 if (name == NULL) {
8837 cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no name in XML data", mxml_get_name(node));
8838 return DB_TYPE_MISMATCH;
8839 }
8840
8841 int num_values;
8842 if (strcmp(mxml_get_name(node), "keyarray") == 0)
8843 num_values = atoi(mxml_get_attribute(node, "num_values"));
8844 else
8845 num_values = 0;
8846
8847 const char* type = mxml_get_attribute(node, "type");
8848
8849 if (type == NULL) {
8850 cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no type in XML data", mxml_get_name(node));
8851 return DB_TYPE_MISMATCH;
8852 }
8853
8854 int tid = rpc_name_tid(type);
8855 if (tid == 0) {
8856 cm_msg(MERROR, "db_paste_node", "found unknown data type \"%s\" in XML data", type);
8857 return DB_TYPE_MISMATCH;
8858 }
8859
8860 HNDLE hKey;
8862 if (status == DB_NO_KEY) {
8864 if (status == DB_NO_ACCESS) {
8865 cm_msg(MINFO, "db_paste_node", "cannot load key \"%s\": write protected", name);
8866 return DB_SUCCESS; /* key or tree is locked, just skip it */
8867 }
8868
8869 if (status != DB_SUCCESS) {
8870 cm_msg(MERROR, "db_paste_node", "cannot create key \"%s\" in ODB, status = %d", name, status);
8871 return status;
8872 }
8874 if (status != DB_SUCCESS) {
8875 cm_msg(MERROR, "db_paste_node", "cannot find key \"%s\" in ODB, status = %d", name, status);
8876 return status;
8877 }
8878 } else {
8879 KEY key;
8881 if (status != DB_SUCCESS) {
8882 cm_msg(MERROR, "db_paste_node", "cannot get key \"%s\" in ODB, status = %d", name, status);
8883 return status;
8884 }
8885
8886 if (num_values > 0 && num_values != key.num_values && key.item_size > 0) {
8887 status = db_set_num_values(hDB, hKey, num_values);
8888 if (status != DB_SUCCESS) {
8889 cm_msg(MERROR, "db_paste_node", "cannot resize key \"%s\" in ODB, status = %d", name, status);
8890 return status;
8891 }
8892 }
8893 }
8894
8895 int size = 0;
8896 char *buf = NULL;
8897
8898 if (tid == TID_STRING || tid == TID_LINK) {
8899 size = atoi(mxml_get_attribute(node, "size"));
8900 buf = (char *)malloc(size);
8901 assert(buf);
8902 buf[0] = 0;
8903 }
8904
8905 if (num_values) {
8906 /* evaluate array */
8907 for (int i = 0; i < mxml_get_number_of_children(node); i++) {
8909 int idx;
8910 if (mxml_get_attribute(child, "index"))
8911 idx = atoi(mxml_get_attribute(child, "index"));
8912 else
8913 idx = i;
8914 if (tid == TID_STRING || tid == TID_LINK) {
8915 if (mxml_get_value(child) == NULL) {
8916 status = db_set_data_index(hDB, hKey, "", size, i, tid);
8917 if (status == DB_NO_ACCESS) {
8918 cm_msg(MINFO, "db_paste_node", "cannot load string or link \"%s\": write protected", mxml_get_attribute(node, "name"));
8919 return DB_SUCCESS; /* key or tree is locked, just skip it */
8920 } else if (status != DB_SUCCESS) {
8921 cm_msg(MERROR, "db_paste_node", "cannot load string or link \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
8922 return status;
8923 }
8924 } else {
8925 mstrlcpy(buf, mxml_get_value(child), size);
8926 status = db_set_data_index(hDB, hKey, buf, size, idx, tid);
8927 if (status == DB_NO_ACCESS) {
8928 cm_msg(MINFO, "db_paste_node", "cannot load array element \"%s\": write protected", mxml_get_attribute(node, "name"));
8929 return DB_SUCCESS; /* key or tree is locked, just skip it */
8930 } else if (status != DB_SUCCESS) {
8931 cm_msg(MERROR, "db_paste_node", "cannot load array element \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
8932 return status;
8933 }
8934 }
8935 } else {
8936 char data[256];
8937 db_sscanf(mxml_get_value(child), data, &size, 0, tid);
8939 if (status == DB_NO_ACCESS) {
8940 cm_msg(MINFO, "db_paste_node", "cannot load array element \"%s\": write protected", mxml_get_attribute(node, "name"));
8941 return DB_SUCCESS; /* key or tree is locked, just skip it */
8942 } else if (status != DB_SUCCESS) {
8943 cm_msg(MERROR, "db_paste_node", "cannot load array element \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
8944 return status;
8945 }
8946 }
8947 }
8948
8949 } else { /* single value */
8950 if (tid == TID_STRING || tid == TID_LINK) {
8951 size = atoi(mxml_get_attribute(node, "size"));
8952 if (mxml_get_value(node) == NULL) {
8953 status = db_set_data(hDB, hKey, "", size, 1, tid);
8954 if (status == DB_NO_ACCESS) {
8955 cm_msg(MINFO, "db_paste_node", "cannot load string or link \"%s\": write protected", mxml_get_attribute(node, "name"));
8956 return DB_SUCCESS; /* key or tree is locked, just skip it */
8957 } else if (status != DB_SUCCESS) {
8958 cm_msg(MERROR, "db_paste_node", "cannot load string or link \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
8959 return status;
8960 }
8961 } else {
8962 mstrlcpy(buf, mxml_get_value(node), size);
8963 status = db_set_data(hDB, hKey, buf, size, 1, tid);
8964 if (status == DB_NO_ACCESS) {
8965 cm_msg(MINFO, "db_paste_node", "cannot load value \"%s\": write protected", mxml_get_attribute(node, "name"));
8966 return DB_SUCCESS; /* key or tree is locked, just skip it */
8967 } else if (status != DB_SUCCESS) {
8968 cm_msg(MERROR, "db_paste_node", "cannot load value \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
8969 return status;
8970 }
8971 }
8972 } else {
8973 char data[256];
8974 db_sscanf(mxml_get_value(node), data, &size, 0, tid);
8975 status = db_set_data(hDB, hKey, data, rpc_tid_size(tid), 1, tid);
8976 if (status == DB_NO_ACCESS) {
8977 cm_msg(MINFO, "db_paste_node", "cannot load value \"%s\": write protected", mxml_get_attribute(node, "name"));
8978 return DB_SUCCESS; /* key or tree is locked, just skip it */
8979 } else if (status != DB_SUCCESS) {
8980 cm_msg(MERROR, "db_paste_node", "cannot load value \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
8981 return status;
8982 }
8983 }
8984 }
8985
8986 if (buf) {
8987 free(buf);
8988 buf = NULL;
8989 }
8990 }
8991
8992 return DB_SUCCESS;
8993}
8994
8995/********************************************************************/
9004{
9005 char error[256];
9006 INT status;
9008
9009 if (hKeyRoot == 0)
9011
9012 /* parse XML buffer */
9013 tree = mxml_parse_buffer(buffer, error, sizeof(error), NULL);
9014 if (tree == NULL) {
9015 puts(error);
9016 return DB_TYPE_MISMATCH;
9017 }
9018
9019 node = mxml_find_node(tree, "odb");
9020 if (node == NULL) {
9021 puts("Cannot find element \"odb\" in XML data");
9022 return DB_TYPE_MISMATCH;
9023 }
9024
9026
9028
9029 return status;
9030}
9031
9032/********************************************************************/
9042INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, int *buffer_size, bool header)
9043{
9044
9045 if (rpc_is_remote())
9046 return rpc_call(RPC_DB_COPY_XML, hDB, hKey, buffer, buffer_size, header);
9047
9048#ifdef LOCAL_ROUTINES
9049 {
9050 INT len;
9051 char *p;
9052 MXML_WRITER *writer;
9053
9054 /* open file */
9055 writer = mxml_open_buffer();
9056 if (writer == NULL) {
9057 cm_msg(MERROR, "db_copy_xml", "Cannot allocate buffer");
9058 return DB_NO_MEMORY;
9059 }
9060
9061 if (header) {
9062 std::string path = db_get_path(hDB, hKey);
9063
9064 /* write XML header */
9065 mxml_start_element(writer, "odb");
9066 mxml_write_attribute(writer, "root", path.c_str());
9067 mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
9068 mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", "http://midas.psi.ch/odb.xsd");
9069
9070 db_save_xml_key(hDB, hKey, 0, writer);
9071 mxml_end_element(writer); // "odb"
9072 } else {
9073 KEY key;
9074 int status = db_get_key(hDB, hKey, &key);
9075 if (status != DB_SUCCESS)
9076 return status;
9077
9078 db_save_xml_key(hDB, hKey, 1, writer);
9079 }
9080
9081 p = mxml_close_buffer(writer);
9082 len = strlen(p) + 1;
9083 if (len > *buffer_size) {
9084 free(p);
9085 *buffer_size = 0;
9086 return DB_TRUNCATED;
9087 }
9088
9089 mstrlcpy(buffer, p, len);
9090 free(p);
9091 *buffer_size = len;
9092 }
9093#endif /* LOCAL_ROUTINES */
9094
9095 return DB_SUCCESS;
9096}
9097
9099#ifndef DOXYGEN_SHOULD_SKIP_THIS
9100
9101/*------------------------------------------------------------------*/
9102void name2c(char *str)
9103/********************************************************************\
9104
9105 Routine: name2c
9106
9107 Purpose: Convert key name to C name. Internal use only.
9108
9109\********************************************************************/
9110{
9111 if (*str >= '0' && *str <= '9')
9112 *str = '_';
9113
9114 while (*str) {
9115 if (!(*str >= 'a' && *str <= 'z') && !(*str >= 'A' && *str <= 'Z') && !(*str >= '0' && *str <= '9'))
9116 *str = '_';
9117 *str = (char) tolower(*str);
9118 str++;
9119 }
9120}
9121
9122/*------------------------------------------------------------------*/
9124/********************************************************************\
9125
9126 Routine: db_save_tree_struct
9127
9128 Purpose: Save database tree as a C structure. Gets called by
9129 db_save_struct(). Internal use only.
9130
9131\********************************************************************/
9132{
9133 INT i, idx;
9134 KEY key;
9135 HNDLE hSubkey;
9136 int wr;
9137
9138 /* first enumerate this level */
9139 for (idx = 0;; idx++) {
9140 char name[MAX_ODB_PATH];
9141
9143 if (!hSubkey)
9144 break;
9145
9146 /* first get the name of the link, than the type of the link target */
9148 mstrlcpy(name, key.name, sizeof(name));
9150
9152
9153 if (key.type != TID_KEY) {
9154 char line[MAX_ODB_PATH];
9155 char str[MAX_ODB_PATH];
9156
9157 for (i = 0; i <= level; i++) {
9158 wr = write(hfile, " ", 2);
9159 assert(wr == 2);
9160 }
9161
9162 switch (key.type) {
9163 case TID_INT8:
9164 case TID_CHAR:
9165 strcpy(line, "char");
9166 break;
9167 case TID_INT16:
9168 strcpy(line, "short");
9169 break;
9170 case TID_FLOAT:
9171 strcpy(line, "float");
9172 break;
9173 case TID_DOUBLE:
9174 strcpy(line, "double");
9175 break;
9176 case TID_BITFIELD:
9177 strcpy(line, "unsigned char");
9178 break;
9179 case TID_STRING:
9180 strcpy(line, "char");
9181 break;
9182 case TID_LINK:
9183 strcpy(line, "char");
9184 break;
9185 default:
9186 strcpy(line, rpc_tid_name(key.type));
9187 break;
9188 }
9189
9190 mstrlcat(line, " ", sizeof(line));
9191 mstrlcpy(str, name, sizeof(str));
9192 name2c(str);
9193
9194 if (key.num_values > 1)
9195 sprintf(str + strlen(str), "[%d]", key.num_values);
9196 if (key.type == TID_STRING || key.type == TID_LINK)
9197 sprintf(str + strlen(str), "[%d]", key.item_size);
9198
9199 mstrlcpy(line + 10, str, sizeof(line) - 10);
9200 mstrlcat(line, ";\n", sizeof(line));
9201
9202 wr = write(hfile, line, strlen(line));
9203 assert(wr > 0);
9204 } else {
9205 char line[10+MAX_ODB_PATH];
9206 char str[MAX_ODB_PATH];
9207
9208 /* recurse subtree */
9209 for (i = 0; i <= level; i++) {
9210 wr = write(hfile, " ", 2);
9211 assert(wr == 2);
9212 }
9213
9214 sprintf(line, "struct {\n");
9215 wr = write(hfile, line, strlen(line));
9216 assert(wr > 0);
9218
9219 for (i = 0; i <= level; i++) {
9220 wr = write(hfile, " ", 2);
9221 assert(wr == 2);
9222 }
9223
9224 strcpy(str, name);
9225 name2c(str);
9226
9227 sprintf(line, "} %s;\n", str);
9228 wr = write(hfile, line, strlen(line));
9229 assert(wr > 0);
9230 }
9231 }
9232}
9233
9235#endif /* DOXYGEN_SHOULD_SKIP_THIS */
9236
9237/********************************************************************/
9250INT db_save(HNDLE hDB, HNDLE hKey, const char *filename, BOOL bRemote)
9251{
9252 if (rpc_is_remote() && bRemote)
9253 return rpc_call(RPC_DB_SAVE, hDB, hKey, filename, bRemote);
9254
9255#ifdef LOCAL_ROUTINES
9256 {
9257 INT hfile, size, buffer_size, n, status;
9258 char *buffer;
9259
9260 /* open file */
9261 hfile = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0644);
9262 if (hfile == -1) {
9263 cm_msg(MERROR, "db_save", "Cannot open file \"%s\"", filename);
9264 return DB_FILE_ERROR;
9265 }
9266
9267 std::string path = db_get_path(hDB, hKey);
9268
9269 buffer_size = 10000;
9270 do {
9271 buffer = (char *) malloc(buffer_size);
9272 if (buffer == NULL) {
9273 cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
9274 break;
9275 }
9276
9277 size = buffer_size;
9278 status = db_copy(hDB, hKey, buffer, &size, path.c_str());
9279 if (status != DB_TRUNCATED) {
9280 n = write(hfile, buffer, buffer_size - size);
9281 free(buffer);
9282 buffer = NULL;
9283
9284 if (n != buffer_size - size) {
9285 cm_msg(MERROR, "db_save", "cannot save .ODB file");
9286 close(hfile);
9287 return DB_FILE_ERROR;
9288 }
9289 break;
9290 }
9291
9292 /* increase buffer size if truncated */
9293 free(buffer);
9294 buffer = NULL;
9295 buffer_size *= 2;
9296 } while (1);
9297
9298 close(hfile);
9299
9300 }
9301#endif /* LOCAL_ROUTINES */
9302
9303 return DB_SUCCESS;
9304}
9305
9306/*------------------------------------------------------------------*/
9307
9308void xml_encode(char *src, int size)
9309{
9310 int i;
9311 char *dst, *p;
9312
9313 dst = (char *) malloc(size);
9314 if (dst == NULL)
9315 return;
9316
9317 *dst = 0;
9318 for (i = 0; i < (int) strlen(src); i++) {
9319 switch (src[i]) {
9320 case '<':
9321 mstrlcat(dst, "&lt;", size);
9322 break;
9323 case '>':
9324 mstrlcat(dst, "&gt;", size);
9325 break;
9326 case '&':
9327 mstrlcat(dst, "&amp;", size);
9328 break;
9329 case '\"':
9330 mstrlcat(dst, "&quot;", size);
9331 break;
9332 case '\'':
9333 mstrlcat(dst, "&apos;", size);
9334 break;
9335 default:
9336 if ((int) strlen(dst) >= size) {
9337 free(dst);
9338 return;
9339 }
9340 p = dst + strlen(dst);
9341 *p = src[i];
9342 *(p + 1) = 0;
9343 }
9344 }
9345
9346 mstrlcpy(src, dst, size);
9347}
9348
9349/*------------------------------------------------------------------*/
9350
9352{
9353 INT i, idx, size, status;
9354 char *data;
9355 HNDLE hSubkey;
9356 KEY key;
9357
9359 if (status != DB_SUCCESS)
9360 return status;
9361
9362 if (key.type == TID_KEY) {
9363
9364 /* save opening tag for subtree */
9365
9366 if (level > 0) {
9367 mxml_start_element(writer, "dir");
9368 mxml_write_attribute(writer, "name", key.name);
9369 mxml_write_attribute(writer, "handle", std::to_string(hKey).c_str());
9370 }
9371
9372 for (idx = 0;; idx++) {
9374
9375 if (!hSubkey)
9376 break;
9377
9378 /* save subtree */
9379 status = db_save_xml_key(hDB, hSubkey, level + 1, writer);
9380 if (status != DB_SUCCESS)
9381 return status;
9382 }
9383
9384 /* save closing tag for subtree */
9385 if (level > 0)
9386 mxml_end_element(writer);
9387
9388 } else {
9389 /* save key value */
9390
9391 if (key.num_values > 1)
9392 mxml_start_element(writer, "keyarray");
9393 else
9394 mxml_start_element(writer, "key");
9395 mxml_write_attribute(writer, "name", key.name);
9396 mxml_write_attribute(writer, "type", rpc_tid_name(key.type));
9397 mxml_write_attribute(writer, "handle", std::to_string(hKey).c_str());
9398
9399 if (key.type == TID_STRING || key.type == TID_LINK) {
9400 char str[256];
9401 sprintf(str, "%d", key.item_size);
9402 mxml_write_attribute(writer, "size", str);
9403 }
9404
9405 if (key.num_values > 1) {
9406 char str[256];
9407 sprintf(str, "%d", key.num_values);
9408 mxml_write_attribute(writer, "num_values", str);
9409 }
9410
9411 size = key.total_size;
9412 data = (char *) malloc(size+1); // an extra byte to zero-terminate strings
9413 if (data == NULL) {
9414 cm_msg(MERROR, "db_save_xml_key", "cannot allocate data buffer");
9415 return DB_NO_MEMORY;
9416 }
9417
9418 db_get_link_data(hDB, hKey, data, &size, key.type);
9419
9420 if (key.num_values == 1) {
9421 if (key.type == TID_STRING) {
9422 data[size] = 0; // make sure strings are NUL-terminated
9423 mxml_write_value(writer, data);
9424 } else {
9425 std::string str = db_sprintf(data, key.item_size, 0, key.type);
9427 std::string path = db_get_path(hDB, hKey);
9428 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());
9429 }
9430 mxml_write_value(writer, str.c_str());
9431 }
9432 mxml_end_element(writer);
9433
9434 } else { /* array of values */
9435
9436 for (i = 0; i < key.num_values; i++) {
9437
9438 mxml_start_element(writer, "value");
9439
9440 {
9441 char str[256];
9442 sprintf(str, "%d", i);
9443 mxml_write_attribute(writer, "index", str);
9444 }
9445
9446 if (key.type == TID_STRING) {
9447 char* p = data + i * key.item_size;
9448 p[key.item_size - 1] = 0; // make sure string is NUL-terminated
9449 //cm_msg(MINFO, "db_save_xml_key", "odb string array item_size %d, index %d length %d", key.item_size, i, (int)strlen(p));
9450 mxml_write_value(writer, p);
9451 } else {
9452 std::string str = db_sprintf(data, key.item_size, i, key.type);
9453 if ((key.type == TID_STRING) && (str.length() >= MAX_STRING_LENGTH-1)) {
9454 std::string path = db_get_path(hDB, hKey);
9455 cm_msg(MERROR, "db_save_xml_key", "Long odb string array probably truncated, odb path \"%s\"[%d]", path.c_str(), i);
9456 }
9457 mxml_write_value(writer, str.c_str());
9458 }
9459
9460 mxml_end_element(writer);
9461 }
9462
9463 mxml_end_element(writer); /* keyarray */
9464 }
9465
9466 free(data);
9467 data = NULL;
9468 }
9469
9470 return DB_SUCCESS;
9471}
9472
9473/********************************************************************/
9485INT db_save_xml(HNDLE hDB, HNDLE hKey, const char *filename)
9486{
9487#ifdef LOCAL_ROUTINES
9488 {
9489 INT status;
9490 MXML_WRITER *writer;
9491
9492 /* open file */
9493 writer = mxml_open_file(filename);
9494 if (writer == NULL) {
9495 cm_msg(MERROR, "db_save_xml", "Cannot open file \"%s\"", filename);
9496 return DB_FILE_ERROR;
9497 }
9498
9499 std::string path = db_get_path(hDB, hKey);
9500
9501 /* write XML header */
9502 mxml_start_element(writer, "odb");
9503 mxml_write_attribute(writer, "root", path.c_str());
9504 mxml_write_attribute(writer, "filename", filename);
9505 mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
9506
9507 std::string xsd_path;
9508
9509 if (getenv("MIDASSYS"))
9510 xsd_path = getenv("MIDASSYS");
9511 else
9512 xsd_path = ".";
9513
9515 xsd_path += "odb.xsd";
9516 mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", xsd_path.c_str());
9517
9518 status = db_save_xml_key(hDB, hKey, 0, writer);
9519
9520 mxml_end_element(writer); // "odb"
9521 mxml_close_file(writer);
9522
9523 return status;
9524 }
9525#endif /* LOCAL_ROUTINES */
9526
9527 return DB_SUCCESS;
9528}
9529
9530/*------------------------------------------------------------------*/
9531
9532void json_write(char **buffer, int* buffer_size, int* buffer_end, int level, const char* s, int quoted)
9533{
9534 int len, remain, xlevel;
9535
9536 len = strlen(s);
9537 remain = *buffer_size - *buffer_end;
9538 assert(remain >= 0);
9539
9540 xlevel = 2*level;
9541
9542 while (10 + xlevel + 3*len > remain) {
9543 // reallocate the buffer
9544 int new_buffer_size = 2*(*buffer_size);
9545 if (new_buffer_size < 4*1024)
9546 new_buffer_size = 4*1024;
9547 //printf("reallocate: len %d, size %d, remain %d, allocate %d\n", len, *buffer_size, remain, new_buffer_size);
9548 assert(new_buffer_size > *buffer_size);
9549 *buffer = (char *)realloc(*buffer, new_buffer_size);
9550 assert(*buffer);
9551 *buffer_size = new_buffer_size;
9552 remain = *buffer_size - *buffer_end;
9553 assert(remain >= 0);
9554 }
9555
9556 if (xlevel) {
9557 int i;
9558 for (i=0; i<xlevel; i++)
9559 (*buffer)[(*buffer_end)++] = ' ';
9560 }
9561
9562 if (!quoted) {
9563 memcpy(*buffer + *buffer_end, s, len);
9564 *buffer_end += len;
9565 (*buffer)[*buffer_end] = 0; // NUL-terminate the buffer
9566 return;
9567 }
9568
9569 char *bufptr = *buffer;
9570 int bufend = *buffer_end;
9571
9572 bufptr[bufend++] = '"';
9573
9574 while (*s) {
9575 switch (*s) {
9576 case '\"':
9577 bufptr[bufend++] = '\\';
9578 bufptr[bufend++] = '\"';
9579 s++;
9580 break;
9581 case '\\':
9582 bufptr[bufend++] = '\\';
9583 bufptr[bufend++] = '\\';
9584 s++;
9585 break;
9586#if 0
9587 case '/':
9588 bufptr[bufend++] = '\\';
9589 bufptr[bufend++] = '/';
9590 s++;
9591 break;
9592#endif
9593 case '\b':
9594 bufptr[bufend++] = '\\';
9595 bufptr[bufend++] = 'b';
9596 s++;
9597 break;
9598 case '\f':
9599 bufptr[bufend++] = '\\';
9600 bufptr[bufend++] = 'f';
9601 s++;
9602 break;
9603 case '\n':
9604 bufptr[bufend++] = '\\';
9605 bufptr[bufend++] = 'n';
9606 s++;
9607 break;
9608 case '\r':
9609 bufptr[bufend++] = '\\';
9610 bufptr[bufend++] = 'r';
9611 s++;
9612 break;
9613 case '\t':
9614 bufptr[bufend++] = '\\';
9615 bufptr[bufend++] = 't';
9616 s++;
9617 break;
9618 default:
9619 bufptr[bufend++] = *s++;
9620 }
9621 }
9622
9623 bufptr[bufend++] = '"';
9624 bufptr[bufend] = 0; // NUL-terminate the buffer
9625
9626 *buffer_end = bufend;
9627
9628 remain = *buffer_size - *buffer_end;
9629 assert(remain > 0);
9630}
9631
9633{
9634 // sprintf "%.2e" can result in strings like "1,23e6" if
9635 // the user's locale has a comma for the decimal spearator.
9636 // However the JSON RFC requires a dot for the decimal.
9637 // This approach of "sprintf-then-replace" is much safer than
9638 // changing the user's locale, and much faster than the C++
9639 // approach of using a stringstream that we "imbue" with a C locale.
9640 char* comma = strchr(str, ',');
9641
9642 if (comma != nullptr) {
9643 *comma = '.';
9644 }
9645}
9646
9647static void json_write_data(char **buffer, int* buffer_size, int* buffer_end, int level, const KEY* key, const char* p)
9648{
9649 char str[256];
9650 switch (key->type) {
9651 case TID_UINT8:
9652 sprintf(str, "%u", *(unsigned char*)p);
9653 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9654 break;
9655 case TID_INT8:
9656 sprintf(str, "%d", *(char*)p);
9657 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9658 break;
9659 case TID_CHAR:
9660 sprintf(str, "%c", *(char*)p);
9661 json_write(buffer, buffer_size, buffer_end, 0, str, 1);
9662 break;
9663 case TID_UINT16:
9664 sprintf(str, "\"0x%04x\"", *(WORD*)p);
9665 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9666 break;
9667 case TID_INT16:
9668 sprintf(str, "%d", *(short*)p);
9669 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9670 break;
9671 case TID_UINT32:
9672 sprintf(str, "\"0x%08x\"", *(DWORD*)p);
9673 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9674 break;
9675 case TID_INT32:
9676 sprintf(str, "%d", *(int*)p);
9677 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9678 break;
9679 case TID_UINT64:
9680 // encode as hex string
9681 sprintf(str, "\"0x%016llx\"", *(UINT64*)p);
9682 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9683 break;
9684 case TID_INT64:
9685 // encode as decimal string
9686 sprintf(str, "\"%lld\"", *(INT64*)p);
9687 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9688 break;
9689 case TID_BOOL:
9690 if (*(int*)p)
9691 json_write(buffer, buffer_size, buffer_end, 0, "true", 0);
9692 else
9693 json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
9694 break;
9695 case TID_FLOAT: {
9696 float flt = (*(float*)p);
9697 if (isnan(flt))
9698 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
9699 else if (isinf(flt)) {
9700 if (flt > 0)
9701 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
9702 else
9703 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
9704 } else if (flt == 0)
9705 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
9706 else if (flt == (int)flt) {
9707 sprintf(str, "%.0f", flt);
9709 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9710 } else {
9711 sprintf(str, "%.7e", flt);
9713 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9714 }
9715 break;
9716 }
9717 case TID_DOUBLE: {
9718 double dbl = (*(double*)p);
9719 if (isnan(dbl))
9720 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
9721 else if (isinf(dbl)) {
9722 if (dbl > 0)
9723 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
9724 else
9725 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
9726 } else if (dbl == 0)
9727 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
9728 else if (dbl == (int)dbl) {
9729 sprintf(str, "%.0f", dbl);
9731 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9732 } else {
9733 sprintf(str, "%.16e", dbl);
9735 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9736 }
9737 break;
9738 }
9739 case TID_BITFIELD:
9740 json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
9741 break;
9742 case TID_STRING:
9743 // data is already NUL terminated // p[key.item_size-1] = 0; // make sure string is NUL terminated!
9744 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
9745 break;
9746 case TID_ARRAY:
9747 json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
9748 break;
9749 case TID_STRUCT:
9750 json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
9751 break;
9752 case TID_KEY:
9753 json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
9754 break;
9755 case TID_LINK:
9756 // data is already NUL terminated // p[key.item_size-1] = 0; // make sure string is NUL terminated!
9757 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
9758 break;
9759 default:
9760 json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
9761 }
9762}
9763
9764static void json_write_key(HNDLE hDB, HNDLE hKey, const KEY* key, const char* link_path, char **buffer, int* buffer_size, int* buffer_end)
9765{
9766 char str[256]; // not used to store anything long, only numeric values like: "item_size: 100"
9767
9768 json_write(buffer, buffer_size, buffer_end, 0, "{ ", 0);
9769
9770 sprintf(str, "\"type\" : %d", key->type);
9771 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9772
9773 if (link_path) {
9774 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9775 json_write(buffer, buffer_size, buffer_end, 0, "link", 1);
9776 json_write(buffer, buffer_size, buffer_end, 0, ": ", 0);
9777 json_write(buffer, buffer_size, buffer_end, 0, link_path, 1);
9778 }
9779
9780 if (key->num_values > 1) {
9781 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9782
9783 sprintf(str, "\"num_values\" : %d", key->num_values);
9784 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9785 }
9786
9787 if (key->type == TID_STRING) {
9788 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9789
9790 sprintf(str, "\"item_size\" : %d", key->item_size);
9791 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9792 }
9793
9794 if (key->notify_count > 0) {
9795 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9796
9797 sprintf(str, "\"notify_count\" : %d", key->notify_count);
9798 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9799 }
9800
9801 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9802
9803 sprintf(str, "\"access_mode\" : %d", key->access_mode);
9804 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9805
9806 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9807
9808 sprintf(str, "\"last_written\" : %d", key->last_written);
9809 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9810
9811 json_write(buffer, buffer_size, buffer_end, 0, " ", 0);
9812
9813 json_write(buffer, buffer_size, buffer_end, 0, "}", 0);
9814}
9815
9816static 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)
9817{
9818 INT i, size, status;
9819 char *data;
9820 KEY key;
9821 KEY link_key;
9822 char link_path[MAX_ODB_PATH];
9823 int omit_top_level_braces = 0;
9824
9825 //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);
9826
9827 if (level < 0) {
9828 level = 0;
9830 }
9831
9833
9834 if (status != DB_SUCCESS)
9835 return status;
9836
9837 link_key = key;
9838
9839 if (key.type == TID_LINK) {
9840 size = sizeof(link_path);
9842
9843 if (status != DB_SUCCESS)
9844 return status;
9845
9846 if (follow_links) {
9848
9849 if (status != DB_SUCCESS)
9850 return status;
9851
9853
9854 if (status != DB_SUCCESS)
9855 return status;
9856 }
9857 }
9858
9859 //printf("key [%s] link [%s], type %d, link %d\n", key.name, link_key.name, key.type, link_key.type);
9860
9861 if (key.type == TID_KEY && (recurse || level<=0)) {
9862 int idx = 0;
9863 int do_close_curly_bracket = 0;
9864
9865 if (level == 0 && !omit_top_level_braces) {
9866 json_write(buffer, buffer_size, buffer_end, 0, "{\n", 0);
9868 }
9869 else if (level > 0) {
9870 json_write(buffer, buffer_size, buffer_end, level, link_key.name, 1);
9871 json_write(buffer, buffer_size, buffer_end, 0, " : {\n", 0);
9873 }
9874
9875 if (level > 100) {
9876 std::string path = db_get_path(hDB, hKey);
9877
9878 json_write(buffer, buffer_size, buffer_end, 0, "/error", 1);
9879 json_write(buffer, buffer_size, buffer_end, 0, " : ", 0);
9880 json_write(buffer, buffer_size, buffer_end, 0, "max nesting level exceed", 1);
9881
9882 cm_msg(MERROR, "db_save_json_key", "max nesting level exceeded at \"%s\", check for symlink loops in this subtree", path.c_str());
9883
9884 } else {
9885 HNDLE hSubkey;
9886
9887 for (;; idx++) {
9889
9890 if (!hSubkey)
9891 break;
9892
9893 if (idx != 0) {
9894 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
9895 }
9896
9897 /* save subtree */
9899 if (status != DB_SUCCESS)
9900 return status;
9901 }
9902 }
9903
9905 if (idx > 0)
9906 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
9907 json_write(buffer, buffer_size, buffer_end, level, "}", 0);
9908 }
9909
9910 } else {
9911
9912 if (save_keys && level == 0) {
9913 json_write(buffer, buffer_size, buffer_end, 0, "{\n", 0);
9914 }
9915
9916 /* save key value */
9917
9918 if (save_keys == 1) {
9919 char str[NAME_LENGTH+15];
9920 sprintf(str, "%s/key", link_key.name);
9921
9922 json_write(buffer, buffer_size, buffer_end, level, str, 1);
9923 json_write(buffer, buffer_size, buffer_end, 0, " : { ", 0);
9924
9925 sprintf(str, "\"type\" : %d", key.type);
9926 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9927
9928 if (link_key.type == TID_LINK && follow_links) {
9929 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9930 json_write(buffer, buffer_size, buffer_end, 0, "link", 1);
9931 json_write(buffer, buffer_size, buffer_end, 0, ": ", 0);
9932 json_write(buffer, buffer_size, buffer_end, 0, link_path, 1);
9933 }
9934
9935 if (key.num_values > 1) {
9936 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9937
9938 sprintf(str, "\"num_values\" : %d", key.num_values);
9939 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9940 }
9941
9942 if (key.type == TID_STRING || key.type == TID_LINK) {
9943 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9944
9945 sprintf(str, "\"item_size\" : %d", key.item_size);
9946 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9947 }
9948
9949 if (key.notify_count > 0) {
9950 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9951
9952 sprintf(str, "\"notify_count\" : %d", key.notify_count);
9953 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9954 }
9955
9956 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9957
9958 sprintf(str, "\"access_mode\" : %d", key.access_mode);
9959 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9960
9961 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9962
9963 sprintf(str, "\"last_written\" : %d", key.last_written);
9964 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9965
9966 json_write(buffer, buffer_size, buffer_end, 0, " ", 0);
9967
9968 json_write(buffer, buffer_size, buffer_end, 0, "}", 0);
9969
9970 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
9971 }
9972
9973 if (save_keys == 2) {
9974 char str[NAME_LENGTH+15];
9975 sprintf(str, "%s/last_written", link_key.name);
9976
9977 json_write(buffer, buffer_size, buffer_end, level, str, 1);
9978
9979 sprintf(str, " : %d", key.last_written);
9980 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9981
9982 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
9983 }
9984
9985 if (save_keys) {
9986 json_write(buffer, buffer_size, buffer_end, level, link_key.name, 1);
9987 json_write(buffer, buffer_size, buffer_end, 0, " : ", 0);
9988 }
9989
9990 if (key.num_values > 1) {
9991 json_write(buffer, buffer_size, buffer_end, 0, "[ ", 0);
9992 }
9993
9994 size = key.total_size;
9995 data = (char *) malloc(size);
9996 if (data == NULL) {
9997 cm_msg(MERROR, "db_save_json_key", "cannot allocate data buffer for %d bytes", size);
9998 return DB_NO_MEMORY;
9999 }
10000
10001 if (key.type != TID_KEY) {
10002 if (follow_links)
10003 status = db_get_data(hDB, hKey, data, &size, key.type);
10004 else
10006
10007 if (status != DB_SUCCESS)
10008 return status;
10009 }
10010
10011 for (i = 0; i < key.num_values; i++) {
10012 char str[256];
10013 char *p = data + key.item_size*i;
10014
10015 if (i != 0)
10016 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10017
10018 switch (key.type) {
10019 case TID_UINT8:
10020 sprintf(str, "%u", *(unsigned char*)p);
10021 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10022 break;
10023 case TID_INT8:
10024 sprintf(str, "%d", *(char*)p);
10025 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10026 break;
10027 case TID_CHAR:
10028 sprintf(str, "%c", *(char*)p);
10029 json_write(buffer, buffer_size, buffer_end, 0, str, 1);
10030 break;
10031 case TID_UINT16:
10032 sprintf(str, "\"0x%04x\"", *(WORD*)p);
10033 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10034 break;
10035 case TID_INT16:
10036 sprintf(str, "%d", *(short*)p);
10037 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10038 break;
10039 case TID_UINT32:
10040 sprintf(str, "\"0x%08x\"", *(DWORD*)p);
10041 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10042 break;
10043 case TID_INT32:
10044 sprintf(str, "%d", *(int*)p);
10045 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10046 break;
10047 case TID_UINT64:
10048 sprintf(str, "\"0x%08llx\"", *(UINT64*)p);
10049 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10050 break;
10051 case TID_INT64:
10052 sprintf(str, "%lld", *(INT64*)p);
10053 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10054 break;
10055 case TID_BOOL:
10056 if (*(int*)p)
10057 json_write(buffer, buffer_size, buffer_end, 0, "true", 0);
10058 else
10059 json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
10060 break;
10061 case TID_FLOAT: {
10062 float flt = (*(float*)p);
10063 if (isnan(flt))
10064 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
10065 else if (isinf(flt)) {
10066 if (flt > 0)
10067 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
10068 else
10069 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
10070 } else if (flt == 0)
10071 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10072 else if (flt == (int)flt) {
10073 sprintf(str, "%.0f", flt);
10074 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10075 } else {
10076 sprintf(str, "%.7e", flt);
10077 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10078 }
10079 break;
10080 }
10081 case TID_DOUBLE: {
10082 double dbl = (*(double*)p);
10083 if (isnan(dbl))
10084 json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
10085 else if (isinf(dbl)) {
10086 if (dbl > 0)
10087 json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
10088 else
10089 json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
10090 } else if (dbl == 0)
10091 json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10092 else if (dbl == (int)dbl) {
10093 sprintf(str, "%.0f", dbl);
10094 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10095 } else {
10096 sprintf(str, "%.16e", dbl);
10097 json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10098 }
10099 break;
10100 }
10101 case TID_BITFIELD:
10102 json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
10103 break;
10104 case TID_STRING:
10105 p[key.item_size-1] = 0; // make sure string is NUL terminated!
10106 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
10107 break;
10108 case TID_ARRAY:
10109 json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
10110 break;
10111 case TID_STRUCT:
10112 json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
10113 break;
10114 case TID_KEY:
10115 json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
10116 break;
10117 case TID_LINK:
10118 p[key.item_size-1] = 0; // make sure string is NUL terminated!
10119 json_write(buffer, buffer_size, buffer_end, 0, p, 1);
10120 break;
10121 default:
10122 json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
10123 }
10124
10125 }
10126
10127 if (key.num_values > 1) {
10128 json_write(buffer, buffer_size, buffer_end, 0, " ]", 0);
10129 } else {
10130 json_write(buffer, buffer_size, buffer_end, 0, "", 0);
10131 }
10132
10133 free(data);
10134 data = NULL;
10135
10136 if (save_keys && level == 0) {
10137 json_write(buffer, buffer_size, buffer_end, 0, "\n}", 0);
10138 }
10139 }
10140
10141 return DB_SUCCESS;
10142}
10143
10144/********************************************************************/
10155INT db_copy_json_array(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
10156{
10157 int size, asize;
10158 int status;
10159 char* data;
10160 int i;
10161 KEY key;
10162
10164 if (status != DB_SUCCESS)
10165 return status;
10166
10167 assert(key.type != TID_KEY);
10168
10169 if (key.num_values > 1) {
10170 json_write(buffer, buffer_size, buffer_end, 0, "[ ", 0);
10171 }
10172
10173 size = key.total_size;
10174
10175 asize = size;
10176 if (asize < 1024)
10177 asize = 1024;
10178
10179 data = (char *) malloc(asize);
10180 if (data == NULL) {
10181 cm_msg(MERROR, "db_save_json_key_data", "cannot allocate data buffer for %d bytes", asize);
10182 return DB_NO_MEMORY;
10183 }
10184
10185 data[0] = 0; // protect against TID_STRING that has key.total_size == 0.
10186
10187 status = db_get_data(hDB, hKey, data, &size, key.type);
10188 if (status != DB_SUCCESS) {
10189 free(data);
10190 return status;
10191 }
10192
10193 for (i = 0; i < key.num_values; i++) {
10194 char *p = data + key.item_size*i;
10195
10196 if (i != 0)
10197 json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10198
10199 json_write_data(buffer, buffer_size, buffer_end, 0, &key, p);
10200 }
10201
10202 if (key.num_values > 1) {
10203 json_write(buffer, buffer_size, buffer_end, 0, " ]", 0);
10204 }
10205
10206 free(data);
10207 data = NULL;
10208
10209 return DB_SUCCESS;
10210}
10211
10212/********************************************************************/
10224INT db_copy_json_index(HNDLE hDB, HNDLE hKey, int index, char **buffer, int* buffer_size, int* buffer_end)
10225{
10226 int status;
10227 KEY key;
10228
10230
10231 if (status != DB_SUCCESS)
10232 return status;
10233
10234 int size = key.item_size;
10235 char* data = (char*)malloc(size + 1); // extra byte for string NUL termination
10236 assert(data != NULL);
10237
10239
10240 if (status != DB_SUCCESS) {
10241 free(data);
10242 return status;
10243 }
10244
10245 assert(size <= key.item_size);
10246 data[key.item_size] = 0; // make sure data is NUL terminated, in case of strings.
10247
10248 json_write_data(buffer, buffer_size, buffer_end, 0, &key, data);
10249
10250 free(data);
10251
10252 return DB_SUCCESS;
10253}
10254
10255static 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)
10256{
10257 int status;
10258 char link_buf[MAX_ODB_PATH]; // link_path points to link_buf
10259 char* link_path = NULL;
10260
10262
10263 if (link.type == TID_LINK) {
10264 int size = sizeof(link_buf);
10266 if (status != DB_SUCCESS)
10267 return status;
10268
10269 //printf("link size %d, len %d, data [%s]\n", size, (int)strlen(link_buf), link_buf);
10270
10271 if ((size > 0) && (strlen(link_buf) > 0)) {
10273
10274 // resolve the link, unless it is a link to an array element
10275 if ((flags & JSFLAG_FOLLOW_LINKS) && strchr(link_path, '[') == NULL) {
10277 if (status != DB_SUCCESS) {
10278 // dangling link to nowhere
10280 }
10281 }
10282 }
10283 }
10284
10287 if (status != DB_SUCCESS)
10288 return status;
10289
10290 if (flags & JSFLAG_OMIT_OLD) {
10291 if (link_target.last_written) {
10292 if (link_target.last_written < timestamp) {
10293 // omit old data
10294 return DB_SUCCESS;
10295 }
10296 }
10297 }
10298
10299 if (need_comma) {
10300 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10301 } else {
10302 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10303 }
10304
10305 char link_name[MAX_ODB_PATH];
10306
10307 mstrlcpy(link_name, link.name, sizeof(link_name));
10308
10309 if (flags & JSFLAG_LOWERCASE) {
10310 for (int i=0; (i<(int)sizeof(link.name)) && link.name[i]; i++)
10311 link_name[i] = tolower(link.name[i]);
10312 }
10313
10314 if ((flags & JSFLAG_LOWERCASE) && !(flags & JSFLAG_OMIT_NAMES)) {
10315 char buf[MAX_ODB_PATH];
10316 mstrlcpy(buf, link_name, sizeof(buf));
10317 mstrlcat(buf, "/name", sizeof(buf));
10318 json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10319 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10320 json_write(buffer, buffer_size, buffer_end, 0, link.name, 1);
10321 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10322 }
10323
10324 if (link.type != TID_KEY && (flags & JSFLAG_SAVE_KEYS)) {
10325 char buf[MAX_ODB_PATH];
10326 mstrlcpy(buf, link_name, sizeof(buf));
10327 mstrlcat(buf, "/key", sizeof(buf));
10328 json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10329 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10330 json_write_key(hDB, hLink, &link_target, link_path, buffer, buffer_size, buffer_end);
10331 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10332 } else if ((link_target.type != TID_KEY) && !(flags & JSFLAG_OMIT_LAST_WRITTEN)) {
10333 char buf[MAX_ODB_PATH];
10334 mstrlcpy(buf, link_name, sizeof(buf));
10335 mstrlcat(buf, "/last_written", sizeof(buf));
10336 json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10337 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10338 sprintf(buf, "%d", link_target.last_written);
10339 json_write(buffer, buffer_size, buffer_end, 0, buf, 0);
10340 json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10341 }
10342
10343 json_write(buffer, buffer_size, buffer_end, level, link_name, 1);
10344 json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10345
10346 if (link_target.type == TID_KEY && !(flags & JSFLAG_RECURSE)) {
10347 json_write(buffer, buffer_size, buffer_end, 0, "{ }" , 0);
10348 } else {
10349 status = json_write_anything(hDB, hLinkTarget, buffer, buffer_size, buffer_end, level, 0, flags, timestamp);
10350 if (status != DB_SUCCESS)
10351 return status;
10352 }
10353
10354 return DB_SUCCESS;
10355}
10356
10357int json_write_bare_subdir(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end, int level, int flags, time_t timestamp)
10358{
10359 if (level > MAX_ODB_PATH/2) {
10360 // max nesting level is limited by max odb path, where each subdirectory takes
10361 // at least 2 bytes - 1 byte directory name and 1 byte for "/"
10362 cm_msg(MERROR, "json_write_bare_subdir", "Max ODB subdirectory nesting level exceeded %d", level);
10363 return DB_TRUNCATED;
10364 }
10365
10366 for (int i=0; ; i++) {
10367 int status;
10368
10369 HNDLE hLink;
10371 if (status != DB_SUCCESS && !hLink)
10372 break;
10373
10374 KEY link;
10376 if (status != DB_SUCCESS)
10377 return status;
10378
10379 bool need_comma = (i!=0);
10380
10381 status = json_write_bare_key(hDB, hLink, link, buffer, buffer_size, buffer_end, level, flags, timestamp, need_comma);
10382 if (status != DB_SUCCESS)
10383 return status;
10384 }
10385
10386 return DB_SUCCESS;
10387}
10388
10389int 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)
10390{
10391 int status;
10392 KEY key;
10393
10395 if (status != DB_SUCCESS)
10396 return status;
10397
10398 if (key.type == TID_KEY) {
10399
10400 json_write(buffer, buffer_size, buffer_end, 0, "{", 0);
10401
10402 status = json_write_bare_subdir(hDB, hKey, buffer, buffer_size, buffer_end, level+1, flags, timestamp);
10403 if (status != DB_SUCCESS)
10404 return status;
10405
10406 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10407 json_write(buffer, buffer_size, buffer_end, level, "}", 0);
10408
10409 } else if (must_be_subdir) {
10410 json_write(buffer, buffer_size, buffer_end, 0, "{", 0);
10411 status = json_write_bare_key(hDB, hKey, key, buffer, buffer_size, buffer_end, level+1, flags, timestamp, false);
10412 if (status != DB_SUCCESS)
10413 return status;
10414 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10415 json_write(buffer, buffer_size, buffer_end, level, "}", 0);
10416 } else {
10417 status = db_copy_json_array(hDB, hKey, buffer, buffer_size, buffer_end);
10418
10419 if (status != DB_SUCCESS)
10420 return status;
10421 }
10422
10423 return DB_SUCCESS;
10424}
10425
10426INT db_copy_json_ls(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
10427{
10428 int status;
10429 bool unlock = false;
10430
10431 if (!rpc_is_remote()) {
10433 if (status != DB_SUCCESS) {
10434 return status;
10435 }
10436 unlock = true;
10437 }
10438
10440
10441 if (unlock) {
10443 }
10444
10445 return status;
10446}
10447
10449{
10450 int status;
10452 if (omit_names)
10453 flags |= JSFLAG_OMIT_NAMES;
10455 flags |= JSFLAG_OMIT_LAST_WRITTEN;
10457 flags |= JSFLAG_OMIT_OLD;
10458 if (!preserve_case)
10459 flags |= JSFLAG_LOWERCASE;
10460
10461 bool unlock = false;
10462
10463 if (!rpc_is_remote()) {
10465 if (status != DB_SUCCESS) {
10466 return status;
10467 }
10468 unlock = true;
10469 }
10470
10471 status = json_write_anything(hDB, hKey, buffer, buffer_size, buffer_end, JS_LEVEL_0, 0, flags, omit_old_timestamp);
10472
10473 if (unlock) {
10475 }
10476
10477 return status;
10478}
10479
10480INT db_copy_json_save(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
10481{
10482 int status;
10483 bool unlock = false;
10484
10485 if (!rpc_is_remote()) {
10487 if (status != DB_SUCCESS) {
10488 return status;
10489 }
10490 unlock = true;
10491 }
10492
10494
10495 if (unlock) {
10497 }
10498
10499 return status;
10500}
10501
10502/********************************************************************/
10513INT db_copy_json_obsolete(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end, int save_keys, int follow_links, int recurse)
10514{
10516 json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10517 return DB_SUCCESS;
10518}
10519
10520/********************************************************************/
10532INT db_save_json(HNDLE hDB, HNDLE hKey, const char *filename, int flags)
10533{
10534 INT status;
10535
10536 /* open file */
10537 FILE *fp = fopen(filename, "w");
10538 if (fp == NULL) {
10539 cm_msg(MERROR, "db_save_json", "Cannot open file \"%s\", fopen() errno %d (%s)", filename, errno, strerror(errno));
10540 return DB_FILE_ERROR;
10541 }
10542
10543 bool unlock = false;
10544
10545 if (!rpc_is_remote()) {
10547 if (status != DB_SUCCESS) {
10548 fclose(fp);
10549 return status;
10550 }
10551 unlock = true;
10552 }
10553
10554 std::string path = db_get_path(hDB, hKey);
10555
10556 bool emptySubdir = false;
10557 HNDLE hSubKey;
10560 emptySubdir = true;
10561
10562 char* buffer = NULL;
10563 int buffer_size = 0;
10564 int buffer_end = 0;
10565
10566 json_write(&buffer, &buffer_size, &buffer_end, 0, "{\n", 0);
10567
10568 json_write(&buffer, &buffer_size, &buffer_end, 1, "/MIDAS version", 1);
10569 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10570 json_write(&buffer, &buffer_size, &buffer_end, 0, MIDAS_VERSION, 1);
10571 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10572
10573 json_write(&buffer, &buffer_size, &buffer_end, 1, "/MIDAS git revision", 1);
10574 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10575 json_write(&buffer, &buffer_size, &buffer_end, 0, GIT_REVISION, 1);
10576 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10577
10578 json_write(&buffer, &buffer_size, &buffer_end, 1, "/filename", 1);
10579 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10580 json_write(&buffer, &buffer_size, &buffer_end, 0, filename, 1);
10581 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10582
10583 json_write(&buffer, &buffer_size, &buffer_end, 1, "/ODB path", 1);
10584 json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10585 json_write(&buffer, &buffer_size, &buffer_end, 0, path.c_str(), 1);
10586
10587 if (emptySubdir)
10588 json_write(&buffer, &buffer_size, &buffer_end, 0, "", 0);
10589 else
10590 json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10591
10592 //status = db_save_json_key_obsolete(hDB, hKey, -1, &buffer, &buffer_size, &buffer_end, 1, 0, 1);
10593 status = json_write_bare_subdir(hDB, hKey, &buffer, &buffer_size, &buffer_end, JS_LEVEL_1, flags, 0);
10594
10595 json_write(&buffer, &buffer_size, &buffer_end, 0, "\n}\n", 0);
10596
10597 if (unlock) {
10599 }
10600
10601 if (status == DB_SUCCESS) {
10602 if (buffer) {
10603 size_t wr = fwrite(buffer, 1, buffer_end, fp);
10604 if (wr != (size_t)buffer_end) {
10605 cm_msg(MERROR, "db_save_json", "Cannot write to file \"%s\", fwrite() errno %d (%s)", filename, errno, strerror(errno));
10606 free(buffer);
10607 fclose(fp);
10608 return DB_FILE_ERROR;
10609 }
10610 }
10611 }
10612
10613 if (buffer)
10614 free(buffer);
10615
10616 fclose(fp);
10617
10618 return DB_SUCCESS;
10619}
10620
10621/********************************************************************/
10633{
10634 KEY key;
10635 char str[100], line[10+100];
10636 INT status, i, fh;
10637 int wr, size;
10638
10639 /* open file */
10640 fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
10641
10642 if (fh == -1) {
10643 cm_msg(MERROR, "db_save_struct", "Cannot open file\"%s\"", file_name);
10644 return DB_FILE_ERROR;
10645 }
10646
10648 if (status != DB_SUCCESS) {
10649 cm_msg(MERROR, "db_save_struct", "cannot find key");
10650 return DB_INVALID_HANDLE;
10651 }
10652
10653 sprintf(line, "typedef struct {\n");
10654
10655 size = strlen(line);
10656 wr = write(fh, line, size);
10657 if (wr != size) {
10658 cm_msg(MERROR, "db_save_struct", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10659 close(fh);
10660 return DB_FILE_ERROR;
10661 }
10662
10664
10665 if (struct_name && struct_name[0])
10666 mstrlcpy(str, struct_name, sizeof(str));
10667 else
10668 mstrlcpy(str, key.name, sizeof(str));
10669
10670 name2c(str);
10671 for (i = 0; i < (int) strlen(str); i++)
10672 str[i] = (char) toupper(str[i]);
10673
10674 sprintf(line, "} %s;\n\n", str);
10675
10676 size = strlen(line);
10677 wr = write(fh, line, size);
10678 if (wr != size) {
10679 cm_msg(MERROR, "db_save_struct", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10680 close(fh);
10681 return DB_FILE_ERROR;
10682 }
10683
10684 close(fh);
10685
10686 return DB_SUCCESS;
10687}
10688
10690#ifndef DOXYGEN_SHOULD_SKIP_THIS
10691
10692/*------------------------------------------------------------------*/
10694/********************************************************************\
10695
10696 Routine: db_save_string
10697
10698 Purpose: Save a branch of a database as a string which can be used
10699 by db_create_record.
10700
10701 Input:
10702 HNDLE hDB Handle to the database
10703 HNDLE hKey Handle of key to start, 0 for root
10704 int fh File handle to write to
10705 char string_name Name of string. If struct_name == NULL,
10706 the name of the key is used.
10707
10708 Output:
10709 none
10710
10711 Function value:
10712 DB_SUCCESS Successful completion
10713 DB_INVALID_HANDLE Database handle is invalid
10714
10715\********************************************************************/
10716{
10717 KEY key;
10718 char str[256], line[50+256];
10719 INT status, i, size, fh, buffer_size;
10720 char *buffer = NULL, *pc;
10721 int wr;
10722
10723
10724 /* open file */
10725 fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
10726
10727 if (fh == -1) {
10728 cm_msg(MERROR, "db_save_string", "Cannot open file\"%s\"", file_name);
10729 return DB_FILE_ERROR;
10730 }
10731
10733 if (status != DB_SUCCESS) {
10734 cm_msg(MERROR, "db_save_string", "cannot find key");
10735 return DB_INVALID_HANDLE;
10736 }
10737
10738 if (string_name && string_name[0])
10739 strcpy(str, string_name);
10740 else
10741 strcpy(str, key.name);
10742
10743 name2c(str);
10744 for (i = 0; i < (int) strlen(str); i++)
10745 str[i] = (char) toupper(str[i]);
10746
10747 sprintf(line, "#define %s(_name) const char *_name[] = {\\\n", str);
10748 size = strlen(line);
10749 wr = write(fh, line, size);
10750 if (wr != size) {
10751 cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10752 close(fh);
10753 if (buffer)
10754 free(buffer);
10755 return DB_FILE_ERROR;
10756 }
10757
10758 buffer_size = 10000;
10759 do {
10760 buffer = (char *) malloc(buffer_size);
10761 if (buffer == NULL) {
10762 cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
10763 break;
10764 }
10765
10766 size = buffer_size;
10767 status = db_copy(hDB, hKey, buffer, &size, "");
10768 if (status != DB_TRUNCATED)
10769 break;
10770
10771 /* increase buffer size if truncated */
10772 free(buffer);
10773 buffer = NULL;
10774 buffer_size *= 2;
10775 } while (1);
10776
10777
10778 pc = buffer;
10779
10780 do {
10781 i = 0;
10782 line[i++] = '"';
10783 while (*pc != '\n' && *pc != 0) {
10784 if (*pc == '\"' || *pc == '\'')
10785 line[i++] = '\\';
10786 line[i++] = *pc++;
10787 }
10788 strcpy(&line[i], "\",\\\n");
10789 if (i > 0) {
10790 size = strlen(line);
10791 wr = write(fh, line, size);
10792 if (wr != size) {
10793 cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10794 close(fh);
10795 if (buffer)
10796 free(buffer);
10797 return DB_FILE_ERROR;
10798 }
10799 }
10800
10801 if (*pc == '\n')
10802 pc++;
10803
10804 } while (*pc);
10805
10806 sprintf(line, "NULL }\n\n");
10807 size = strlen(line);
10808 wr = write(fh, line, size);
10809 if (wr != size) {
10810 cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10811 close(fh);
10812 if (buffer)
10813 free(buffer);
10814 return DB_FILE_ERROR;
10815 }
10816
10817 close(fh);
10818 free(buffer);
10819
10820 return DB_SUCCESS;
10821}
10822
10824#endif /* DOXYGEN_SHOULD_SKIP_THIS */
10825
10826/********************************************************************/
10849INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
10850{
10851 if (data_size == 0)
10852 sprintf(string, "<NULL>");
10853 else
10854 switch (type) {
10855 case TID_UINT8:
10856 sprintf(string, "%d", *(((BYTE *) data) + idx));
10857 break;
10858 case TID_INT8:
10859 sprintf(string, "%d", *(((char *) data) + idx));
10860 break;
10861 case TID_CHAR:
10862 sprintf(string, "%c", *(((char *) data) + idx));
10863 break;
10864 case TID_UINT16:
10865 sprintf(string, "%u", *(((WORD *) data) + idx));
10866 break;
10867 case TID_INT16:
10868 sprintf(string, "%d", *(((short *) data) + idx));
10869 break;
10870 case TID_UINT32:
10871 sprintf(string, "%u", *(((DWORD *) data) + idx));
10872 break;
10873 case TID_INT32:
10874 sprintf(string, "%d", *(((INT *) data) + idx));
10875 break;
10876 case TID_UINT64:
10877 sprintf(string, "%llu", *(((UINT64 *) data) + idx));
10878 break;
10879 case TID_INT64:
10880 sprintf(string, "%lld", *(((INT64 *) data) + idx));
10881 break;
10882 case TID_BOOL:
10883 sprintf(string, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
10884 break;
10885 case TID_FLOAT:
10886 if (ss_isnan(*(((float *) data) + idx)))
10887 sprintf(string, "NAN");
10888 else
10889 sprintf(string, "%.7g", *(((float *) data) + idx));
10890 break;
10891 case TID_DOUBLE:
10892 if (ss_isnan(*(((double *) data) + idx)))
10893 sprintf(string, "NAN");
10894 else
10895 sprintf(string, "%.16lg", *(((double *) data) + idx));
10896 break;
10897 case TID_BITFIELD:
10898 sprintf(string, "%u", *(((DWORD *) data) + idx));
10899 break;
10900 case TID_STRING:
10901 case TID_LINK:
10902 mstrlcpy(string, ((char *) data) + data_size * idx, MAX_STRING_LENGTH);
10903 break;
10904 default:
10905 sprintf(string, "<unknown>");
10906 break;
10907 }
10908
10909 return DB_SUCCESS;
10910}
10911
10912/********************************************************************/
10925INT db_sprintff(char *string, const char *format, const void *data, INT data_size, INT idx, DWORD type)
10926{
10927 if (data_size == 0)
10928 sprintf(string, "<NULL>");
10929 else
10930 switch (type) {
10931 case TID_UINT8:
10932 sprintf(string, format, *(((BYTE *) data) + idx));
10933 break;
10934 case TID_INT8:
10935 sprintf(string, format, *(((char *) data) + idx));
10936 break;
10937 case TID_CHAR:
10938 sprintf(string, format, *(((char *) data) + idx));
10939 break;
10940 case TID_UINT16:
10941 sprintf(string, format, *(((WORD *) data) + idx));
10942 break;
10943 case TID_INT16:
10944 sprintf(string, format, *(((short *) data) + idx));
10945 break;
10946 case TID_UINT32:
10947 sprintf(string, format, *(((DWORD *) data) + idx));
10948 break;
10949 case TID_INT32:
10950 sprintf(string, format, *(((INT *) data) + idx));
10951 break;
10952 case TID_UINT64:
10953 sprintf(string, format, *(((UINT64 *) data) + idx));
10954 break;
10955 case TID_INT64:
10956 sprintf(string, format, *(((INT64 *) data) + idx));
10957 break;
10958 case TID_BOOL:
10959 sprintf(string, format, *(((BOOL *) data) + idx) ? 'y' : 'n');
10960 break;
10961 case TID_FLOAT:
10962 if (ss_isnan(*(((float *) data) + idx)))
10963 sprintf(string, "NAN");
10964 else
10965 sprintf(string, format, *(((float *) data) + idx));
10966 break;
10967 case TID_DOUBLE:
10968 if (ss_isnan(*(((double *) data) + idx)))
10969 sprintf(string, "NAN");
10970 else
10971 sprintf(string, format, *(((double *) data) + idx));
10972 break;
10973 case TID_BITFIELD:
10974 sprintf(string, format, *(((DWORD *) data) + idx));
10975 break;
10976 case TID_STRING:
10977 case TID_LINK:
10978 mstrlcpy(string, ((char *) data) + data_size * idx, MAX_STRING_LENGTH);
10979 break;
10980 default:
10981 sprintf(string, "<unknown>");
10982 break;
10983 }
10984
10985 return DB_SUCCESS;
10986}
10987
10988/*------------------------------------------------------------------*/
10989INT db_sprintfh(char *string, const void *data, INT data_size, INT idx, DWORD type)
10990/********************************************************************\
10991
10992 Routine: db_sprintfh
10993
10994 Purpose: Convert a database value to a string according to its type
10995 in hex format
10996
10997 Input:
10998 void *data Value data
10999 INT idx Index for array data
11000 INT data_size Size of single data element
11001 DWORD type Valye type, one of TID_xxx
11002
11003 Output:
11004 char *string ASCII string of data
11005
11006 Function value:
11007 DB_SUCCESS Successful completion
11008
11009\********************************************************************/
11010{
11011 if (data_size == 0)
11012 sprintf(string, "<NULL>");
11013 else
11014 switch (type) {
11015 case TID_UINT8:
11016 sprintf(string, "0x%X", *(((BYTE *) data) + idx));
11017 break;
11018 case TID_INT8:
11019 sprintf(string, "0x%X", *(((char *) data) + idx));
11020 break;
11021 case TID_CHAR:
11022 sprintf(string, "%c", *(((char *) data) + idx));
11023 break;
11024 case TID_UINT16:
11025 sprintf(string, "0x%X", *(((WORD *) data) + idx));
11026 break;
11027 case TID_INT16:
11028 sprintf(string, "0x%hX", *(((short *) data) + idx));
11029 break;
11030 case TID_UINT32:
11031 sprintf(string, "0x%X", *(((DWORD *) data) + idx));
11032 break;
11033 case TID_INT32:
11034 sprintf(string, "0x%X", *(((INT *) data) + idx));
11035 break;
11036 case TID_UINT64:
11037 sprintf(string, "0x%llX", *(((UINT64 *) data) + idx));
11038 break;
11039 case TID_INT64:
11040 sprintf(string, "0x%llX", *(((INT64 *) data) + idx));
11041 break;
11042 case TID_BOOL:
11043 sprintf(string, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11044 break;
11045 case TID_FLOAT:
11046 if (ss_isnan(*(((float *) data) + idx)))
11047 sprintf(string, "NAN");
11048 else
11049 sprintf(string, "%.7g", *(((float *) data) + idx));
11050 break;
11051 case TID_DOUBLE:
11052 if (ss_isnan(*(((double *) data) + idx)))
11053 sprintf(string, "NAN");
11054 else
11055 sprintf(string, "%.16lg", *(((double *) data) + idx));
11056 break;
11057 case TID_BITFIELD:
11058 sprintf(string, "0x%X", *(((DWORD *) data) + idx));
11059 break;
11060 case TID_STRING:
11061 case TID_LINK:
11062 sprintf(string, "%s", ((char *) data) + data_size * idx);
11063 break;
11064 default:
11065 sprintf(string, "<unknown>");
11066 break;
11067 }
11068
11069 return DB_SUCCESS;
11070}
11071
11072/********************************************************************/
11093std::string db_sprintf(const void *data, INT data_size, INT idx, DWORD type)
11094{
11095 if (data_size == 0) {
11096 return "<NULL>";
11097 } else {
11098 char buf[256];
11099 switch (type) {
11100 case TID_UINT8:
11101 sprintf(buf, "%d", *(((BYTE *) data) + idx));
11102 return buf;
11103 case TID_INT8:
11104 sprintf(buf, "%d", *(((char *) data) + idx));
11105 return buf;
11106 case TID_CHAR:
11107 sprintf(buf, "%c", *(((char *) data) + idx));
11108 return buf;
11109 case TID_UINT16:
11110 sprintf(buf, "%u", *(((WORD *) data) + idx));
11111 return buf;
11112 case TID_INT16:
11113 sprintf(buf, "%d", *(((short *) data) + idx));
11114 return buf;
11115 case TID_UINT32:
11116 sprintf(buf, "%u", *(((DWORD *) data) + idx));
11117 return buf;
11118 case TID_INT32:
11119 sprintf(buf, "%d", *(((INT *) data) + idx));
11120 return buf;
11121 case TID_UINT64:
11122 sprintf(buf, "%llu", *(((UINT64 *) data) + idx));
11123 return buf;
11124 case TID_INT64:
11125 sprintf(buf, "%lld", *(((INT64 *) data) + idx));
11126 return buf;
11127 case TID_BOOL:
11128 sprintf(buf, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11129 return buf;
11130 case TID_FLOAT:
11131 if (ss_isnan(*(((float *) data) + idx))) {
11132 return "NAN";
11133 } else {
11134 sprintf(buf, "%.7g", *(((float *) data) + idx));
11135 return buf;
11136 }
11137 case TID_DOUBLE:
11138 if (ss_isnan(*(((double *) data) + idx))) {
11139 return "NAN";
11140 } else {
11141 sprintf(buf, "%.16lg", *(((double *) data) + idx));
11142 return buf;
11143 }
11144 case TID_BITFIELD:
11145 sprintf(buf, "%u", *(((DWORD *) data) + idx));
11146 return buf;
11147 case TID_STRING:
11148 case TID_LINK:
11149 return (((char *) data) + data_size * idx);
11150 default:
11151 return "<unknown>";
11152 }
11153 }
11154}
11155
11156/********************************************************************/
11169std::string db_sprintff(const char *format, const void *data, INT data_size, INT idx, DWORD type)
11170{
11171 if (data_size == 0) {
11172 return "<NULL>";
11173 } else {
11174 char buf[256];
11175 switch (type) {
11176 case TID_UINT8:
11177 sprintf(buf, format, *(((BYTE *) data) + idx));
11178 return buf;
11179 case TID_INT8:
11180 sprintf(buf, format, *(((char *) data) + idx));
11181 return buf;
11182 case TID_CHAR:
11183 sprintf(buf, format, *(((char *) data) + idx));
11184 return buf;
11185 case TID_UINT16:
11186 sprintf(buf, format, *(((WORD *) data) + idx));
11187 return buf;
11188 case TID_INT16:
11189 sprintf(buf, format, *(((short *) data) + idx));
11190 return buf;
11191 case TID_UINT32:
11192 sprintf(buf, format, *(((DWORD *) data) + idx));
11193 return buf;
11194 case TID_INT32:
11195 sprintf(buf, format, *(((INT *) data) + idx));
11196 return buf;
11197 case TID_UINT64:
11198 sprintf(buf, format, *(((UINT64 *) data) + idx));
11199 return buf;
11200 case TID_INT64:
11201 sprintf(buf, format, *(((INT64 *) data) + idx));
11202 return buf;
11203 case TID_BOOL:
11204 sprintf(buf, format, *(((BOOL *) data) + idx) ? 'y' : 'n');
11205 return buf;
11206 case TID_FLOAT:
11207 if (ss_isnan(*(((float *) data) + idx))) {
11208 return "NAN";
11209 } else {
11210 sprintf(buf, format, *(((float *) data) + idx));
11211 return buf;
11212 }
11213 case TID_DOUBLE:
11214 if (ss_isnan(*(((double *) data) + idx))) {
11215 return "NAN";
11216 } else {
11217 sprintf(buf, format, *(((double *) data) + idx));
11218 return buf;
11219 }
11220 case TID_BITFIELD:
11221 sprintf(buf, format, *(((DWORD *) data) + idx));
11222 return buf;
11223 case TID_STRING:
11224 case TID_LINK:
11225 return (((char *) data) + data_size * idx);
11226 default:
11227 return "<unknown>";
11228 }
11229 }
11230}
11231
11233#ifndef DOXYGEN_SHOULD_SKIP_THIS
11234
11235/*------------------------------------------------------------------*/
11236std::string db_sprintfh(const void *data, INT data_size, INT idx, DWORD type)
11237/********************************************************************\
11238
11239 Routine: db_sprintfh
11240
11241 Purpose: Convert a database value to a string according to its type
11242 in hex format
11243
11244 Input:
11245 void *data Value data
11246 INT idx Index for array data
11247 INT data_size Size of single data element
11248 DWORD type Valye type, one of TID_xxx
11249
11250 Output:
11251 char *string ASCII string of data
11252
11253 Function value:
11254 DB_SUCCESS Successful completion
11255
11256\********************************************************************/
11257{
11258 if (data_size == 0) {
11259 return "<NULL>";
11260 } else {
11261 char buf[256];
11262 switch (type) {
11263 case TID_UINT8:
11264 sprintf(buf, "0x%X", *(((BYTE *) data) + idx));
11265 return buf;
11266 case TID_INT8:
11267 sprintf(buf, "0x%X", *(((char *) data) + idx));
11268 return buf;
11269 case TID_CHAR:
11270 sprintf(buf, "%c", *(((char *) data) + idx));
11271 return buf;
11272 case TID_UINT16:
11273 sprintf(buf, "0x%X", *(((WORD *) data) + idx));
11274 return buf;
11275 case TID_INT16:
11276 sprintf(buf, "0x%hX", *(((short *) data) + idx));
11277 return buf;
11278 case TID_UINT32:
11279 sprintf(buf, "0x%X", *(((DWORD *) data) + idx));
11280 return buf;
11281 case TID_INT32:
11282 sprintf(buf, "0x%X", *(((INT *) data) + idx));
11283 return buf;
11284 case TID_UINT64:
11285 sprintf(buf, "0x%llX", *(((UINT64 *) data) + idx));
11286 return buf;
11287 case TID_INT64:
11288 sprintf(buf, "0x%llX", *(((INT64 *) data) + idx));
11289 return buf;
11290 case TID_BOOL:
11291 sprintf(buf, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11292 return buf;
11293 case TID_FLOAT:
11294 if (ss_isnan(*(((float *) data) + idx))) {
11295 return "NAN";
11296 } else {
11297 sprintf(buf, "%.7g", *(((float *) data) + idx));
11298 return buf;
11299 }
11300 case TID_DOUBLE:
11301 if (ss_isnan(*(((double *) data) + idx))) {
11302 return "NAN";
11303 } else {
11304 sprintf(buf, "%.16lg", *(((double *) data) + idx));
11305 return buf;
11306 }
11307 case TID_BITFIELD:
11308 sprintf(buf, "0x%X", *(((DWORD *) data) + idx));
11309 return buf;
11310 case TID_STRING:
11311 case TID_LINK:
11312 return (((char *) data) + data_size * idx);
11313 default:
11314 return "<unknown>";
11315 }
11316 }
11317}
11318
11319/*------------------------------------------------------------------*/
11320INT db_sscanf(const char *data_str, void *data, INT * data_size, INT i, DWORD tid)
11321/********************************************************************\
11322
11323 Routine: db_sscanf
11324
11325 Purpose: Convert a string to a database value according to its type
11326
11327 Input:
11328 char *data_str ASCII string of data
11329 INT i Index for array data
11330 DWORD tid Value type, one of TID_xxx
11331
11332 Output:
11333 void *data Value data
11334 INT *data_size Size of single data element
11335
11336 Function value:
11337 DB_SUCCESS Successful completion
11338
11339\********************************************************************/
11340{
11341 DWORD value = 0;
11342 BOOL hex = FALSE;
11343
11344 if (data_str == NULL)
11345 return 0;
11346
11347 *data_size = rpc_tid_size(tid);
11348 if (strncmp(data_str, "0x", 2) == 0) {
11349 hex = TRUE;
11350 sscanf(data_str + 2, "%x", &value);
11351 }
11352
11353 switch (tid) {
11354 case TID_UINT8:
11355 case TID_INT8:
11356 if (hex)
11357 *((char *) data + i) = (char) value;
11358 else
11359 *((char *) data + i) = (char) atoi(data_str);
11360 break;
11361 case TID_CHAR:
11362 *((char *) data + i) = data_str[0];
11363 break;
11364 case TID_UINT16:
11365 if (hex)
11366 *((WORD *) data + i) = (WORD) value;
11367 else
11368 *((WORD *) data + i) = (WORD) atoi(data_str);
11369 break;
11370 case TID_INT16:
11371 if (hex)
11372 *((short int *) data + i) = (short int) value;
11373 else
11374 *((short int *) data + i) = (short int) atoi(data_str);
11375 break;
11376 case TID_UINT32:
11377 if (hex)
11378 value = strtoul(data_str, nullptr, 16);
11379 else
11380 value = strtoul(data_str, nullptr, 10);
11381
11382 *((DWORD *) data + i) = value;
11383 break;
11384 case TID_INT32:
11385 if (hex)
11386 value = strtol(data_str, nullptr, 16);
11387 else
11388 value = strtol(data_str, nullptr, 10);
11389
11390 *((INT *) data + i) = value;
11391 break;
11392 case TID_UINT64:
11393 if (hex)
11394 *((UINT64 *) data + i) = strtoull(data_str, nullptr, 16);
11395 else
11396 *((UINT64 *) data + i) = strtoull(data_str, nullptr, 10);
11397 break;
11398 case TID_INT64:
11399 if (hex)
11400 *((UINT64 *) data + i) = strtoll(data_str, nullptr, 16);
11401 else
11402 *((UINT64 *) data + i) = strtoll(data_str, nullptr, 10);
11403 break;
11404 case TID_BOOL:
11405 if (data_str[0] == 'y' || data_str[0] == 'Y' ||
11406 data_str[0] == 't' || data_str[0] == 'T' || atoi(data_str) > 0)
11407 *((BOOL *) data + i) = 1;
11408 else
11409 *((BOOL *) data + i) = 0;
11410 break;
11411 case TID_FLOAT:
11412 if (data_str[0] == 'n' || data_str[0] == 'N')
11413 *((float *) data + i) = (float) ss_nan();
11414 else
11415 *((float *) data + i) = (float) atof(data_str);
11416 break;
11417 case TID_DOUBLE:
11418 if (data_str[0] == 'n' || data_str[0] == 'N')
11419 *((double *) data + i) = ss_nan();
11420 else
11421 *((double *) data + i) = atof(data_str);
11422 break;
11423 case TID_BITFIELD:
11424 if (hex)
11425 value = strtoul(data_str, nullptr, 16);
11426 else
11427 value = strtoul(data_str, nullptr, 10);
11428
11429 *((DWORD *) data + i) = value;
11430 break;
11431 case TID_STRING:
11432 case TID_LINK:
11433 strcpy((char *) data, data_str);
11434 *data_size = strlen(data_str) + 1;
11435 break;
11436 }
11437
11438 return DB_SUCCESS;
11439}
11440
11441/*------------------------------------------------------------------*/
11442
11443#ifdef LOCAL_ROUTINES
11444
11445static 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)
11446/********************************************************************\
11447
11448 Routine: db_recurse_record_tree_locked
11449
11450 Purpose: Recurse a database tree and calculate its size or copy
11451 data. Internal use only.
11452
11453\********************************************************************/
11454{
11455 const KEY *pold;
11456 INT size, align, corr, total_size_tmp;
11457
11458 KEYLIST *pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
11459 if (!pkeylist->first_key)
11460 return;
11461 // FIXME: validate pkeylist->first_key
11462 pkey = (const KEY *) ((char *) pheader + pkeylist->first_key);
11463
11464 /* first browse through this level */
11465 do {
11466 pold = NULL;
11467
11468 if (pkey->type == TID_LINK) {
11469 const KEY *plink = db_resolve_link_locked(pheader, pkey, NULL, msg);
11470
11471 if (!plink)
11472 return;
11473
11474 if (plink->type == TID_KEY) {
11475 db_recurse_record_tree_locked(hDB, pheader, plink, data, total_size, base_align, NULL, bSet, convert_flags, msg);
11476 } else {
11477 pold = pkey;
11478 pkey = plink;
11479 }
11480 }
11481
11482 if (pkey->type != TID_KEY) {
11483 /* correct for alignment */
11484 align = 1;
11485
11486 if (rpc_tid_size(pkey->type))
11487 align = rpc_tid_size(pkey->type) < base_align ? rpc_tid_size(pkey->type) : base_align;
11488
11489 if (max_align && align > *max_align)
11490 *max_align = align;
11491
11492 corr = VALIGN(*total_size, align) - *total_size;
11493 *total_size += corr;
11494 if (data)
11495 *data = (void *) ((char *) (*data) + corr);
11496
11497 /* calculate data size */
11498 size = pkey->item_size * pkey->num_values;
11499
11500 if (data) {
11501 if (bSet) {
11502 KEY* wpkey = (KEY*)pkey;
11503 /* copy data if there is write access */
11504 if (pkey->access_mode & MODE_WRITE) {
11505 memcpy((char *) pheader + pkey->data, *data, pkey->item_size * pkey->num_values);
11506
11507 /* convert data */
11508 if (convert_flags) {
11509 if (pkey->num_values > 1)
11510 rpc_convert_data((char *) pheader + pkey->data,
11511 pkey->type, RPC_FIXARRAY, pkey->item_size * pkey->num_values, convert_flags);
11512 else
11513 rpc_convert_single((char *) pheader + pkey->data, pkey->type, 0, convert_flags);
11514 }
11515
11516 /* update time */
11517 wpkey->last_written = ss_time();
11518
11519 /* notify clients which have key open */
11520 db_notify_clients_locked(pheader, hDB, db_pkey_to_hkey(pheader, pkey), -1, TRUE, msg);
11521 }
11522 } else {
11523 /* copy key data if there is read access */
11524 if (pkey->access_mode & MODE_READ) {
11525 memcpy(*data, (char *) pheader + pkey->data, pkey->item_size * pkey->num_values);
11526
11527 /* convert data */
11528 if (convert_flags) {
11529 if (pkey->num_values > 1)
11530 rpc_convert_data(*data, pkey->type,
11532 pkey->item_size * pkey->num_values, convert_flags);
11533 else
11534 rpc_convert_single(*data, pkey->type, RPC_OUTGOING, convert_flags);
11535 }
11536 }
11537 }
11538
11539 *data = (char *) (*data) + size;
11540 }
11541
11542 *total_size += size;
11543 } else {
11544 /* align new substructure according to the maximum
11545 align value in this structure */
11546 align = 1;
11547
11548 total_size_tmp = *total_size;
11549 db_recurse_record_tree_locked(hDB, pheader, pkey, NULL, &total_size_tmp, base_align, &align, bSet, convert_flags, msg);
11550
11551 if (max_align && align > *max_align)
11552 *max_align = align;
11553
11554 corr = VALIGN(*total_size, align) - *total_size;
11555 *total_size += corr;
11556 if (data)
11557 *data = (void *) ((char *) (*data) + corr);
11558
11559 /* now recurse subtree */
11560 db_recurse_record_tree_locked(hDB, pheader, pkey, data, total_size, base_align, NULL, bSet, convert_flags, msg);
11561
11562 corr = VALIGN(*total_size, align) - *total_size;
11563 *total_size += corr;
11564 if (data)
11565 *data = (void *) ((char *) (*data) + corr);
11566 }
11567
11568 if (pold) {
11569 pkey = pold;
11570 pold = NULL;
11571 }
11572
11573 if (!pkey->next_key)
11574 break;
11575
11576 // FIXME: validate pkey->next_key
11577 pkey = (KEY *) ((char *) pheader + pkey->next_key);
11578 } while (TRUE);
11579}
11580
11581static 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)
11582/********************************************************************\
11583
11584 Routine: db_recurse_record_tree_locked
11585
11586 Purpose: Recurse a database tree and calculate its size or copy
11587 data. Internal use only.
11588
11589\********************************************************************/
11590{
11591 /* get first subkey of hKey */
11593
11594 const KEY* pkey = db_get_pkey(pheader, hKey, NULL, "db_recurse_record_tree", msg);
11595
11596 if (!pkey) {
11597 return;
11598 }
11599
11600 db_recurse_record_tree_locked(hDB, pheader, pkey, data, total_size, base_align, max_align, bSet, convert_flags, msg);
11601}
11602
11603#endif /* LOCAL_ROUTINES */
11604
11606#endif /* DOXYGEN_SHOULD_SKIP_THIS */
11607
11608/********************************************************************/
11622{
11623 if (rpc_is_remote()) {
11624 align = ss_get_struct_align();
11625 return rpc_call(RPC_DB_GET_RECORD_SIZE, hDB, hKey, align, buf_size);
11626 }
11627#ifdef LOCAL_ROUTINES
11628 {
11629 KEY key;
11631
11632 if (!align)
11633 align = ss_get_struct_align();
11634
11635 /* check if key has subkeys */
11637 if (status != DB_SUCCESS)
11638 return status;
11639
11640 if (key.type != TID_KEY) {
11641 /* just a single key */
11642 *buf_size = key.item_size * key.num_values;
11643 return DB_SUCCESS;
11644 }
11645
11646 db_err_msg* msg = NULL;
11648
11649 /* determine record size */
11650 *buf_size = max_align = 0;
11651 db_recurse_record_tree_locked(hDB, hKey, NULL, buf_size, align, &max_align, 0, 0, &msg);
11652
11653 //int tmp = *buf_size;
11654 /* correct for byte padding */
11655 *buf_size = VALIGN(*buf_size, max_align);
11656
11657 //if (tmp != *buf_size) {
11658 // 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);
11659 //}
11660
11662 if (msg)
11663 db_flush_msg(&msg);
11664 }
11665#endif /* LOCAL_ROUTINES */
11666
11667 return DB_SUCCESS;
11668}
11669
11670/********************************************************************/
11715INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align)
11716{
11717 if (rpc_is_remote()) {
11718 align = ss_get_struct_align();
11719 return rpc_call(RPC_DB_GET_RECORD, hDB, hKey, data, buf_size, align);
11720 }
11721#ifdef LOCAL_ROUTINES
11722 {
11723 KEY key;
11724 INT convert_flags, status;
11725 INT total_size;
11726 void *pdata;
11727
11728 if (data && buf_size) {
11729 memset(data, 0x00, *buf_size);
11730 }
11731
11732 convert_flags = 0;
11733
11734 if (!align)
11735 align = ss_get_struct_align();
11736 else {
11737 /* only convert data if called remotely, as indicated by align != 0 */
11738 if (rpc_is_mserver()) {
11739 convert_flags = rpc_get_convert_flags();
11740 }
11741 }
11742
11743 /* check if key has subkeys */
11745 if (status != DB_SUCCESS)
11746 return status;
11747
11748 if (key.type != TID_KEY) {
11749 /* copy single key */
11750 if (key.item_size * key.num_values != *buf_size) {
11751 cm_msg(MERROR, "db_get_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d * %d = %d)",
11754 }
11755
11756 db_get_data(hDB, hKey, data, buf_size, key.type);
11757
11758 if (convert_flags) {
11759 if (key.num_values > 1)
11761 else
11762 rpc_convert_single(data, key.type, RPC_OUTGOING, convert_flags);
11763 }
11764
11765 return DB_SUCCESS;
11766 }
11767
11768 /* check record size */
11769 db_get_record_size(hDB, hKey, align, &total_size);
11770 if (total_size != *buf_size) {
11771 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);
11773 }
11774
11775 /* get subkey data */
11776 pdata = data;
11777 total_size = 0;
11778
11779 db_err_msg* msg = NULL;
11781 db_recurse_record_tree_locked(hDB, hKey, &pdata, &total_size, align, NULL, FALSE, convert_flags, &msg);
11783 if (msg)
11784 db_flush_msg(&msg);
11785
11786 //if (total_size != *buf_size) {
11787 // 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);
11788 // //return DB_STRUCT_SIZE_MISMATCH;
11789 //}
11790 }
11791#endif /* LOCAL_ROUTINES */
11792
11793 return DB_SUCCESS;
11794}
11795
11796/********************************************************************/
11812INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align, const char *rec_str)
11813{
11814 int size = *buf_size;
11815 int odb_size = 0;
11816 int status;
11817 //char path[MAX_ODB_PATH];
11818
11819 /* check record size first */
11820
11822 if (status != DB_SUCCESS)
11823 return status;
11824
11825 /* if size mismatch, call repair function */
11826
11827 if (odb_size != size) {
11828 std::string path = db_get_path(hDB, hKey);
11829 cm_msg(MINFO, "db_get_record1", "Fixing ODB \"%s\" struct size mismatch (expected %d, odb size %d)", path.c_str(), size, odb_size);
11831 if (status != DB_SUCCESS)
11832 return status;
11833 }
11834
11835 /* run db_get_record(), if success, we are done */
11836
11837 status = db_get_record(hDB, hKey, data, buf_size, align);
11838 if (status == DB_SUCCESS)
11839 return status;
11840
11841 /* try repair with db_check_record() */
11842
11844 if (status != DB_SUCCESS)
11845 return status;
11846
11847 /* verify struct size again, because there can still be a mismatch if there
11848 * are extra odb entries at the end of the record as db_check_record()
11849 * seems to ignore all odb entries past the end of "rec_str". K.O.
11850 */
11851
11853 if (status != DB_SUCCESS)
11854 return status;
11855
11856 std::string path = db_get_path(hDB, hKey);
11857
11858 if (odb_size != size) {
11859 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());
11861 if (status != DB_SUCCESS)
11862 return status;
11863 }
11864
11865 cm_msg(MERROR, "db_get_record1", "repaired struct size mismatch of \"%s\"", path.c_str());
11866
11867 *buf_size = size;
11868 status = db_get_record(hDB, hKey, data, buf_size, align);
11869
11870 return status;
11871}
11872
11873static 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)
11874{
11875 title[0] = 0;
11876 key_name[0] = 0;
11877 *tid = 0;
11878 *n_data = 0;
11879 *string_length = 0;
11880 *out_rec_str = NULL;
11881
11882 //
11883 // expected format of rec_str:
11884 //
11885 // title: "[.]",
11886 // numeric value: "example_int = INT : 3",
11887 // string value: "example_string = STRING : [20] /Runinfo/Run number",
11888 // array: "aaa = INT[10] : ...",
11889 // string array: "sarr = STRING[10] : [32] ",
11890 //
11891
11892 //printf("parse_rec_str: [%s]\n", rec_str);
11893
11894 while (*rec_str == '\n')
11895 rec_str++;
11896
11897 /* check if it is a section title */
11898 if (rec_str[0] == '[') {
11899 rec_str++;
11900
11901 title[0] = 0;
11902
11903 /* extract title and append '/' */
11904 mstrlcpy(title, rec_str, title_size);
11905 char* p = strchr(title, ']');
11906 if (p)
11907 *p = 0;
11908
11909 int len = strlen(title);
11910 if (len > 0) {
11911 if (title[len - 1] != '/')
11912 mstrlcat(title, "/", title_size);
11913 }
11914
11915 // skip to the next end-of-line
11916 const char* pend = strchr(rec_str, '\n');
11917 if (pend)
11918 rec_str = pend;
11919 else
11921
11922 while (*rec_str == '\n')
11923 rec_str++;
11924
11926 return DB_SUCCESS;
11927 }
11928
11929 if (rec_str[0] == ';') {
11930 // skip to the next end-of-line
11931 const char* pend = strchr(rec_str, '\n');
11932 if (pend)
11933 rec_str = pend;
11934 else
11936
11937 while (*rec_str == '\n')
11938 rec_str++;
11939
11941 return DB_SUCCESS;
11942 }
11943
11944 const char* peq = strchr(rec_str, '=');
11945 if (!peq) {
11946 cm_msg(MERROR, "db_parse_record", "do not see \'=\'");
11947 return DB_INVALID_PARAM;
11948 }
11949
11950 int key_name_len = peq - rec_str;
11951
11952 // remove trailing equals sign and trailing spaces
11953 while (key_name_len > 1) {
11954 if (rec_str[key_name_len-1] == '=') {
11955 key_name_len--;
11956 continue;
11957 }
11958 if (rec_str[key_name_len-1] == ' ') {
11959 key_name_len--;
11960 continue;
11961 }
11962 break;
11963 }
11964
11965 memcpy(key_name, rec_str, key_name_len);
11966 key_name[key_name_len] = 0;
11967
11968 rec_str = peq + 1; // consume the "=" sign
11969
11970 while (*rec_str == ' ') // consume spaces
11971 rec_str++;
11972
11973 // extract type id
11974 char stid[256];
11975 int i;
11976 for (i=0; i<(int)sizeof(stid)-1; i++) {
11977 char s = *rec_str;
11978 if (s == 0) break;
11979 if (s == ' ') break;
11980 if (s == '\n') break;
11981 if (s == '[') break;
11982 stid[i] = s;
11983 rec_str++;
11984 }
11985 stid[i] = 0;
11986
11987 DWORD xtid = 0;
11988 for (xtid = 0; xtid < TID_LAST; xtid++) {
11989 if (strcmp(rpc_tid_name(xtid), stid) == 0) {
11990 *tid = xtid;
11991 break;
11992 }
11993 }
11994
11995 //printf("tid [%s], tid %d\n", stid, *tid);
11996
11997 if (xtid == TID_LAST) {
11998 cm_msg(MERROR, "db_parse_record", "do not see \':\'");
11999 return DB_INVALID_PARAM;
12000 }
12001
12002 while (*rec_str == ' ') // consume spaces
12003 rec_str++;
12004
12005 *n_data = 1;
12006
12007 if (*rec_str == '[') {
12008 // decode array size
12009 rec_str++; // cosume the '['
12010 *n_data = atoi(rec_str);
12011 const char *pbr = strchr(rec_str, ']');
12012 if (!pbr) {
12013 cm_msg(MERROR, "db_parse_record", "do not see closing bracket \']\'");
12014 return DB_INVALID_PARAM;
12015 }
12016 rec_str = pbr + 1; // skip the closing bracket
12017 }
12018
12019 while (*rec_str == ' ') // consume spaces
12020 rec_str++;
12021
12022 const char* pcol = strchr(rec_str, ':');
12023 if (!pcol) {
12024 cm_msg(MERROR, "db_parse_record", "do not see \':\'");
12025 return DB_INVALID_PARAM;
12026 }
12027
12028 rec_str = pcol + 1; // skip the ":"
12029
12030 while (*rec_str == ' ') // consume spaces
12031 rec_str++;
12032
12033 *string_length = 0;
12034 if (xtid == TID_LINK || xtid == TID_STRING) {
12035 // extract string length
12036 const char* pbr = strchr(rec_str, '[');
12037 if (pbr) {
12038 *string_length = atoi(pbr+1);
12039 }
12040 }
12041
12042 // skip to the next end-of-line
12043 const char* pend = strchr(rec_str, '\n');
12044 if (pend)
12045 rec_str = pend;
12046 else
12048
12049 while (*rec_str == '\n')
12050 rec_str++;
12051
12053 return DB_SUCCESS;
12054}
12055
12056static 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)
12057{
12058 assert(tid > 0);
12059 assert(n_data > 0);
12060 int tsize = rpc_tid_size(tid);
12061 int offset = *buf_ptr - buf_start;
12062 int align = 0;
12063 if (tsize && (offset%tsize != 0)) {
12064 while (offset%tsize != 0) {
12065 align++;
12066 *(*buf_ptr) = 0xFF; // pad bytes for correct data alignement
12067 (*buf_ptr)++;
12068 (*buf_remain)--;
12069 offset++;
12070 }
12071 }
12072 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);
12073 if (tsize > 0) {
12074 int xsize = tsize*n_data;
12075 if (xsize > *buf_remain) {
12076 cm_msg(MERROR, "db_get_record2", "buffer overrun at key \"%s\", size %d, buffer remaining %d", key_name, xsize, *buf_remain);
12077 return DB_INVALID_PARAM;
12078 }
12079 int ysize = xsize;
12080 int status = db_get_value(hDB, hKey, key_name, *buf_ptr, &ysize, tid, FALSE);
12081 //printf("status %d, xsize %d\n", status, xsize);
12082 if (status != DB_SUCCESS) {
12083 cm_msg(MERROR, "db_get_record2", "cannot read \"%s\", db_get_value() status %d", key_name, status);
12084 memset(*buf_ptr, 0, xsize);
12085 *buf_ptr += xsize;
12086 *buf_remain -= xsize;
12087 return status;
12088 }
12089 *buf_ptr += xsize;
12090 *buf_remain -= xsize;
12091 } else if (tid == TID_STRING) {
12092 int xstatus = 0;
12093 int i;
12094 for (i=0; i<n_data; i++) {
12095 int xsize = string_length;
12096 if (xsize > *buf_remain) {
12097 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);
12098 return DB_INVALID_PARAM;
12099 }
12100 char xkey_name[MAX_ODB_PATH+100];
12101 sprintf(xkey_name, "%s[%d]", key_name, i);
12103 //printf("status %d, string length %d, xsize %d, actual len %d\n", status, string_length, xsize, (int)strlen(*buf_ptr));
12104 if (status == DB_TRUNCATED) {
12105 // make sure string is NUL terminated
12106 (*buf_ptr)[string_length-1] = 0;
12107 cm_msg(MERROR, "db_get_record2", "string key \"%s\" index %d, string value was truncated", key_name, i);
12108 } else if (status != DB_SUCCESS) {
12109 cm_msg(MERROR, "db_get_record2", "cannot read string \"%s\"[%d], db_get_value() status %d", key_name, i, status);
12111 xstatus = status;
12112 }
12115 }
12116 if (xstatus != 0) {
12117 return xstatus;
12118 }
12119 } else {
12120 cm_msg(MERROR, "db_get_record2", "cannot read key \"%s\" of unsupported type %d", key_name, tid);
12121 return DB_INVALID_PARAM;
12122 }
12123 return DB_SUCCESS;
12124}
12125
12126/********************************************************************/
12177{
12178 int status = DB_SUCCESS;
12179
12180 printf("db_get_record2!\n");
12181
12182 assert(data != NULL);
12183 assert(xbuf_size != NULL);
12184 assert(*xbuf_size > 0);
12185 assert(correct == 0);
12186
12187 int truncated = 0;
12188#if 1
12189 char* r1 = NULL;
12190 int rs = *xbuf_size;
12191 if (1) {
12192 r1 = (char*)malloc(rs);
12193 assert(r1 != NULL);
12194 memset(data, 0xFF, *xbuf_size);
12195 memset(r1, 0xFF, rs);
12196 //status = db_get_record1(hDB, hKey, r1, &rs, 0, rec_str);
12197 status = db_get_record(hDB, hKey, r1, &rs, 0);
12198 printf("db_get_record status %d\n", status);
12199 }
12200#endif
12201
12202 char* buf_start = (char*)data;
12203 int buf_size = *xbuf_size;
12204
12205 char* buf_ptr = buf_start;
12206 int buf_remain = buf_size;
12207
12208 while (rec_str && *rec_str != 0) {
12209 char title[256];
12210 char key_name[MAX_ODB_PATH];
12211 int tid = 0;
12212 int n_data = 0;
12213 int string_length = 0;
12214 const char* rec_str_next = NULL;
12215
12216 status = db_parse_record(rec_str, &rec_str_next, title, sizeof(title), key_name, sizeof(key_name), &tid, &n_data, &string_length);
12217
12218 //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);
12219
12221
12222 if (status != DB_SUCCESS) {
12223 return status;
12224 }
12225
12226 if (key_name[0] == 0) {
12227 // skip title strings, comments, etc
12228 continue;
12229 }
12230
12232 if (status == DB_INVALID_PARAM) {
12233 cm_msg(MERROR, "db_get_record2", "error: cannot continue reading odb record because of previous fatal error, status %d", status);
12234 return DB_INVALID_PARAM;
12235 } if (status != DB_SUCCESS) {
12236 truncated = 1;
12237 }
12238
12240 }
12241
12242 if (r1) {
12243 int ok = -1;
12244 int i;
12245 for (i=0; i<rs; i++) {
12246 if (r1[i] != buf_start[i]) {
12247 ok = i;
12248 break;
12249 }
12250 }
12251 if (ok>=0 || buf_remain>0) {
12252 printf("db_get_record2: miscompare at %d out of %d, buf_remain %d\n", ok, rs, buf_remain);
12253 } else {
12254 printf("db_get_record2: check ok\n");
12255 }
12256 }
12257
12258 if (buf_remain > 0) {
12259 // FIXME: we finished processing the data definition string, but unused space remains in the buffer
12260 return DB_TRUNCATED;
12261 }
12262
12263 if (truncated)
12264 return DB_TRUNCATED;
12265 else
12266 return DB_SUCCESS;
12267}
12268
12269/********************************************************************/
12299INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
12300{
12301 if (rpc_is_remote()) {
12302 align = ss_get_struct_align();
12303 return rpc_call(RPC_DB_SET_RECORD, hDB, hKey, data, buf_size, align);
12304 }
12305#ifdef LOCAL_ROUTINES
12306 {
12307 KEY key;
12308 INT convert_flags;
12309 INT total_size;
12310 void *pdata;
12311
12312 convert_flags = 0;
12313
12314 if (!align)
12315 align = ss_get_struct_align();
12316 else {
12317 /* only convert data if called remotely, as indicated by align != 0 */
12318 if (rpc_is_mserver()) {
12319 convert_flags = rpc_get_convert_flags();
12320 }
12321 }
12322
12323 /* check if key has subkeys */
12324 db_get_key(hDB, hKey, &key);
12325 if (key.type != TID_KEY) {
12326 /* copy single key */
12327 if (key.item_size * key.num_values != buf_size) {
12328 cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
12330 }
12331
12332 if (convert_flags) {
12333 if (key.num_values > 1)
12335 else
12336 rpc_convert_single(data, key.type, 0, convert_flags);
12337 }
12338
12340 return DB_SUCCESS;
12341 }
12342
12343 /* check record size */
12344 db_get_record_size(hDB, hKey, align, &total_size);
12345 if (total_size != buf_size) {
12346 cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
12348 }
12349
12350 /* set subkey data */
12351 pdata = data;
12352 total_size = 0;
12353
12355 db_err_msg* msg = NULL;
12356 db_allow_write_locked(&_database[hDB-1], "db_set_record");
12357 db_recurse_record_tree_locked(hDB, hKey, &pdata, &total_size, align, NULL, TRUE, convert_flags, &msg);
12359 if (msg)
12360 db_flush_msg(&msg);
12361 }
12362#endif /* LOCAL_ROUTINES */
12363
12364 return DB_SUCCESS;
12365}
12366
12368#ifndef DOXYGEN_SHOULD_SKIP_THIS
12369
12370/*------------------------------------------------------------------*/
12372/********************************************************************\
12373
12374 Routine: db_add_open_record
12375
12376 Purpose: Server part of db_open_record. Internal use only.
12377
12378\********************************************************************/
12379{
12380 if (rpc_is_remote())
12381 return rpc_call(RPC_DB_ADD_OPEN_RECORD, hDB, hKey, access_mode);
12382
12383#ifdef LOCAL_ROUTINES
12384 {
12385 INT i;
12386 int status;
12387
12388 if (hDB > _database_entries || hDB <= 0) {
12389 cm_msg(MERROR, "db_add_open_record", "invalid database handle");
12390 return DB_INVALID_HANDLE;
12391 }
12392
12393 db_err_msg* msg = NULL;
12394
12395 /* lock database */
12397
12398 DATABASE *pdb = &_database[hDB - 1];
12399 DATABASE_HEADER *pheader = pdb->database_header;
12401
12402 /* check if key is already open */
12403 for (i = 0; i < pclient->max_index; i++) {
12404 if (pclient->open_record[i].handle == hKey)
12405 break;
12406 }
12407
12408 if (i < pclient->max_index) {
12410 return DB_SUCCESS;
12411 }
12412
12413 /* not found, search free entry */
12414 for (i = 0; i < pclient->max_index; i++) {
12415 if (pclient->open_record[i].handle == 0)
12416 break;
12417 }
12418
12419 /* check if maximum number reached */
12420 if (i == MAX_OPEN_RECORDS) {
12422 return DB_NO_MEMORY;
12423 }
12424
12425 db_allow_write_locked(pdb, "db_add_open_record");
12426
12427 KEY *pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_add_open_record", &msg);
12428
12429 if (!pkey) {
12431 if (msg)
12432 db_flush_msg(&msg);
12433 return status;
12434 }
12435
12436 if (i == pclient->max_index)
12437 pclient->max_index++;
12438
12439 pclient->open_record[i].handle = hKey;
12440 pclient->open_record[i].access_mode = access_mode;
12441
12442 /* increment notify_count */
12443 pkey->notify_count++;
12444
12445 pclient->num_open_records++;
12446
12447 /* set exclusive bit if requested */
12448 if (access_mode & MODE_WRITE)
12449 db_set_mode(hDB, hKey, (WORD) (pkey->access_mode | MODE_EXCLUSIVE), 2);
12450
12452 }
12453#endif /* LOCAL_ROUTINES */
12454
12455 return DB_SUCCESS;
12456}
12457
12458#ifdef LOCAL_ROUTINES
12459
12461{
12462 int status = DB_SUCCESS;
12463
12465
12466 /* search key */
12467 int idx;
12468 for (idx = 0; idx < pclient->max_index; idx++)
12469 if (pclient->open_record[idx].handle == hKey)
12470 break;
12471
12472 if (idx == pclient->max_index) {
12473 return DB_INVALID_HANDLE;
12474 }
12475
12476 KEY* pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_remove_open_record_wlocked", NULL);
12477
12478 if (!pkey)
12479 return status;
12480
12481 /* decrement notify_count */
12482
12483 if (pkey->notify_count > 0)
12484 pkey->notify_count--;
12485
12486 pclient->num_open_records--;
12487
12488 /* remove exclusive flag */
12489 if (pclient->open_record[idx].access_mode & MODE_WRITE)
12490 db_set_mode_wlocked(pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2, NULL);
12491
12492 memset(&pclient->open_record[idx], 0, sizeof(OPEN_RECORD));
12493
12494 /* calculate new max_index entry */
12495 int i;
12496 for (i = pclient->max_index - 1; i >= 0; i--)
12497 if (pclient->open_record[i].handle != 0)
12498 break;
12499 pclient->max_index = i + 1;
12500
12501 return DB_SUCCESS;
12502}
12503#endif // LOCAL_ROUTINES
12504
12505
12507/********************************************************************\
12508
12509 Routine: db_remove_open_record
12510
12511 Purpose: Gets called by db_close_record. Internal use only.
12512
12513\********************************************************************/
12514{
12515 if (rpc_is_remote())
12517
12518 int status = DB_SUCCESS;
12519
12520#ifdef LOCAL_ROUTINES
12521 {
12522 if (hDB > _database_entries || hDB <= 0) {
12523 cm_msg(MERROR, "db_remove_open_record", "invalid database handle %d", hDB);
12524 return DB_INVALID_HANDLE;
12525 }
12526
12528
12529 DATABASE *pdb = &_database[hDB - 1];
12530 DATABASE_HEADER *pheader = pdb->database_header;
12531
12532 db_allow_write_locked(pdb, "db_remove_open_record");
12533
12535
12537 }
12538#endif /* LOCAL_ROUTINES */
12539
12540 return status;
12541}
12542
12543/*------------------------------------------------------------------*/
12544
12545#ifdef LOCAL_ROUTINES
12546
12548/********************************************************************\
12549
12550 Routine: db_notify_clients
12551
12552 Purpose: Gets called by db_set_xxx functions. Internal use only.
12553
12554\********************************************************************/
12555{
12556 HNDLE hKey;
12558 INT i, j;
12559 char str[80];
12560 int status;
12561
12562 hKey = hKeyMod;
12563
12564 const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_notify_clients", msg);
12565
12566 if (!pkey) {
12567 return status;
12568 }
12569
12570 do {
12571
12572 /* check which client has record open */
12573 if (pkey->notify_count)
12574 for (i = 0; i < pheader->max_client_index; i++) {
12575 const DATABASE_CLIENT* pclient = &pheader->client[i];
12576 for (j = 0; j < pclient->max_index; j++)
12577 if (pclient->open_record[j].handle == hKey) {
12578 /* send notification to remote process */
12579 sprintf(str, "O %d %d %d %d", hDB, hKey, hKeyMod, index);
12580 ss_resume(pclient->port, str);
12581 }
12582 }
12583
12584 if (pkey->parent_keylist == 0 || !bWalk)
12585 return DB_SUCCESS;
12586
12587 // FIXME: validate pkey->parent_keylist
12588 pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
12589 pkey = db_get_pkey(pheader, pkeylist->parent, &status, "db_notify_clients", msg);
12590 if (!pkey) {
12591 return status;
12592 }
12593 hKey = db_pkey_to_hkey(pheader, pkey);
12594 } while (TRUE);
12595
12596}
12597
12598#endif /* LOCAL_ROUTINES */
12599/*------------------------------------------------------------------*/
12600
12601
12603/********************************************************************\
12604
12605 Routine: db_notify_clients
12606
12607 Purpose: Gets called by db_set_xxx functions. Internal use only.
12608 \********************************************************************/
12609{
12610 if (rpc_is_remote()) {
12611 cm_msg(MERROR, "db_notify_clients", "db_notify_clients() does not work in remotely connected MIDAS clients");
12612 return DB_INVALID_HANDLE;
12613 }
12614
12615#ifdef LOCAL_ROUTINES
12616 {
12617 db_err_msg* msg = NULL;
12619 if (status != DB_SUCCESS)
12620 return status;
12622 db_notify_clients_locked(pheader, hDB, hKeyMod, index, bWalk, &msg);
12624 if (msg)
12625 db_flush_msg(&msg);
12626 }
12627#endif
12628 return DB_SUCCESS;
12629}
12630
12632/********************************************************************\
12633
12634 Routine: db_notify_clients_array
12635
12636 Purpose: This function is typically called after a set of calls
12637 to db_set_data1 which omits hot-link notification to
12638 programs. After several ODB values are modified in a set,
12639 this function has to be called to trigger the hot-links
12640 of the whole set.
12641
12642 \********************************************************************/
12643{
12644 if (rpc_is_remote())
12646
12647#ifdef LOCAL_ROUTINES
12648 {
12650 if (status != DB_SUCCESS)
12651 return status;
12652 db_err_msg* msg = NULL;
12654 int count = size/sizeof(INT);
12655 for (int i=0 ; i<count; i++) {
12656 db_notify_clients_locked(pheader, hDB, hKeys[i], -1, TRUE, &msg);
12657 }
12659 if (msg)
12660 db_flush_msg(&msg);
12661 }
12662#endif
12663 return DB_SUCCESS;
12664}
12665
12666/*------------------------------------------------------------------*/
12668{
12669 char full_name[MAX_ODB_PATH];
12670 INT status, size;
12672 KEY initkey, key;
12673
12674 /* avoid compiler warnings */
12675 status = level;
12676
12677 /* compose name of init key */
12678 std::string s = db_get_path(hDB, hKey);
12679 mstrlcpy(full_name, s.c_str(), sizeof(full_name));
12680 *strchr(full_name, 'O') = 'I';
12681
12682 /* if key in init record found, copy original data to init data */
12684 if (status == DB_SUCCESS) {
12686 if (status != DB_SUCCESS) {
12687 cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', db_get_key() status %d", full_name, status);
12688 return;
12689 }
12691 if (status != DB_SUCCESS) {
12692 cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', second db_get_key() status %d", full_name, status);
12693 return;
12694 }
12695
12696 if (initkey.type != TID_KEY && initkey.type == key.type) {
12697 char* allocbuffer = NULL;
12698 char stackbuffer[10000];
12699 char* buffer = stackbuffer;
12700 size = sizeof(stackbuffer);
12701 while (1) {
12702 /* copy data from original key to new key */
12703 status = db_get_data(hDB, hKey, buffer, &size, initkey.type);
12704 if (status == DB_SUCCESS) {
12705 status = db_set_data(hDB, hKeyInit, buffer, initkey.total_size, initkey.num_values, initkey.type);
12706 if (status != DB_SUCCESS) {
12707 cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', db_set_data() status %d", full_name, status);
12708 return;
12709 }
12710 break;
12711 }
12712 if (status == DB_TRUNCATED) {
12713 size *= 2;
12714 allocbuffer = (char *)realloc(allocbuffer, size);
12715 assert(allocbuffer != NULL);
12716 buffer = allocbuffer;
12717 continue;
12718 }
12719 cm_msg(MERROR, "merge_records", "aborting on unexpected failure of db_get_data(%s), status %d", full_name, status);
12720 abort();
12721 }
12722 if (allocbuffer)
12723 free(allocbuffer);
12724 }
12725 } else if (status == DB_NO_KEY) {
12726 /* do nothing */
12727 } else if (status == DB_INVALID_LINK) {
12729 if (status == DB_SUCCESS) {
12730 size = sizeof(full_name);
12732 }
12733 cm_msg(MERROR, "merge_records", "Invalid link \"%s\"", full_name);
12734 } else {
12735 cm_msg(MERROR, "merge_records", "aborting on unexpected failure of db_find_key(%s), status %d", full_name, status);
12736 abort();
12737 }
12738}
12739
12740static int _global_open_count; // FIXME: this is not thread-safe
12741
12743{
12744 if (pkey->notify_count)
12746}
12747
12749#endif /* DOXYGEN_SHOULD_SKIP_THIS */
12750
12751/********************************************************************/
12808INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
12809{
12810 char str[256], key_name[256], *buffer;
12811 INT status, size, buffer_size;
12813
12814 if (rpc_is_remote())
12815 return rpc_call(RPC_DB_CREATE_RECORD, hDB, hKey, orig_key_name, init_str);
12816
12817 /* make this function atomic */
12819
12820 /* strip trailing '/' */
12821 mstrlcpy(key_name, orig_key_name, sizeof(key_name));
12822 if (strlen(key_name) > 1 && key_name[strlen(key_name) - 1] == '/')
12823 key_name[strlen(key_name) - 1] = 0;
12824
12825 /* merge temporay record and original record */
12826 status = db_find_key(hDB, hKey, key_name, &hKeyOrig);
12827 if (status == DB_SUCCESS) {
12828 assert(hKeyOrig != 0);
12829#ifdef CHECK_OPEN_RECORD
12830 /* check if key or subkey is opened */
12831 _global_open_count = 0; // FIXME: this is not thread safe
12833 if (_global_open_count) {
12835 return DB_OPEN_RECORD;
12836 }
12837#endif
12838 /* create temporary records */
12839 sprintf(str, "/System/Tmp/%sI", ss_tid_to_string(ss_gettid()).c_str());
12840 //printf("db_create_record str [%s]\n", str);
12841 db_find_key(hDB, 0, str, &hKeyTmp);
12842 if (hKeyTmp)
12845 status = db_find_key(hDB, 0, str, &hKeyTmp);
12846 if (status != DB_SUCCESS) {
12848 return status;
12849 }
12850
12851 sprintf(str, "/System/Tmp/%sO", ss_tid_to_string(ss_gettid()).c_str());
12852 //printf("db_create_record str [%s]\n", str);
12853 db_find_key(hDB, 0, str, &hKeyTmpO);
12854 if (hKeyTmpO)
12858 if (status != DB_SUCCESS) {
12860 return status;
12861 }
12862
12863 status = db_paste(hDB, hKeyTmp, init_str);
12864 if (status != DB_SUCCESS) {
12866 return status;
12867 }
12868
12869 buffer_size = 10000;
12870 buffer = (char *) malloc(buffer_size);
12871 do {
12872 size = buffer_size;
12873 status = db_copy(hDB, hKeyOrig, buffer, &size, "");
12874 if (status == DB_TRUNCATED) {
12875 buffer_size += 10000;
12876 buffer = (char *) realloc(buffer, buffer_size);
12877 assert(buffer != NULL);
12878 continue;
12879 }
12880 if (status != DB_SUCCESS) {
12882 return status;
12883 }
12884
12885 } while (status != DB_SUCCESS);
12886
12887 status = db_paste(hDB, hKeyTmpO, buffer);
12888 if (status != DB_SUCCESS) {
12889 free(buffer);
12891 return status;
12892 }
12893
12894 /* merge temporay record and original record */
12896
12897 /* delete original record */
12898 while (1) {
12900 if (!hSubkey)
12901 break;
12902
12904 if (status != DB_SUCCESS) {
12905 free(buffer);
12907 return status;
12908 }
12909 }
12910
12911 /* copy merged record to original record */
12912 do {
12913 size = buffer_size;
12914 status = db_copy(hDB, hKeyTmp, buffer, &size, "");
12915 if (status == DB_TRUNCATED) {
12916 buffer_size += 10000;
12917 buffer = (char *) realloc(buffer, buffer_size);
12918 continue;
12919 }
12920 if (status != DB_SUCCESS) {
12921 free(buffer);
12923 return status;
12924 }
12925
12926 } while (status != DB_SUCCESS);
12927
12928 status = db_paste(hDB, hKeyOrig, buffer);
12929 if (status != DB_SUCCESS) {
12930 free(buffer);
12932 return status;
12933 }
12934
12935 /* delete temporary records */
12938
12939 free(buffer);
12940 buffer = NULL;
12941 } else if (status == DB_NO_KEY) {
12942 /* create fresh record */
12943 db_create_key(hDB, hKey, key_name, TID_KEY);
12944 status = db_find_key(hDB, hKey, key_name, &hKeyTmp);
12945 if (status != DB_SUCCESS) {
12947 return status;
12948 }
12949
12950 status = db_paste(hDB, hKeyTmp, init_str);
12951 if (status != DB_SUCCESS) {
12953 return status;
12954 }
12955 } else {
12956 cm_msg(MERROR, "db_create_record", "aborting on unexpected failure of db_find_key(%s), status %d", key_name, status);
12957 abort();
12958 }
12959
12961
12962 return DB_SUCCESS;
12963}
12964
12965/********************************************************************/
12982{
12983 char line[MAX_STRING_LENGTH];
12984 char title[MAX_STRING_LENGTH];
12985 char key_name[MAX_STRING_LENGTH];
12986 char info_str[MAX_STRING_LENGTH + 50];
12987 char *pc;
12988 const char *pold, *rec_str_orig;
12989 DWORD tid;
12990 INT i, j, n_data, string_length, status;
12992 KEY key;
12994
12995 if (rpc_is_remote())
12997
12998 /* check if record exists */
13000
13001 /* create record if not */
13002 if (status == DB_NO_KEY) {
13003 if (correct)
13005 return DB_NO_KEY;
13006 }
13007
13008 assert(hKeyRoot);
13009
13010 title[0] = 0;
13012
13014 if (key.type == TID_KEY)
13015 /* get next subkey which is not of type TID_KEY */
13017 else
13018 /* test root key itself */
13020
13021 if (hKeyTest == 0 && *rec_str != 0) {
13022 if (correct)
13024
13025 return DB_STRUCT_MISMATCH;
13026 }
13027
13028 do {
13029 if (*rec_str == 0)
13030 break;
13031
13032 for (i = 0; *rec_str != '\n' && *rec_str && i < MAX_STRING_LENGTH; i++)
13033 line[i] = *rec_str++;
13034
13035 if (i == MAX_STRING_LENGTH) {
13036 cm_msg(MERROR, "db_check_record", "line too long");
13037 return DB_TRUNCATED;
13038 }
13039
13040 line[i] = 0;
13041 if (*rec_str == '\n')
13042 rec_str++;
13043
13044 /* check if it is a section title */
13045 if (line[0] == '[') {
13046 /* extract title and append '/' */
13047 strcpy(title, line + 1);
13048 if (strchr(title, ']'))
13049 *strchr(title, ']') = 0;
13050 if (title[0] && title[strlen(title) - 1] != '/')
13051 strcat(title, "/");
13052 } else {
13053 /* valid data line if it includes '=' and no ';' */
13054 if (strchr(line, '=') && line[0] != ';') {
13055 /* copy type info and data */
13056 pc = strchr(line, '=') + 1;
13057 while (*pc == ' ')
13058 pc++;
13059 strcpy(info_str, pc);
13060
13061 /* extract key name */
13062 *strchr(line, '=') = 0;
13063
13064 pc = &line[strlen(line) - 1];
13065 while (*pc == ' ')
13066 *pc-- = 0;
13067
13068 mstrlcpy(key_name, line, sizeof(key_name));
13069
13070 /* evaluate type info */
13071 strcpy(line, info_str);
13072 if (strchr(line, ' '))
13073 *strchr(line, ' ') = 0;
13074
13075 n_data = 1;
13076 if (strchr(line, '[')) {
13077 n_data = atol(strchr(line, '[') + 1);
13078 *strchr(line, '[') = 0;
13079 }
13080
13081 for (tid = 0; tid < TID_LAST; tid++)
13082 if (strcmp(rpc_tid_name(tid), line) == 0)
13083 break;
13084 if (tid == TID_LAST) {
13085 for (tid = 0; tid < TID_LAST; tid++)
13086 if (strcmp(rpc_tid_name_old(tid), line) == 0)
13087 break;
13088 }
13089
13090 string_length = 0;
13091
13092 if (tid == TID_LAST)
13093 cm_msg(MERROR, "db_check_record", "found unknown data type \"%s\" in ODB file", line);
13094 else {
13095 /* skip type info */
13096 pc = info_str;
13097 while (*pc != ' ' && *pc)
13098 pc++;
13099 while ((*pc == ' ' || *pc == ':') && *pc)
13100 pc++;
13101
13102 if (n_data > 1) {
13103 info_str[0] = 0;
13104 if (!*rec_str)
13105 break;
13106
13107 for (j = 0; *rec_str != '\n' && *rec_str; j++)
13108 info_str[j] = *rec_str++;
13109 info_str[j] = 0;
13110 if (*rec_str == '\n')
13111 rec_str++;
13112 }
13113
13114 for (i = 0; i < n_data; i++) {
13115 /* strip trailing \n */
13116 pc = &info_str[strlen(info_str) - 1];
13117 while (*pc == '\n' || *pc == '\r')
13118 *pc-- = 0;
13119
13120 if (tid == TID_STRING || tid == TID_LINK) {
13121 if (!string_length) {
13122 if (info_str[1] == '=')
13123 string_length = -1;
13124 else {
13125 pc = strchr(info_str, '[');
13126 if (pc != NULL)
13127 string_length = atoi(pc + 1);
13128 else
13129 string_length = -1;
13130 }
13133 cm_msg(MERROR, "db_check_record", "found string exceeding MAX_STRING_LENGTH");
13134 }
13135 }
13136
13137 if (string_length == -1) {
13138 /* multi-line string */
13139 if (strstr(rec_str, "\n====#$@$#====\n") != NULL) {
13140 string_length = (POINTER_T) strstr(rec_str, "\n====#$@$#====\n") - (POINTER_T) rec_str + 1;
13141
13142 rec_str = strstr(rec_str, "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
13143 } else
13144 cm_msg(MERROR, "db_check_record", "found multi-line string without termination sequence");
13145 } else {
13146 if (strchr(info_str, ']'))
13147 pc = strchr(info_str, ']') + 1;
13148 else
13149 pc = info_str + 2;
13150 while (*pc && *pc != ' ')
13151 pc++;
13152 while (*pc && *pc == ' ')
13153 pc++;
13154
13155 /* limit string size */
13156 *(pc + string_length - 1) = 0;
13157 }
13158 } else {
13159 pc = info_str;
13160
13161 if (n_data > 1 && info_str[0] == '[') {
13162 pc = strchr(info_str, ']') + 1;
13163 while (*pc && *pc == ' ')
13164 pc++;
13165 }
13166 }
13167
13168 if (i < n_data - 1) {
13169 info_str[0] = 0;
13170 if (!*rec_str)
13171 break;
13172
13173 pold = rec_str;
13174
13175 for (j = 0; *rec_str != '\n' && *rec_str; j++)
13176 info_str[j] = *rec_str++;
13177 info_str[j] = 0;
13178 if (*rec_str == '\n')
13179 rec_str++;
13180
13181 /* test if valid data */
13182 if (tid != TID_STRING && tid != TID_LINK) {
13183 if (info_str[0] == 0 || (strchr(info_str, '=')
13184 && strchr(info_str, ':')))
13185 rec_str = pold;
13186 }
13187 }
13188 }
13189
13190 /* if rec_str contains key, but not ODB, return mismatch */
13191 if (!hKeyTest) {
13192 if (correct)
13194
13195 return DB_STRUCT_MISMATCH;
13196 }
13197
13199 assert(status == DB_SUCCESS);
13200
13202
13203 if (key.type == TID_STRING) {
13204 //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);
13205 if (string_length > 0 && string_length != key.item_size) {
13207 }
13208 }
13209
13210 /* check rec_str vs. ODB key */
13211 if (!equal_ustring(key.name, key_name) || key.type != tid || key.num_values != n_data || bad_string_length) {
13212 //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);
13213 if (correct)
13215
13216 return DB_STRUCT_MISMATCH;
13217 }
13218
13219 /* get next key in ODB */
13221 }
13222 }
13223 }
13224 } while (TRUE);
13225
13226 return DB_SUCCESS;
13227}
13228
13229/********************************************************************/
13301 WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info)
13302{
13303 INT idx, status, size;
13304 KEY key;
13305 void *data;
13306
13307 /* allocate new space for the local record list */
13308 if (_record_list_entries == 0) {
13310 assert(_record_list != NULL);
13311 memset(_record_list, 0, sizeof(RECORD_LIST));
13312 if (_record_list == NULL) {
13313 cm_msg(MERROR, "db_open_record", "not enough memory");
13314 return DB_NO_MEMORY;
13315 }
13316
13318 idx = 0;
13319 } else {
13320 /* check for a deleted entry */
13321 for (idx = 0; idx < _record_list_entries; idx++)
13322 if (!_record_list[idx].handle)
13323 break;
13324
13325 /* if not found, create new one */
13326 if (idx == _record_list_entries) {
13328 if (_record_list == NULL) {
13329 cm_msg(MERROR, "db_open_record", "not enough memory");
13330 return DB_NO_MEMORY;
13331 }
13332
13334
13336 }
13337 }
13338
13339 db_get_key(hDB, hKey, &key);
13340
13341 /* check record size */
13342 status = db_get_record_size(hDB, hKey, 0, &size);
13343 if (status != DB_SUCCESS) {
13345 cm_msg(MERROR, "db_open_record", "cannot get record size, db_get_record_size() status %d", status);
13346 return DB_NO_MEMORY;
13347 }
13348
13349 if (size != rec_size && ptr != NULL) {
13351 std::string path = db_get_path(hDB, hKey);
13352 cm_msg(MERROR, "db_open_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d)", path.c_str(), rec_size, size);
13354 }
13355
13356 /* check for read access */
13357 if (((key.access_mode & MODE_EXCLUSIVE) && (access_mode & MODE_WRITE))
13358 || (!(key.access_mode & MODE_WRITE) && (access_mode & MODE_WRITE))
13359 || (!(key.access_mode & MODE_READ) && (access_mode & MODE_READ))) {
13361 return DB_NO_ACCESS;
13362 }
13363
13364 if (access_mode & MODE_ALLOC) {
13365 data = malloc(size);
13366
13367 if (data == NULL) {
13369 cm_msg(MERROR, "db_open_record", "not enough memory, malloc(%d) returned NULL", size);
13370 return DB_NO_MEMORY;
13371 }
13372
13373 memset(data, 0, size);
13374
13375 *((void **) ptr) = data;
13376 } else {
13377 data = ptr;
13378 }
13379
13380 /* copy record to local memory */
13381 if (access_mode & MODE_READ && data != NULL) {
13382 status = db_get_record(hDB, hKey, data, &size, 0);
13383 if (status != DB_SUCCESS) {
13385 cm_msg(MERROR, "db_open_record", "cannot get record, db_get_record() status %d", status);
13386 return DB_NO_MEMORY;
13387 }
13388 }
13389
13390 /* copy local record to ODB */
13391 if (access_mode & MODE_WRITE) {
13392 /* only write to ODB if not in MODE_ALLOC */
13393 if ((access_mode & MODE_ALLOC) == 0) {
13394 status = db_set_record(hDB, hKey, data, size, 0);
13395 if (status != DB_SUCCESS) {
13397 cm_msg(MERROR, "db_open_record", "cannot set record, db_set_record() status %d", status);
13398 return DB_NO_MEMORY;
13399 }
13400 }
13401
13402 /* init a local copy of the record */
13403 _record_list[idx].copy = malloc(size);
13404 if (_record_list[idx].copy == NULL) {
13405 cm_msg(MERROR, "db_open_record", "not enough memory");
13406 return DB_NO_MEMORY;
13407 }
13408
13409 memcpy(_record_list[idx].copy, data, size);
13410 }
13411
13412 /* initialize record list */
13415 _record_list[idx].access_mode = access_mode;
13417 _record_list[idx].buf_size = size;
13418 _record_list[idx].dispatcher = dispatcher;
13420
13421 /* add record entry in database structure */
13422 return db_add_open_record(hDB, hKey, (WORD) (access_mode & ~MODE_ALLOC));
13423}
13424
13425/********************************************************************/
13452 WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info,
13453 const char *rec_str)
13454{
13455 if (rec_str) {
13456 int status;
13457 if (rec_size) {
13458 char* pbuf;
13459 int size = rec_size;
13460 pbuf = (char*)malloc(size);
13461 assert(pbuf != NULL);
13462 status = db_get_record1(hDB, hKey, pbuf, &size, 0, rec_str);
13463 free(pbuf);
13464 if (status != DB_SUCCESS)
13465 return status;
13466 }
13467
13469 if (status != DB_SUCCESS)
13470 return status;
13471 }
13472
13473 return db_open_record(hDB, hKey, ptr, rec_size, access_mode, dispatcher, info);
13474}
13475
13476/********************************************************************/
13484{
13485#ifdef LOCAL_ROUTINES
13486 {
13487 INT i;
13488
13489 for (i = 0; i < _record_list_entries; i++)
13490 if (_record_list[i].handle == hKey && _record_list[i].hDB == hDB)
13491 break;
13492
13493 if (i == _record_list_entries)
13494 return DB_INVALID_HANDLE;
13495
13496 /* remove record entry from database structure */
13498
13499 /* free local memory */
13500 if (_record_list[i].access_mode & MODE_ALLOC) {
13501 free(_record_list[i].data);
13503 }
13504
13505 if (_record_list[i].access_mode & MODE_WRITE) {
13506 free(_record_list[i].copy);
13508 }
13509
13510 memset(&_record_list[i], 0, sizeof(RECORD_LIST));
13511 }
13512#endif /* LOCAL_ROUTINES */
13513
13514 return DB_SUCCESS;
13515}
13516
13517/********************************************************************/
13525{
13526 INT i;
13527
13528 for (i = 0; i < _record_list_entries; i++) {
13529 if (_record_list[i].handle) {
13530 if (_record_list[i].access_mode & MODE_WRITE) {
13531 free(_record_list[i].copy);
13533 }
13534
13535 if (_record_list[i].access_mode & MODE_ALLOC) {
13536 free(_record_list[i].data);
13538 }
13539
13540 memset(&_record_list[i], 0, sizeof(RECORD_LIST));
13541 }
13542 }
13543
13544 if (_record_list_entries > 0) {
13546 free(_record_list);
13548 }
13549
13550 return DB_SUCCESS;
13551}
13552
13553/********************************************************************/
13563{
13564 INT i, size, status;
13565
13567
13568 /* check all record entries for matching key */
13569 for (i = 0; i < _record_list_entries; i++)
13570 if (_record_list[i].handle == hKeyRoot) {
13572
13573 /* get updated data if record not opened in write mode */
13574 if ((_record_list[i].access_mode & MODE_WRITE) == 0) {
13575 size = _record_list[i].buf_size;
13576 if (_record_list[i].data != NULL) {
13577 status = db_get_record(hDB, hKeyRoot, _record_list[i].data, &size, 0); // db_open_record() update
13578 //printf("db_open_record update status %d, size %d %d\n", status, _record_list[i].buf_size, size);
13579 }
13580
13581 /* call dispatcher if requested */
13582 if (_record_list[i].dispatcher)
13584 }
13585 }
13586
13587 /* check all watch entries for matching key */
13588 for (i = 0; i < _watch_list_entries; i++)
13589 if (_watch_list[i].handle == hKeyRoot) {
13591
13592 /* call dispatcher if requested */
13593 if (_watch_list[i].dispatcher)
13595 }
13596
13597 return status;
13598}
13599
13600/********************************************************************/
13610{
13611 char buffer[32];
13612
13613 int convert_flags = rpc_get_convert_flags();
13614
13615 NET_COMMAND* nc = (NET_COMMAND *) buffer;
13616
13618 nc->header.param_size = 4 * sizeof(INT);
13619 *((INT *) nc->param) = hDB;
13620 *((INT *) nc->param + 1) = hKeyRoot;
13621 *((INT *) nc->param + 2) = hKey;
13622 *((INT *) nc->param + 3) = index;
13623
13624 if (convert_flags) {
13627 rpc_convert_single(&nc->param[0], TID_UINT32, RPC_OUTGOING, convert_flags);
13628 rpc_convert_single(&nc->param[4], TID_UINT32, RPC_OUTGOING, convert_flags);
13629 rpc_convert_single(&nc->param[8], TID_UINT32, RPC_OUTGOING, convert_flags);
13630 rpc_convert_single(&nc->param[12], TID_UINT32, RPC_OUTGOING, convert_flags);
13631 }
13632
13633 /* send the update notification to the client */
13634 send_tcp(client_socket, buffer, sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT), 0);
13635
13636 return DB_SUCCESS;
13637}
13638
13639/********************************************************************/
13788{
13789 INT i;
13790
13791 for (i = 0; i < _record_list_entries; i++)
13792 if (_record_list[i].access_mode & MODE_WRITE) {
13793 if (memcmp(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size) != 0) {
13794 if (rpc_is_remote()) {
13795 int align = ss_get_struct_align();
13797 } else {
13799 }
13800 memcpy(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size);
13801 }
13802 }
13803
13804 return DB_SUCCESS;
13805}
13806
13807/*------------------------------------------------------------------*/
13808
13809/********************************************************************/
13823INT db_watch(HNDLE hDB, HNDLE hKey, void (*dispatcher) (INT, INT, INT, void*), void* info)
13824{
13825 INT idx, status;
13826 KEY key;
13827
13828 /* check for valid key */
13829 assert(hKey);
13830
13831 /* allocate new space for the local record list */
13832 if (_watch_list_entries == 0) {
13833 _watch_list = (WATCH_LIST *) malloc(sizeof(WATCH_LIST));
13834 assert(_watch_list != NULL);
13835 memset(_watch_list, 0, sizeof(WATCH_LIST));
13836 if (_watch_list == NULL) {
13837 cm_msg(MERROR, "db_watch", "not enough memory");
13838 return DB_NO_MEMORY;
13839 }
13840
13842 idx = 0;
13843 } else {
13844 /* check for a deleted entry */
13845 for (idx = 0; idx < _watch_list_entries; idx++)
13846 if (!_watch_list[idx].handle)
13847 break;
13848
13849 /* if not found, create new one */
13850 if (idx == _watch_list_entries) {
13852 if (_watch_list == NULL) {
13853 cm_msg(MERROR, "db_watch", "not enough memory");
13854 return DB_NO_MEMORY;
13855 }
13856
13858
13860 }
13861 }
13862
13863 /* check key */
13865 if (status != DB_SUCCESS) {
13867 std::string path = db_get_path(hDB, hKey);
13868 cm_msg(MERROR, "db_watch", "cannot get key \"%s\"", path.c_str());
13869 return DB_NO_MEMORY;
13870 }
13871
13872 /* check for read access */
13873 if (!(key.access_mode & MODE_READ)) {
13875 std::string path = db_get_path(hDB, hKey);
13876 cm_msg(MERROR, "db_watch", "cannot get key \"%s\"", path.c_str());
13877 return DB_NO_ACCESS;
13878 }
13879
13880 /* initialize record list */
13883 _watch_list[idx].dispatcher = dispatcher;
13885
13886 /* add record entry in database structure */
13888}
13889
13890
13891/********************************************************************/
13899{
13900#ifdef LOCAL_ROUTINES
13901 {
13902 INT i;
13903
13904 for (i = 0; i < _watch_list_entries; i++)
13905 if (_watch_list[i].handle == hKey && _watch_list[i].hDB == hDB)
13906 break;
13907
13908 if (i == _watch_list_entries)
13909 return DB_INVALID_HANDLE;
13910
13911 /* remove record entry from database structure */
13913
13914 memset(&_watch_list[i], 0, sizeof(WATCH_LIST));
13915 }
13916#endif /* LOCAL_ROUTINES */
13917
13918 return DB_SUCCESS;
13919}
13920
13921/********************************************************************/
13929{
13930 for (int i = _watch_list_entries-1; i >= 0 ; i--) {
13931 if ((_watch_list[i].hDB == 0) && (_watch_list[i].handle == 0)) {
13932 // empty or deleted watch list entry
13933 continue;
13934 }
13936 }
13937
13938 return DB_SUCCESS;
13939}
13940
13941/*------------------------------------------------------------------*/
13942
13943/* C++ wrapper for db_get_value */
13944
13945INT EXPRT db_get_value_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int index, std::string* s, BOOL create, int create_string_length)
13946{
13947 int status;
13948 int hkey;
13949
13950 //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);
13951
13952 if (index > 0 && create) {
13953 cm_msg(MERROR, "db_get_value_string", "cannot resize odb string arrays, please use db_resize_string() instead");
13954 return DB_OUT_OF_RANGE;
13955 }
13956
13957 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
13958 if (status == DB_SUCCESS) {
13959 KEY key;
13961 if (status != DB_SUCCESS)
13962 return status;
13964 return DB_OUT_OF_RANGE;
13965 int size = key.item_size;
13966 if (size == 0) {
13967 if (s)
13968 *s = "";
13969 //printf("db_get_value_string: return empty string, item_size %d\n", key.item_size);
13970 return DB_SUCCESS;
13971 }
13972 char* buf = (char*)malloc(size);
13973 assert(buf != NULL);
13974 status = db_get_data_index(hdb, hkey, buf, &size, index, TID_STRING);
13975 if (status != DB_SUCCESS) {
13976 free(buf);
13977 return status;
13978 }
13979 if (s)
13980 *s = buf;
13981 free(buf);
13982 //printf("db_get_value_string: return [%s], len %d, item_size %d, size %d\n", s->c_str(), s->length(), key.item_size, size);
13983 return DB_SUCCESS;
13984 } else if (!create) {
13985 // does not exist and not asked to create it
13986 return status;
13987 } else {
13988 //printf("db_get_value_string: creating [%s]\n", key_name);
13990 if (status != DB_SUCCESS)
13991 return status;
13992 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
13993 if (status != DB_SUCCESS)
13994 return status;
13995 assert(s != NULL);
13996 if (create_string_length == 0) {
13997 int size = s->length() + 1; // 1 byte for final \0
13998 status = db_set_data_index(hdb, hkey, s->c_str(), size, index, TID_STRING);
13999 } else {
14000 char* buf = (char*)malloc(create_string_length);
14001 assert(buf);
14002 mstrlcpy(buf, s->c_str(), create_string_length);
14004 free(buf);
14005 }
14006 if (status != DB_SUCCESS)
14007 return status;
14008 //printf("db_get_value_string: created with value [%s]\n", s->c_str());
14009 return DB_SUCCESS;
14010 }
14011 // NOT REACHED
14012}
14013
14014/* C++ wrapper for db_set_value */
14015
14016INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string* s)
14017{
14018 assert(s != NULL);
14019 int size = s->length() + 1; // 1 byte for final \0
14020 //printf("db_set_value_string: key_name [%s], string [%s], size %d\n", key_name, s->c_str(), size);
14021 return db_set_value(hDB, hKeyRoot, key_name, s->c_str(), size, 1, TID_STRING);
14022}
14023
14024/********************************************************************/
14036INT EXPRT db_resize_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int num_values, int max_string_length)
14037{
14038 int status;
14039 int hkey;
14040
14041 //printf("db_resize_string: key_name [%s], num_values %d, max_string_length %d\n", key_name, num_values, max_string_length);
14042
14043 int old_num_values = 0;
14044 int old_item_size = 0;
14045 int old_size = 0;
14046 char* old_data = NULL;
14047
14048 if (key_name) {
14049 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
14050 } else {
14051 hkey = hKeyRoot;
14053 }
14054 if (status == DB_SUCCESS) {
14055 KEY key;
14057 if (status != DB_SUCCESS)
14058 return status;
14062 if (old_size > 0) {
14063 old_data = (char*)malloc(old_size);
14064 assert(old_data != NULL);
14065 int size = old_size;
14067 if (status != DB_SUCCESS) {
14068 free(old_data);
14069 return status;
14070 }
14071 assert(size == old_size);
14072 }
14073 } else {
14075 if (status != DB_SUCCESS)
14076 return status;
14077 status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
14078 if (status != DB_SUCCESS)
14079 return status;
14080 }
14081
14082 //printf("old_num_values %d, old_item_size %d, old_size %d\n", old_num_values, old_item_size, old_size);
14083
14084 int item_size = max_string_length;
14085
14086 if (item_size < 1)
14087 item_size = old_item_size;
14088
14089 if (num_values < 1)
14090 num_values = old_num_values;
14091
14092 int new_size = num_values * item_size;
14093 char* new_data = (char*)malloc(new_size);
14094 assert(new_data);
14095
14097
14098 if (old_data) {
14099 int num = old_num_values;
14100 if (num > num_values)
14101 num = num_values;
14102
14103 //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);
14104
14105 for (int i=0; i<num; i++) {
14106 const char* old_ptr = old_data + i*old_item_size;
14107 char* new_ptr = new_data + i*item_size;
14108 mstrlcpy(new_ptr, old_ptr, item_size);
14109 }
14110 }
14111
14113
14114 if (old_data)
14115 free(old_data);
14116 if (new_data)
14117 free(new_data);
14118
14119 return status;
14120}
14121
14122MJsonNode* db_scl(HNDLE hDB)
14123{
14124#ifdef LOCAL_ROUTINES
14125 MJsonNode* scl = MJsonNode::MakeObject();
14126 MJsonNode* clients = MJsonNode::MakeArray();
14127
14128 scl->AddToObject("now", MJsonNode::MakeNumber(ss_time_sec()));
14129 scl->AddToObject("clients", clients);
14130
14131 /* lock database */
14133
14134 DATABASE *pdb = &_database[hDB - 1];
14135 DATABASE_HEADER *pheader = pdb->database_header;
14136
14138
14139 /* list clients */
14140 for (int i = 0; i < pheader->max_client_index; i++) {
14141 DATABASE_CLIENT *pclient = &pheader->client[i];
14142 if (pclient->pid) {
14143 MJsonNode* c = MJsonNode::MakeObject();
14144 c->AddToObject("slot", MJsonNode::MakeNumber(i));
14145 c->AddToObject("pid", MJsonNode::MakeNumber(pclient->pid));
14146 c->AddToObject("name", MJsonNode::MakeString(pclient->name));
14147 std::string path = msprintf("/System/Clients/%d/Host", pclient->pid);
14148 const KEY* pkey = db_find_pkey_locked(pheader, NULL, path.c_str(), NULL, NULL);
14149 if (pkey) {
14150 int host_size = pkey->total_size;
14151 char* host = (char*)malloc(host_size);
14152 assert(host != NULL);
14153 db_get_data_locked(pheader, pkey, 0, host, &host_size, TID_STRING, NULL);
14154 c->AddToObject("host", MJsonNode::MakeString(host));
14155 free(host);
14156 }
14157 c->AddToObject("watchdog_timeout_millisec", MJsonNode::MakeNumber(pclient->watchdog_timeout));
14158 // "now" and "last_activity" is millisecond time wrapped around at 32 bits, must do unsigned 32-bit math here, not in javascript
14159 c->AddToObject("last_activity_millisec", MJsonNode::MakeNumber(now - pclient->last_activity));
14160 clients->AddToArray(c);
14161 }
14162 }
14163
14165
14166 return scl;
14167#else
14168 return MJsonNode::MakeNull();
14169#endif
14170}
14171
14172MJsonNode* db_sor(HNDLE hDB, const char* root_path)
14173{
14174 //printf("db_sor(%s)\n", root_path);
14175 assert(root_path != NULL);
14176#ifdef LOCAL_ROUTINES
14177 size_t root_path_len = strlen(root_path);
14178 MJsonNode* sor = MJsonNode::MakeArray();
14179
14180 /* lock database */
14182
14183 DATABASE *pdb = &_database[hDB - 1];
14184 DATABASE_HEADER *pheader = pdb->database_header;
14185
14186 /* list clients */
14187 for (int i = 0; i < pheader->max_client_index; i++) {
14188 DATABASE_CLIENT *pclient = &pheader->client[i];
14189 if (pclient->pid) {
14190 for (int j = 0; j < pclient->max_index; j++) {
14191 std::string path = db_get_path_locked(pheader, pclient->open_record[j].handle);
14192 if (path.length() < root_path_len)
14193 continue;
14194 if (strncmp(root_path, path.c_str(), root_path_len) != 0)
14195 continue;
14196 MJsonNode* c = MJsonNode::MakeObject();
14197 c->AddToObject("name", MJsonNode::MakeString(pclient->name));
14198 //c->AddToObject("handle", MJsonNode::MakeNumber(pclient->open_record[j].handle));
14199 c->AddToObject("access_mode", MJsonNode::MakeNumber(pclient->open_record[j].access_mode));
14200 c->AddToObject("flags", MJsonNode::MakeNumber(pclient->open_record[j].flags));
14201 c->AddToObject("path", MJsonNode::MakeString(path.c_str()));
14202 sor->AddToArray(c);
14203 }
14204 }
14205 }
14206
14208
14209 return sor;
14210#else
14211 return MJsonNode::MakeNull();
14212#endif
14213}
14214
/* end of odbfunctionc */
14216
14217/* emacs
14218 * Local Variables:
14219 * tab-width: 8
14220 * c-basic-offset: 3
14221 * indent-tabs-mode: nil
14222 * End:
14223 */
#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:3339
#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:227
#define MSG_ODB
Definition msystem.h:303
INT ss_get_struct_align()
Definition system.cxx:1321
INT ss_suspend_get_odb_port(INT *port)
Definition system.cxx:4399
bool ss_is_valid_utf8(const char *string)
Definition system.cxx:8146
INT ss_mutex_release(MUTEX_T *mutex)
Definition system.cxx:3229
int ss_isnan(double x)
Definition system.cxx:8039
INT ss_shm_flush(const char *name, const void *adr, size_t size, HNDLE handle, bool wait_for_thread)
Definition system.cxx:1178
DWORD ss_millitime()
Definition system.cxx:3465
INT ss_semaphore_create(const char *name, HNDLE *semaphore_handle)
Definition system.cxx:2532
INT ss_mutex_delete(MUTEX_T *mutex)
Definition system.cxx:3283
INT ss_getpid(void)
Definition system.cxx:1379
INT ss_mutex_create(MUTEX_T **mutex, BOOL recursive)
Definition system.cxx:3013
INT ss_shm_open(const char *name, INT size, void **adr, size_t *shm_size, HNDLE *handle, BOOL get_size)
Definition system.cxx:326
INT ss_semaphore_release(HNDLE semaphore_handle)
Definition system.cxx:2853
void ss_stack_history_entry(char *tag)
Definition system.cxx:8100
std::string ss_tid_to_string(midas_thread_t thread_id)
Definition system.cxx:1643
DWORD ss_time()
Definition system.cxx:3534
INT ss_resume(INT port, const char *message)
Definition system.cxx:4916
midas_thread_t ss_gettid(void)
Definition system.cxx:1591
INT ss_semaphore_delete(HNDLE semaphore_handle, INT destroy_flag)
Definition system.cxx:2941
double ss_time_sec()
Definition system.cxx:3539
double ss_nan()
Definition system.cxx:8018
INT ss_semaphore_wait_for(HNDLE semaphore_handle, DWORD timeout_millisec)
Definition system.cxx:2711
INT ss_shm_unprotect(HNDLE handle, void **adr, size_t shm_size, BOOL read, BOOL write, const char *caller_name)
Definition system.cxx:1062
INT ss_shm_close(const char *name, void *adr, size_t shm_size, HNDLE handle, INT destroy_flag)
Definition system.cxx:757
INT send_tcp(int sock, char *buffer, DWORD buffer_size, INT flags)
Definition system.cxx:5357
BOOL ss_pid_exists(int pid)
Definition system.cxx:1442
INT ss_shm_protect(HNDLE handle, void *adr, size_t shm_size)
Definition system.cxx:1005
INT ss_mutex_wait_for(MUTEX_T *mutex, INT timeout)
Definition system.cxx:3109
INT cm_msg_flush_buffer()
Definition midas.cxx:879
INT cm_msg(INT message_type, const char *filename, INT line, const char *routine, const char *format,...)
Definition midas.cxx:929
BOOL equal_ustring(const char *str1, const char *str2)
Definition odb.cxx:3206
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:4130
INT db_sprintfh(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10989
INT db_flush_database(HNDLE hDB)
Definition odb.cxx:2273
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:9764
INT db_remove_open_record(HNDLE hDB, HNDLE hKey, BOOL lock)
Definition odb.cxx:12506
static void check_open_keys(HNDLE hDB, HNDLE hKey, KEY *pkey, INT level, void *info)
Definition odb.cxx:12742
INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT idx, DWORD type)
Definition odb.cxx:6898
INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
Definition odb.cxx:3861
INT db_send_changed_records()
Definition odb.cxx:13787
INT db_find_link(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4279
INT db_check_client(HNDLE hDB, HNDLE hKeyClient)
Definition odb.cxx:3064
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:7062
INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT *buf_size, DWORD type, BOOL create)
Definition odb.cxx:5420
static int db_delete_client_info_wlocked(HNDLE hDB, DATABASE_HEADER *pheader, int pid, db_err_msg **msg)
Definition odb.cxx:2765
INT db_reorder_key(HNDLE hDB, HNDLE hKey, INT idx)
Definition odb.cxx:6366
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:1032
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:10532
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:13300
INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE *hDB, const char *client_name)
Definition odb.cxx:1789
static int db_paste_node(HNDLE hDB, HNDLE hKeyRoot, PMXML_NODE node)
Definition odb.cxx:8785
INT db_get_open_records(HNDLE hDB, HNDLE hKey, char *str, INT buf_size, BOOL fix)
Definition odb.cxx:5193
INT db_paste_xml(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
Definition odb.cxx:9003
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:7106
void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
Definition odb.cxx:2832
INT db_show_mem(HNDLE hDB, char **result, BOOL verbose)
Definition odb.cxx:672
static const KEY * db_resolve_link_locked(const DATABASE_HEADER *, const KEY *, int *pstatus, db_err_msg **)
Definition odb.cxx:1096
INT db_lock_database(HNDLE hDB)
Definition odb.cxx:2460
static bool db_validate_hkey(const DATABASE_HEADER *pheader, HNDLE hKey)
Definition odb.cxx:919
std::string strcomb1(const char **list)
Definition odb.cxx:600
INT db_delete_key1(HNDLE hDB, HNDLE hKey, INT level, BOOL follow_links)
Definition odb.cxx:3674
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:9816
INT db_save_xml(HNDLE hDB, HNDLE hKey, const char *filename)
Definition odb.cxx:9485
INT db_set_link_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7430
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:4036
INT db_get_path(HNDLE hDB, HNDLE hKey, char *path, INT buf_size)
Definition odb.cxx:4995
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:10357
INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align, const char *rec_str)
Definition odb.cxx:11812
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:3369
INT db_copy(HNDLE hDB, HNDLE hKey, char *buffer, INT *buffer_size, const char *path)
Definition odb.cxx:8222
INT db_set_link_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7759
BOOL strmatch(char *pattern, char *str)
Definition odb.cxx:3240
INT db_get_record_size(HNDLE hDB, HNDLE hKey, INT align, INT *buf_size)
Definition odb.cxx:11621
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:7153
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:5511
INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6544
INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
Definition odb.cxx:3313
INT db_check_record(HNDLE hDB, HNDLE hKey, const char *keyname, const char *rec_str, BOOL correct)
Definition odb.cxx:12981
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:10480
INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, int *buffer_size, bool header)
Definition odb.cxx:9042
INT db_unlock_database(HNDLE hDB)
Definition odb.cxx:2582
MJsonNode * db_scl(HNDLE hDB)
Definition odb.cxx:14122
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:10632
INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, INT align)
Definition odb.cxx:11715
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:9308
INT db_unwatch(HNDLE hDB, HNDLE hKey)
Definition odb.cxx:13898
INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
Definition odb.cxx:8032
INT db_get_next_link(HNDLE hDB, HNDLE hKey, HNDLE *subkey_handle)
Definition odb.cxx:5821
INT db_scan_tree_link(HNDLE hDB, HNDLE hKey, INT level, void(*callback)(HNDLE, HNDLE, KEY *, INT, void *), void *info)
Definition odb.cxx:4851
INT db_save(HNDLE hDB, HNDLE hKey, const char *filename, BOOL bRemote)
Definition odb.cxx:9250
static bool db_validate_and_repair_db_wlocked(DATABASE_HEADER *pheader, db_err_msg **msg)
Definition odb.cxx:1661
INT db_allow_write_locked(DATABASE *p, const char *caller_name)
Definition odb.cxx:2553
INT db_scan_tree(HNDLE hDB, HNDLE hKey, INT level, INT(*callback)(HNDLE, HNDLE, KEY *, INT, void *), void *info)
Definition odb.cxx:4772
INT db_get_key(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6024
static INT db_get_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, KEY *key, db_err_msg **msg)
Definition odb.cxx:5947
static int _global_open_count
Definition odb.cxx:12740
INT db_get_link(HNDLE hDB, HNDLE hKey, KEY *key)
Definition odb.cxx:6077
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:984
static bool db_validate_data_offset(const DATABASE_HEADER *pheader, int offset)
Definition odb.cxx:907
static int db_remove_open_record_wlocked(DATABASE *pdb, DATABASE_HEADER *pheader, HNDLE hKey)
Definition odb.cxx:12460
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:10255
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:12056
INT db_load(HNDLE hDB, HNDLE hKeyRoot, const char *filename, BOOL bRemote)
Definition odb.cxx:8131
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:13945
INT db_sprintff(char *string, const char *format, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10925
static void db_delete_client_wlocked(DATABASE_HEADER *pheader, int jclient, db_err_msg **msg)
Definition odb.cxx:2730
static void add_to_buf(struct print_key_info_buf *buf, const char *s)
Definition odb.cxx:622
INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD *timeout, DWORD *last)
Definition odb.cxx:3019
INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:7653
static HNDLE db_pkey_to_hkey(const DATABASE_HEADER *pheader, const KEY *pkey)
Definition odb.cxx:1027
static bool db_validate_key_offset(const DATABASE_HEADER *pheader, int offset)
Definition odb.cxx:895
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:9123
INT db_find_keys(HNDLE hDB, HNDLE hKeyRoot, char *odbpath, std::vector< HNDLE > &hKeyVector)
Definition odb.cxx:4591
INT db_save_string(HNDLE hDB, HNDLE hKey, const char *file_name, const char *string_name, BOOL append)
Definition odb.cxx:10693
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:1521
INT db_copy_json_index(HNDLE hDB, HNDLE hKey, int index, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:10224
INT db_close_all_records()
Definition odb.cxx:13524
static const KEY * db_enum_next_locked(const DATABASE_HEADER *pheader, const KEY *pdir, const KEY *pkey, db_err_msg **msg)
Definition odb.cxx:1088
INT db_paste(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
Definition odb.cxx:8492
INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER *writer)
Definition odb.cxx:9351
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:13823
static std::string db_get_path_locked(const DATABASE_HEADER *pheader, HNDLE hKey)
Definition odb.cxx:4967
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:6196
INT db_close_all_databases(void)
Definition odb.cxx:2365
INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7220
INT db_enum_link(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5730
static const KEY * db_get_pkey(const DATABASE_HEADER *pheader, HNDLE hKey, int *pstatus, const char *caller, db_err_msg **msg)
Definition odb.cxx:932
INT db_copy_json_ls(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:10426
void name2c(char *str)
Definition odb.cxx:9102
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:7019
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:10389
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:5151
INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
Definition odb.cxx:10849
INT db_update_last_activity(DWORD millitime)
Definition odb.cxx:2697
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:10513
void strarrayindex(char *odbpath, int *index1, int *index2)
Definition odb.cxx:3278
INT db_set_data1(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
Definition odb.cxx:7318
DATABASE_CLIENT * db_get_my_client_locked(DATABASE *pdb)
Definition odb.cxx:1367
static int db_set_mode_wlocked(DATABASE_HEADER *, KEY *, WORD mode, int recurse, db_err_msg **)
Definition odb.cxx:7984
INT db_get_data1(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type, INT *num_values)
Definition odb.cxx:6761
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:10448
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:5305
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:5266
static int db_find_open_records(HNDLE hDB, HNDLE hKey, KEY *key, INT level, void *xresult)
Definition odb.cxx:5109
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:12547
static void json_write_data(char **buffer, int *buffer_size, int *buffer_end, int level, const KEY *key, const char *p)
Definition odb.cxx:9647
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:3870
INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4084
static const KEY * db_enum_first_locked(const DATABASE_HEADER *pheader, const KEY *pkey, db_err_msg **msg)
Definition odb.cxx:1058
INT db_update_record_local(INT hDB, INT hKeyRoot, INT hKey, int index)
Definition odb.cxx:13562
INT db_find_link1(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE *subhKey)
Definition odb.cxx:4444
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:11445
INT db_copy_json_array(HNDLE hDB, HNDLE hKey, char **buffer, int *buffer_size, int *buffer_end)
Definition odb.cxx:10155
MJsonNode * db_sor(HNDLE hDB, const char *root_path)
Definition odb.cxx:14172
INT db_get_link_data(HNDLE hDB, HNDLE hKey, void *data, INT *buf_size, DWORD type)
Definition odb.cxx:6661
void db_set_watchdog_params(DWORD timeout)
Definition odb.cxx:2981
static void db_validate_sizes()
Definition odb.cxx:1413
INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
Definition odb.cxx:13609
INT db_rename_key(HNDLE hDB, HNDLE hKey, const char *name)
Definition odb.cxx:6266
BOOL ends_with_ustring(const char *str, const char *suffix)
Definition odb.cxx:3227
void db_cleanup2(const char *client_name, int ignore_timeout, DWORD actual_time, const char *who)
Definition odb.cxx:2902
void json_write(char **buffer, int *buffer_size, int *buffer_end, int level, const char *s, int quoted)
Definition odb.cxx:9532
INT EXPRT db_paste_json(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
static bool is_utf8(const char *string)
Definition odb.cxx:824
INT db_get_key_time(HNDLE hDB, HNDLE hKey, DWORD *delta)
Definition odb.cxx:6137
int db_delete_client_info(HNDLE hDB, int pid)
Definition odb.cxx:2796
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:1147
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:835
INT db_set_client_name(HNDLE hDB, const char *client_name)
Definition odb.cxx:2407
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:5370
INT db_notify_clients_array(HNDLE hDB, HNDLE hKeys[], INT size)
Definition odb.cxx:12631
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:13451
INT db_merge_data(HNDLE hDB, HNDLE hKeyRoot, const char *name, void *data, INT data_size, INT num_values, INT type)
Definition odb.cxx:7933
INT db_notify_clients(HNDLE hDB, HNDLE hKeyMod, int index, BOOL bWalk)
Definition odb.cxx:12602
INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
Definition odb.cxx:12299
INT db_set_data_index1(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type, BOOL bNotify)
Definition odb.cxx:7833
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:11873
INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE *subkey_handle)
Definition odb.cxx:5591
INT EXPRT db_resize_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int num_values, int max_string_length)
Definition odb.cxx:14036
INT db_close_record(HNDLE hDB, HNDLE hKey)
Definition odb.cxx:13483
static int db_validate_open_records_wlocked(DATABASE_HEADER *pheader, db_err_msg **msg)
Definition odb.cxx:1598
INT db_create_record(HNDLE hDB, HNDLE hKey, const char *orig_key_name, const char *init_str)
Definition odb.cxx:12808
INT db_sscanf(const char *data_str, void *data, INT *data_size, INT i, DWORD tid)
Definition odb.cxx:11320
static db_err_msg * _last_error_message
Definition odb.cxx:111
INT db_unwatch_all()
Definition odb.cxx:13928
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:12371
INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string *s)
Definition odb.cxx:14016
INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
Definition odb.cxx:2669
INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
Definition odb.cxx:7507
INT db_get_record2(HNDLE hDB, HNDLE hKey, void *data, INT *xbuf_size, INT align, const char *rec_str, BOOL correct)
Definition odb.cxx:12176
INT db_create_link(HNDLE hDB, HNDLE hKey, const char *link_name, const char *destination)
Definition odb.cxx:3606
static void merge_records(HNDLE hDB, HNDLE hKey, KEY *pkey, INT level, void *info)
Definition odb.cxx:12667
INT db_close_database(HNDLE hDB)
Definition odb.cxx:2157
INT db_protect_database(HNDLE hDB)
Definition odb.cxx:3172
static INT print_key_info(HNDLE hDB, HNDLE hKey, KEY *pkey, INT level, void *info)
Definition odb.cxx:640
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:3192
INT db_get_free_mem(HNDLE hDB, INT *key_size, INT *data_size)
Definition odb.cxx:788
static DATABASE * _database
Definition odb.cxx:54
static void json_ensure_decimal_dot(char *str)
Definition odb.cxx:9632
#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:11728
#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:12783
const char * rpc_tid_name_old(INT id)
Definition midas.cxx:11793
#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:13685
const char * rpc_tid_name(INT id)
Definition midas.cxx:11786
#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:11800
#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:12840
#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:13052
INT rpc_tid_size(INT id)
Definition midas.cxx:11779
void rpc_convert_single(void *data, INT tid, INT flags, INT convert_flags)
Definition midas.cxx:11703
#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:1476
INT _semaphore_elog
Definition midas.cxx:1477
INT _semaphore_history
Definition midas.cxx:1478
std::string msprintf(const char *format,...)
Definition midas.cxx:419
#define JSFLAG_RECURSE
Definition midas.h:1722
#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:1717
int INT
Definition midas.h:129
#define JSFLAG_FOLLOW_LINKS
Definition midas.h:1721
#define JSFLAG_LOWERCASE
Definition midas.h:1723
#define MATTRPRINTF(a, b)
Definition midas.h:221
#define JSFLAG_OMIT_LAST_WRITTEN
Definition midas.h:1725
#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:1583
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:1724
#define WATCHDOG_INTERVAL
Definition midas.h:288
#define JS_LEVEL_1
Definition midas.h:1718
#define MAX_OPEN_RECORDS
Definition midas.h:276
#define TRUE
Definition midas.h:182
#define JS_MUST_BE_SUBDIR
Definition midas.h:1719
unsigned char UINT8
Definition midas.h:136
#define POINTER_T
Definition midas.h:166
#define JSFLAG_SAVE_KEYS
Definition midas.h:1720
unsigned int UINT32
Definition midas.h:140
#define JSFLAG_OMIT_OLD
Definition midas.h:1726
#define RPC_FIXARRAY
Definition midas.h:1581
#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:1213
DWORD last_activity
Definition msystem.h:424
char name[NAME_LENGTH]
Definition msystem.h:418
OPEN_RECORD open_record[MAX_OPEN_RECORDS]
Definition msystem.h:428
DWORD watchdog_timeout
Definition msystem.h:425
char name[NAME_LENGTH]
Definition msystem.h:433
DATABASE_CLIENT client[MAX_CLIENTS]
Definition msystem.h:443
INT first_free_data
Definition msystem.h:441
INT max_client_index
Definition msystem.h:436
BOOL protect
Definition msystem.h:460
void * database_data
Definition msystem.h:454
INT timeout
Definition msystem.h:464
HNDLE shm_size
Definition msystem.h:458
BOOL protect_read
Definition msystem.h:461
INT lock_cnt
Definition msystem.h:456
INT client_index
Definition msystem.h:452
HNDLE semaphore
Definition msystem.h:455
HNDLE shm_handle
Definition msystem.h:459
DATABASE_HEADER * database_header
Definition msystem.h:453
BOOL attached
Definition msystem.h:451
BOOL protect_write
Definition msystem.h:462
BOOL inside_lock_unlock
Definition msystem.h:465
void * shm_adr
Definition msystem.h:457
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:293
char param[32]
Definition msystem.h:294
WORD access_mode
Definition msystem.h:474
void(* dispatcher)(INT, INT, void *)
Definition msystem.h:478
void * info
Definition msystem.h:479
INT buf_size
Definition msystem.h:477
HNDLE handle
Definition msystem.h:472
void * data
Definition msystem.h:475
void * copy
Definition msystem.h:476
HNDLE hDB
Definition msystem.h:473
Definition midas.h:1232
DATABASE_HEADER * pheader
Definition odb.cxx:1512
HNDLE handle
Definition msystem.h:486
HNDLE hDB
Definition msystem.h:487
void * info
Definition msystem.h:489
void(* dispatcher)(INT, INT, INT, void *info)
Definition msystem.h:488
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:1312
static double comma(double a, double b)
Definition tinyexpr.c:248
static te_expr * list(state *s)
Definition tinyexpr.c:567