Line data Source code
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 :
14 : /**dox***************************************************************/
15 : /** @file odb.c
16 : The Online Database file
17 : */
18 :
19 : /** @defgroup odbfunctionc ODB Functions (db_xxx)
20 : */
21 :
22 : /**dox***************************************************************/
23 : /** @addtogroup odbfunctionc
24 : *
25 : * @{ */
26 :
27 : /**dox***************************************************************/
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
54 : static DATABASE *_database;
55 : static INT _database_entries = 0;
56 : #endif
57 :
58 : static RECORD_LIST *_record_list;
59 : static INT _record_list_entries = 0;
60 :
61 : static WATCH_LIST *_watch_list;
62 : static INT _watch_list_entries = 0;
63 :
64 : INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER * writer);
65 :
66 : /*------------------------------------------------------------------*/
67 :
68 : #ifdef LOCAL_ROUTINES
69 : typedef struct db_err_msg_struct db_err_msg;
70 : static void db_msg(db_err_msg** msg, INT message_type, const char *filename, INT line, const char *routine, const char *format, ...) MATTRPRINTF(6,7);
71 : static void db_print_msg(const db_err_msg* msg);
72 : static void db_flush_msg(db_err_msg** msg);
73 : static INT db_find_key_locked(const DATABASE_HEADER *pheader, HNDLE hKey, const char *key_name, HNDLE * subhKey, db_err_msg** msg);
74 : static const KEY* db_find_pkey_locked(const DATABASE_HEADER *pheader, const KEY* pkey, const char *key_name, int *pstatus, db_err_msg** msg);
75 : static std::string db_get_path_locked(const DATABASE_HEADER* pheader, HNDLE hKey);
76 : static std::string db_get_path_locked(const DATABASE_HEADER* pheader, const KEY *pkey);
77 : 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);
78 : static int db_set_mode_wlocked(DATABASE_HEADER*,KEY*,WORD mode,int recurse,db_err_msg**);
79 : static const KEY* db_resolve_link_locked(const DATABASE_HEADER*, const KEY*,int *pstatus, db_err_msg**);
80 : static int db_notify_clients_locked(const DATABASE_HEADER* pheader, HNDLE hDB, HNDLE hKeyMod, int index, BOOL bWalk, db_err_msg** msg);
81 : static int db_create_key_wlocked(DATABASE_HEADER* pheader, KEY* parentKey, const char *key_name, DWORD type, KEY** pnewkey, db_err_msg** msg);
82 : 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);
83 : 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);
84 : 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);
85 : 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);
86 : 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);
87 : 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);
88 : static int db_remove_open_record_wlocked(DATABASE* pdb, DATABASE_HEADER* pheader, HNDLE hKey);
89 : #endif // LOCAL_ROUTINES
90 :
91 : /*------------------------------------------------------------------*/
92 :
93 : /********************************************************************\
94 : * *
95 : * db_msg_xxx error message handling *
96 : * *
97 : \********************************************************************/
98 :
99 : #ifdef LOCAL_ROUTINES
100 :
101 : struct db_err_msg_struct
102 : {
103 : db_err_msg *next = NULL;
104 : int message_type = 0;
105 : std::string filename;
106 : int line = 0;
107 : std::string routine;
108 : std::string text;
109 : };
110 :
111 : static db_err_msg* _last_error_message = NULL; // for debuging core dumps
112 :
113 0 : void db_print_msg(const db_err_msg* msg)
114 : {
115 0 : while (msg != NULL) {
116 0 : 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 0 : msg = msg->next;
118 : }
119 0 : }
120 :
121 7 : void db_msg(db_err_msg** msgp, INT message_type, const char *filename, INT line, const char *routine, const char *format, ...)
122 : {
123 7 : if (!msgp)
124 0 : return;
125 :
126 : va_list argptr;
127 : char message[1000];
128 :
129 : /* print argument list into message */
130 7 : va_start(argptr, format);
131 7 : vsnprintf(message, sizeof(message)-1, format, argptr);
132 7 : va_end(argptr);
133 7 : message[sizeof(message)-1] = 0; // ensure string is NUL-terminated
134 :
135 7 : db_err_msg* msg = new db_err_msg;
136 :
137 7 : msg->next = NULL;
138 7 : msg->message_type = message_type;
139 7 : msg->filename = filename;
140 7 : msg->line = line;
141 7 : msg->routine = routine;
142 7 : msg->text = message;
143 :
144 7 : _last_error_message = msg;
145 :
146 : //printf("new message:\n");
147 : //db_print_msg(msg);
148 :
149 7 : if (*msgp == NULL) {
150 7 : *msgp = msg;
151 7 : return;
152 : }
153 :
154 : // append new message to the end of the list
155 0 : db_err_msg *m = (*msgp);
156 0 : while (m->next != NULL) {
157 0 : m = m->next;
158 : }
159 0 : assert(m->next == NULL);
160 0 : m->next = msg;
161 :
162 : //printf("Message list with added new message:\n");
163 : //db_print_msg(*msgp);
164 0 : return;
165 : }
166 :
167 7 : void db_flush_msg(db_err_msg** msgp)
168 : {
169 7 : db_err_msg *msg = *msgp;
170 7 : *msgp = NULL;
171 :
172 : if (/* DISABLES CODE */ (0)) {
173 : printf("db_flush_msg: %p\n", msg);
174 : db_print_msg(msg);
175 : }
176 :
177 14 : while (msg != NULL) {
178 7 : cm_msg(msg->message_type, msg->filename.c_str(), msg->line, msg->routine.c_str(), "%s", msg->text.c_str());
179 7 : db_err_msg* next = msg->next;
180 7 : msg->message_type = 0;
181 7 : msg->next = NULL;
182 7 : delete msg;
183 7 : msg = next;
184 : }
185 7 : }
186 :
187 : #endif // LOCAL_ROUTINES
188 :
189 : /*------------------------------------------------------------------*/
190 :
191 : #ifdef LOCAL_ROUTINES
192 :
193 : /********************************************************************\
194 : * *
195 : * Shared Memory Allocation *
196 : * *
197 : \********************************************************************/
198 :
199 : static bool db_validate_key_offset(const DATABASE_HEADER * pheader, int offset);
200 :
201 : /*------------------------------------------------------------------*/
202 190 : static void *malloc_key(DATABASE_HEADER * pheader, INT size, const char* caller)
203 : {
204 190 : FREE_DESCRIP *pfree, *pfound, *pprev = NULL;
205 :
206 190 : if (size == 0)
207 0 : return NULL;
208 :
209 : /* quadword alignment for alpha CPU */
210 190 : size = ALIGN8(size);
211 :
212 : //printf("malloc_key(%d) from [%s]\n", size, caller);
213 :
214 190 : if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
215 0 : return NULL;
216 : }
217 :
218 : /* search for free block */
219 190 : 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 307 : while (pfree->size < size && pfree->next_free) {
224 117 : if (!db_validate_key_offset(pheader, pfree->next_free)) {
225 0 : return NULL;
226 : }
227 117 : pprev = pfree;
228 117 : 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 190 : if (pfree->size < size)
236 0 : return 0;
237 :
238 190 : pfound = pfree;
239 :
240 : /* if found block is first in list, correct pheader */
241 190 : if (pfree == (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key)) {
242 77 : if (size < pfree->size) {
243 : /* free block is only used partially */
244 75 : pheader->first_free_key += size;
245 75 : pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
246 :
247 75 : pfree->size = pfound->size - size;
248 75 : pfree->next_free = pfound->next_free;
249 : } else {
250 : /* free block is used totally */
251 2 : pheader->first_free_key = pfree->next_free;
252 : }
253 : } else {
254 : /* check if free block is used totally */
255 113 : if (pfound->size - size < (int) sizeof(FREE_DESCRIP)) {
256 : /* skip block totally */
257 4 : pprev->next_free = pfound->next_free;
258 : } else {
259 : /* decrease free block */
260 109 : pfree = (FREE_DESCRIP *) ((char *) pfound + size);
261 :
262 109 : pfree->size = pfound->size - size;
263 109 : pfree->next_free = pfound->next_free;
264 :
265 109 : pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
266 : }
267 : }
268 :
269 190 : assert((void*)pfound != (void*)pheader);
270 :
271 190 : memset(pfound, 0, size);
272 :
273 190 : return pfound;
274 : }
275 :
276 : /*------------------------------------------------------------------*/
277 77 : static void free_key(DATABASE_HEADER * pheader, void *address, INT size)
278 : {
279 : FREE_DESCRIP *pfree, *pprev, *pnext;
280 :
281 77 : if (size == 0)
282 0 : return;
283 :
284 77 : assert(address != pheader);
285 :
286 : /* quadword alignment for alpha CPU */
287 77 : size = ALIGN8(size);
288 :
289 77 : pfree = (FREE_DESCRIP *) address;
290 77 : pprev = NULL;
291 :
292 : /* clear current block */
293 77 : memset(address, 0, size);
294 :
295 : /* if key comes before first free block, adjust pheader */
296 77 : if ((POINTER_T) address - (POINTER_T) pheader < pheader->first_free_key) {
297 10 : pfree->size = size;
298 10 : pfree->next_free = pheader->first_free_key;
299 10 : pheader->first_free_key = (POINTER_T) address - (POINTER_T) pheader;
300 : } else {
301 : /* find last free block before current block */
302 67 : pprev = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
303 :
304 106 : while (pprev->next_free < (POINTER_T) address - (POINTER_T) pheader) {
305 39 : if (pprev->next_free <= 0) {
306 0 : cm_msg(MERROR, "free_key", "database is corrupted: pprev=%p, pprev->next_free=%d", pprev, pprev->next_free);
307 0 : return;
308 : }
309 39 : pprev = (FREE_DESCRIP *) ((char *) pheader + pprev->next_free);
310 : }
311 :
312 67 : pfree->size = size;
313 67 : pfree->next_free = pprev->next_free;
314 :
315 67 : pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
316 : }
317 :
318 : /* try to melt adjacent free blocks after current block */
319 77 : pnext = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
320 77 : if ((POINTER_T) pnext == (POINTER_T) pfree + pfree->size) {
321 31 : pfree->size += pnext->size;
322 31 : pfree->next_free = pnext->next_free;
323 :
324 31 : memset(pnext, 0, pnext->size);
325 : }
326 :
327 : /* try to melt adjacent free blocks before current block */
328 77 : if (pprev && pprev->next_free == (POINTER_T) pprev - (POINTER_T) pheader + pprev->size) {
329 35 : pprev->size += pfree->size;
330 35 : pprev->next_free = pfree->next_free;
331 :
332 35 : memset(pfree, 0, pfree->size);
333 : }
334 : }
335 :
336 125 : static int validate_free_data(DATABASE_HEADER * pheader, int free_data)
337 : {
338 125 : if (free_data <= 0)
339 0 : return 0;
340 :
341 125 : 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 0 : return 0;
344 : }
345 :
346 125 : 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 0 : return 0;
349 : }
350 :
351 125 : 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 0 : return 0;
354 : }
355 :
356 125 : return 1;
357 : }
358 :
359 : /*------------------------------------------------------------------*/
360 114 : static void *malloc_data(DATABASE_HEADER * pheader, INT size)
361 : {
362 114 : if (size == 0)
363 0 : return NULL;
364 :
365 114 : assert(size > 0);
366 :
367 : /* quadword alignment for alpha CPU */
368 114 : 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 114 : assert(size >= (int)sizeof(FREE_DESCRIP));
372 :
373 114 : if (!validate_free_data(pheader, pheader->first_free_data)) {
374 0 : return NULL;
375 : }
376 :
377 : /* search for free block */
378 114 : FREE_DESCRIP *pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
379 114 : FREE_DESCRIP *pprev = NULL;
380 114 : FREE_DESCRIP *pfound = NULL;
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 117 : if (pfree->size >= size) {
386 : // we will use this block
387 114 : pfound = pfree;
388 114 : break;
389 : }
390 :
391 3 : if (!pfree->next_free) {
392 : // no more free blocks
393 0 : return NULL;
394 : }
395 :
396 3 : 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 0 : return NULL;
400 : }
401 3 : pprev = pfree;
402 3 : 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 114 : assert(pfound != NULL);
408 114 : assert(size <= pfound->size);
409 :
410 : /* if found block is first in list, correct pheader */
411 114 : if (!pprev) {
412 111 : if (size < pfree->size) {
413 : /* free block is only used partially */
414 106 : pheader->first_free_data += size;
415 106 : pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
416 :
417 106 : pfree->size = pfound->size - size;
418 106 : pfree->next_free = pfound->next_free;
419 : } else {
420 : /* free block is used totally */
421 5 : pheader->first_free_data = pfree->next_free;
422 : }
423 : } else {
424 : /* check if free block is used totally */
425 3 : if (pfound->size - size < (int) sizeof(FREE_DESCRIP)) {
426 : /* delete this free block from the free blocks chain */
427 0 : pprev->next_free = pfound->next_free;
428 : } else {
429 : /* decrease free block */
430 3 : pfree = (FREE_DESCRIP *) ((char *) pfound + size);
431 :
432 3 : pfree->size = pfound->size - size;
433 3 : pfree->next_free = pfound->next_free;
434 :
435 3 : pprev->next_free = (POINTER_T) pfree - (POINTER_T) pheader;
436 : }
437 : }
438 :
439 114 : assert((void*)pfound != (void*)pheader);
440 :
441 : /* zero memeory */
442 114 : memset(pfound, 0, size);
443 :
444 114 : return pfound;
445 : }
446 :
447 : /*------------------------------------------------------------------*/
448 45 : static int free_data(DATABASE_HEADER * pheader, void *address, INT size, const char* caller)
449 : {
450 45 : if (size == 0)
451 0 : return DB_SUCCESS;
452 :
453 45 : assert(address != pheader);
454 :
455 : /* quadword alignment for alpha CPU */
456 45 : 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 45 : assert(size >= (int)sizeof(FREE_DESCRIP));
460 :
461 45 : FREE_DESCRIP *pprev = NULL;
462 45 : FREE_DESCRIP *pfree = (FREE_DESCRIP *) address;
463 45 : int pfree_offset = (POINTER_T) address - (POINTER_T) pheader;
464 :
465 : /* clear current block */
466 45 : memset(address, 0, size);
467 :
468 45 : if (pheader->first_free_data == 0) {
469 : /* if free list is empty, create the first free block, adjust pheader */
470 0 : pfree->size = size;
471 0 : pfree->next_free = 0;
472 0 : pheader->first_free_data = pfree_offset;
473 : /* nothing else to do */
474 0 : return DB_SUCCESS;
475 45 : } 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 11 : pfree->size = size;
478 11 : pfree->next_free = pheader->first_free_data;
479 11 : 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 34 : pprev = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
485 :
486 42 : while (pprev->next_free < pfree_offset) {
487 8 : 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 0 : break;
491 : }
492 8 : if (!validate_free_data(pheader, pprev->next_free)) {
493 0 : 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 0 : return DB_CORRUPTED;
495 : }
496 :
497 8 : pprev = (FREE_DESCRIP *) ((char *) pheader + pprev->next_free);
498 : }
499 :
500 34 : pfree->size = size;
501 34 : pfree->next_free = pprev->next_free;
502 :
503 34 : pprev->next_free = pfree_offset;
504 : }
505 :
506 : /* try to melt adjacent free blocks after current block */
507 45 : FREE_DESCRIP *pnext = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
508 45 : if ((POINTER_T) pnext == (POINTER_T) pfree + pfree->size) {
509 : //printf("free_data: merging first and second free block\n");
510 6 : pfree->size += pnext->size;
511 6 : pfree->next_free = pnext->next_free;
512 :
513 6 : memset(pnext, 0, pnext->size);
514 : }
515 :
516 : /* try to melt adjacent free blocks before current block */
517 45 : if (pprev && pprev->next_free == (POINTER_T) pprev - (POINTER_T) pheader + pprev->size) {
518 : //printf("free_data: merging pprev and pfree\n");
519 31 : pprev->size += pfree->size;
520 31 : pprev->next_free = pfree->next_free;
521 :
522 31 : memset(pfree, 0, pfree->size);
523 : }
524 :
525 45 : return DB_SUCCESS;
526 : }
527 :
528 : /*------------------------------------------------------------------*/
529 27 : static void *realloc_data(DATABASE_HEADER * pheader, void *address, INT old_size, INT new_size, const char* caller)
530 : {
531 27 : void *tmp = NULL;
532 :
533 27 : if (old_size) {
534 : int status;
535 2 : tmp = malloc(old_size);
536 2 : if (tmp == NULL) {
537 0 : cm_msg(MERROR, "realloc_data", "cannot malloc(%d), called from %s", old_size, caller);
538 0 : return NULL;
539 : }
540 :
541 2 : memcpy(tmp, address, old_size);
542 :
543 2 : status = free_data(pheader, address, old_size, caller);
544 2 : if (status != DB_SUCCESS) {
545 0 : free(tmp);
546 0 : cm_msg(MERROR, "realloc_data", "cannot free_data(%p, %d), called from %s", address, old_size, caller);
547 0 : return NULL;
548 : }
549 : }
550 :
551 27 : void *pnew = malloc_data(pheader, new_size);
552 :
553 27 : if (!pnew) {
554 0 : if (tmp)
555 0 : free(tmp);
556 0 : cm_msg(MERROR, "realloc_data", "cannot malloc_data(%d), called from %s", new_size, caller);
557 0 : return NULL;
558 : }
559 :
560 27 : if (old_size) {
561 2 : memcpy(pnew, tmp, old_size < new_size ? old_size : new_size);
562 2 : free(tmp);
563 : }
564 :
565 27 : return pnew;
566 : }
567 :
568 : #endif // LOCAL_ROUTINES
569 :
570 : /*------------------------------------------------------------------*/
571 0 : char *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 0 : for (i = 0, j = 0; list[i]; i++)
579 0 : j += strlen(list[i]) + 1;
580 0 : j += 1;
581 :
582 0 : if (str == NULL)
583 0 : str = (char *) malloc(j);
584 : else
585 0 : str = (char *) realloc(str, j);
586 :
587 0 : assert(str != NULL);
588 :
589 0 : str[0] = 0;
590 0 : for (i = 0; list[i]; i++) {
591 0 : strcat(str, list[i]);
592 0 : strcat(str, "\n");
593 : }
594 :
595 0 : return str;
596 : }
597 :
598 : /*------------------------------------------------------------------*/
599 :
600 2 : std::string strcomb1(const char **list)
601 : /* convert list of strings into single string to be used by db_paste() */
602 : {
603 2 : std::string s;
604 :
605 24 : for (int i = 0; list[i]; i++) {
606 22 : s += list[i];
607 22 : s += "\n";
608 : }
609 :
610 2 : return s;
611 0 : }
612 :
613 : /*------------------------------------------------------------------*/
614 :
615 : struct print_key_info_buf
616 : {
617 : int alloc_size;
618 : int used;
619 : char* buf;
620 : };
621 :
622 0 : static void add_to_buf(struct print_key_info_buf* buf, const char* s)
623 : {
624 0 : int len = strlen(s);
625 0 : if (buf->used + len + 10 > buf->alloc_size) {
626 0 : 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 0 : buf->buf = (char*)realloc(buf->buf, new_size);
629 0 : assert(buf->buf != NULL);
630 0 : buf->alloc_size = new_size;
631 : }
632 :
633 0 : memcpy(buf->buf + buf->used, s, len);
634 0 : buf->used += len;
635 0 : buf->buf[buf->used] = 0; // zero-terminate the string
636 0 : }
637 :
638 : #ifdef LOCAL_ROUTINES
639 :
640 0 : static INT print_key_info(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
641 : {
642 0 : struct print_key_info_buf* buf = (struct print_key_info_buf*)info;
643 : int i;
644 :
645 : char str[256];
646 :
647 0 : sprintf(str, "%08X %08X %08X ",
648 0 : (int) (hKey - sizeof(DATABASE_HEADER)),
649 0 : (int) (pkey->data - sizeof(DATABASE_HEADER)), (int) pkey->total_size);
650 :
651 0 : assert(strlen(str)+10 < sizeof(str));
652 :
653 0 : for (i = 0; i < level; i++)
654 0 : strcat(str, " ");
655 :
656 0 : assert(strlen(str)+10 < sizeof(str));
657 :
658 0 : strcat(str, pkey->name);
659 0 : strcat(str, "\n");
660 :
661 0 : assert(strlen(str)+10 < sizeof(str));
662 :
663 : //printf("str [%s]\n", str);
664 :
665 0 : add_to_buf(buf, str);
666 :
667 0 : return SUCCESS;
668 : }
669 :
670 : static bool db_validate_data_offset(const DATABASE_HEADER * pheader, int offset);
671 :
672 0 : INT db_show_mem(HNDLE hDB, char **result, BOOL verbose)
673 : {
674 : INT total_size_key, total_size_data;
675 :
676 : struct print_key_info_buf buf;
677 0 : buf.buf = NULL;
678 0 : buf.used = 0;
679 0 : buf.alloc_size = 0;
680 :
681 0 : db_lock_database(hDB);
682 :
683 0 : DATABASE_HEADER *pheader = _database[hDB - 1].database_header;
684 :
685 : char str[256];
686 :
687 0 : sprintf(str, "Database header size is 0x%04X, all following values are offset by this!\n", (int)sizeof(DATABASE_HEADER));
688 0 : add_to_buf(&buf, str);
689 0 : sprintf(str, "Key area 0x00000000 - 0x%08X, size %d bytes\n", pheader->key_size - 1, pheader->key_size);
690 0 : add_to_buf(&buf, str);
691 0 : 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 0 : add_to_buf(&buf, str);
693 :
694 0 : add_to_buf(&buf, "Keylist:\n");
695 0 : add_to_buf(&buf, "--------\n");
696 0 : total_size_key = 0;
697 :
698 0 : if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
699 0 : add_to_buf(&buf, "ODB is corrupted: pheader->first_free_key is invalid\n");
700 0 : db_unlock_database(hDB);
701 0 : if (result) {
702 0 : *result = buf.buf;
703 : } else {
704 0 : free(buf.buf);
705 : }
706 0 : return DB_CORRUPTED;
707 : }
708 :
709 0 : FREE_DESCRIP *pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
710 :
711 0 : while ((POINTER_T) pfree != (POINTER_T) pheader) {
712 0 : total_size_key += pfree->size;
713 0 : sprintf(str, "Free block at 0x%08X, size 0x%08X, next 0x%08X\n",
714 0 : (int) ((POINTER_T) pfree - (POINTER_T) pheader - sizeof(DATABASE_HEADER)),
715 0 : pfree->size, pfree->next_free ? (int) (pfree->next_free - sizeof(DATABASE_HEADER)) : 0);
716 0 : add_to_buf(&buf, str);
717 0 : if (!db_validate_key_offset(pheader, pfree->next_free)) {
718 0 : add_to_buf(&buf, "ODB is corrupted: next_free is invalid!");
719 0 : break;
720 : }
721 0 : pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
722 : }
723 :
724 0 : sprintf(str, "\nFree Key area: %d bytes out of %d bytes\n", total_size_key, pheader->key_size);
725 0 : add_to_buf(&buf, str);
726 :
727 0 : add_to_buf(&buf, "\nData:\n");
728 0 : add_to_buf(&buf, "-----\n");
729 0 : total_size_data = 0;
730 :
731 0 : if (!db_validate_data_offset(pheader, pheader->first_free_data)) {
732 0 : add_to_buf(&buf, "ODB is corrupted: pheader->first_free_data is invalid\n");
733 0 : db_unlock_database(hDB);
734 0 : if (result) {
735 0 : *result = buf.buf;
736 : } else {
737 0 : free(buf.buf);
738 : }
739 0 : return DB_CORRUPTED;
740 : }
741 :
742 0 : pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
743 :
744 0 : while ((POINTER_T) pfree != (POINTER_T) pheader) {
745 0 : total_size_data += pfree->size;
746 0 : sprintf(str, "Free block at 0x%08X, size 0x%08X, next 0x%08X\n",
747 0 : (int) ((POINTER_T) pfree - (POINTER_T) pheader - sizeof(DATABASE_HEADER)),
748 0 : pfree->size, pfree->next_free ? (int) (pfree->next_free - sizeof(DATABASE_HEADER)) : 0);
749 0 : add_to_buf(&buf, str);
750 0 : if (!db_validate_data_offset(pheader, pfree->next_free)) {
751 0 : add_to_buf(&buf, "ODB is corrupted: next_free is invalid!");
752 0 : break;
753 : }
754 0 : pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
755 : }
756 :
757 0 : sprintf(str, "\nFree Data area: %d bytes out of %d bytes\n", total_size_data, pheader->data_size);
758 0 : add_to_buf(&buf, str);
759 :
760 0 : sprintf(str, "\nFree: %1d (%1.1lf%%) keylist, %1d (%1.1lf%%) data\n",
761 : total_size_key,
762 0 : 100 * (double) total_size_key / pheader->key_size,
763 0 : total_size_data, 100 * (double) total_size_data / pheader->data_size);
764 0 : add_to_buf(&buf, str);
765 :
766 0 : if (verbose) {
767 0 : add_to_buf(&buf, "\n\n");
768 0 : add_to_buf(&buf, "Key Data Size\n");
769 0 : add_to_buf(&buf, "----------------------------\n");
770 0 : db_scan_tree(hDB, pheader->root_key, 0, print_key_info, &buf);
771 : }
772 :
773 0 : sprintf(str, "\nTotal ODB size: %d (0x%08X) Bytes, %lg MiB\n",
774 0 : pheader->key_size + pheader->data_size, pheader->key_size + pheader->data_size,
775 0 : ((pheader->key_size + pheader->data_size) / 1E6));
776 0 : add_to_buf(&buf, str);
777 0 : db_unlock_database(hDB);
778 :
779 0 : if (result) {
780 0 : *result = buf.buf;
781 : } else {
782 0 : free(buf.buf);
783 : }
784 :
785 0 : return DB_SUCCESS;
786 : }
787 :
788 0 : INT db_get_free_mem(HNDLE hDB, INT *key_size, INT *data_size)
789 : {
790 : DATABASE_HEADER *pheader;
791 : FREE_DESCRIP *pfree;
792 :
793 0 : *data_size = 0;
794 0 : *key_size = 0;
795 :
796 0 : db_lock_database(hDB);
797 :
798 0 : pheader = _database[hDB - 1].database_header;
799 :
800 0 : pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
801 :
802 0 : while ((POINTER_T) pfree != (POINTER_T) pheader) {
803 0 : *key_size += pfree->size;
804 0 : pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
805 : }
806 :
807 0 : *data_size = 0;
808 0 : pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
809 :
810 0 : while ((POINTER_T) pfree != (POINTER_T) pheader) {
811 0 : *data_size += pfree->size;
812 0 : pfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
813 : }
814 :
815 0 : db_unlock_database(hDB);
816 0 : 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
824 438 : static bool is_utf8(const char * string)
825 : {
826 438 : if (!string)
827 0 : return false;
828 :
829 438 : return ss_is_valid_utf8(string);
830 : }
831 :
832 : #ifdef LOCAL_ROUTINES
833 :
834 : /*------------------------------------------------------------------*/
835 410 : static 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 410 : if (name == NULL) {
840 1 : db_msg(msg, MERROR, "db_validate_name", "Invalid name passed to %s: should not be NULL", caller_name);
841 1 : return DB_INVALID_NAME;
842 : }
843 :
844 409 : size_t len = strlen(name);
845 :
846 409 : if (len < 1) {
847 4 : db_msg(msg, MERROR, "db_validate_name", "Invalid name passed to %s: should not be an empty string", caller_name);
848 4 : return DB_INVALID_NAME;
849 : }
850 :
851 405 : if (strchr(name, '[')) {
852 1 : db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"[\"", name, caller_name);
853 1 : return DB_INVALID_NAME;
854 : }
855 :
856 404 : if (name[0] == ' ') {
857 0 : db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not start with a space", name, caller_name);
858 0 : return DB_INVALID_NAME;
859 : }
860 :
861 404 : if (name[len-1] == ' ') {
862 0 : db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not end with a space", name, caller_name);
863 0 : return DB_INVALID_NAME;
864 : }
865 :
866 404 : if (strchr(name, ']')) {
867 1 : db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"[\"", name, caller_name);
868 1 : return DB_INVALID_NAME;
869 : }
870 :
871 403 : if (!is_utf8(name)) {
872 0 : db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: invalid unicode UTF-8 encoding", name, caller_name);
873 0 : return DB_INVALID_NAME;
874 : }
875 :
876 403 : if (!maybe_path) {
877 269 : if (strchr(name, '/')) {
878 0 : db_msg(msg, MERROR, "db_validate_name", "Invalid name \"%s\" passed to %s: should not contain \"/\"", name, caller_name);
879 0 : return DB_INVALID_NAME;
880 : }
881 :
882 269 : if (strlen(name) >= NAME_LENGTH) {
883 0 : 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 0 : return DB_INVALID_NAME;
885 : }
886 : }
887 :
888 : //if (strcmp(name, "test")==0)
889 : //return DB_INVALID_NAME;
890 :
891 403 : return DB_SUCCESS;
892 : }
893 :
894 : /*------------------------------------------------------------------*/
895 9607 : static bool db_validate_key_offset(const DATABASE_HEADER * pheader, int offset)
896 : /* check if key offset lies in valid range */
897 : {
898 9607 : if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
899 0 : return false;
900 :
901 9607 : if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size)
902 0 : return false;
903 :
904 9607 : return true;
905 : }
906 :
907 1519 : static bool db_validate_data_offset(const DATABASE_HEADER * pheader, int offset)
908 : /* check if data offset lies in valid range */
909 : {
910 1519 : if (offset != 0 && offset < (int) sizeof(DATABASE_HEADER))
911 0 : return false;
912 :
913 1519 : if (offset > (int) sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size)
914 0 : return false;
915 :
916 1519 : return true;
917 : }
918 :
919 1173 : static bool db_validate_hkey(const DATABASE_HEADER * pheader, HNDLE hKey)
920 : {
921 1173 : if (hKey == 0) {
922 0 : cm_msg(MERROR, "db_validate_hkey", "Error: invalid zero hkey %d", hKey);
923 0 : return false;
924 : }
925 1173 : if (!db_validate_key_offset(pheader, hKey)) {
926 0 : cm_msg(MERROR, "db_validate_hkey", "Error: invalid hkey %d", hKey);
927 0 : return false;
928 : }
929 1173 : return true;
930 : }
931 :
932 5101 : static const KEY* db_get_pkey(const DATABASE_HEADER* pheader, HNDLE hKey, int* pstatus, const char* caller, db_err_msg **msg)
933 : {
934 5101 : BOOL hKey_is_root_key = FALSE;
935 :
936 5101 : if (!hKey) {
937 253 : hKey_is_root_key = TRUE;
938 253 : hKey = pheader->root_key;
939 : }
940 :
941 : /* check if hKey argument is correct */
942 5101 : if (hKey == 0) {
943 0 : if (pstatus)
944 0 : *pstatus = DB_INVALID_HANDLE;
945 0 : return NULL;
946 : }
947 :
948 : /* check if hKey argument is correct */
949 5101 : if (!db_validate_key_offset(pheader, hKey)) {
950 0 : if (pstatus)
951 0 : *pstatus = DB_INVALID_HANDLE;
952 0 : return NULL;
953 : }
954 :
955 5101 : const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
956 :
957 5101 : if (pkey->type < 1 || pkey->type >= TID_LAST) {
958 0 : DWORD tid = pkey->type;
959 0 : if (hKey_is_root_key) {
960 0 : db_msg(msg, MERROR, caller, "db_get_pkey: root_key hkey %d invalid key type %d, database root directory is corrupted", hKey, tid);
961 0 : if (pstatus)
962 0 : *pstatus = DB_CORRUPTED;
963 0 : return NULL;
964 : } else {
965 0 : std::string path = db_get_path_locked(pheader, hKey);
966 0 : db_msg(msg, MERROR, caller, "db_get_pkey: hkey %d path \"%s\" invalid key type %d", hKey, path.c_str(), tid);
967 0 : }
968 0 : if (pstatus)
969 0 : *pstatus = DB_NO_KEY;
970 0 : return NULL;
971 : }
972 :
973 5101 : if (pkey->name[0] == 0) {
974 0 : std::string path = db_get_path_locked(pheader, hKey);
975 0 : db_msg(msg, MERROR, caller, "db_get_pkey: hkey %d path \"%s\" invalid name \"%s\" is empty", hKey, path.c_str(), pkey->name);
976 0 : if (pstatus)
977 0 : *pstatus = DB_NO_KEY;
978 0 : return NULL;
979 0 : }
980 :
981 5101 : return pkey;
982 : }
983 :
984 1285 : 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)
985 : {
986 1285 : if (pkey->type != TID_KEY) {
987 0 : std::string path = db_get_path_locked(pheader, hKey);
988 0 : 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 0 : return NULL;
990 0 : }
991 :
992 1285 : if (!hKey) {
993 0 : hKey = pheader->root_key;
994 : }
995 :
996 1285 : if (!db_validate_data_offset(pheader, pkey->data)) {
997 0 : std::string path = db_get_path_locked(pheader, hKey);
998 0 : db_msg(msg, MERROR, caller, "hkey %d path \"%s\" invalid pkey->data %d", hKey, path.c_str(), pkey->data);
999 0 : return NULL;
1000 0 : }
1001 :
1002 1285 : const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->data);
1003 :
1004 1285 : if (pkeylist->parent != hKey) {
1005 0 : std::string path = db_get_path_locked(pheader, hKey);
1006 0 : 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 0 : return NULL;
1008 0 : }
1009 :
1010 1285 : if (pkeylist->first_key == 0 && pkeylist->num_keys != 0) {
1011 0 : if (!kludge_repair) {
1012 0 : std::string path = db_get_path_locked(pheader, hKey);
1013 0 : 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 0 : return NULL;
1015 0 : }
1016 :
1017 : // FIXME: this repair should be done in db_validate_and_repair_key()
1018 :
1019 0 : std::string path = db_get_path_locked(pheader, hKey);
1020 0 : 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 0 : ((KEYLIST*)pkeylist)->num_keys = 0;
1022 0 : }
1023 :
1024 1285 : return pkeylist;
1025 : }
1026 :
1027 1868 : static HNDLE db_pkey_to_hkey(const DATABASE_HEADER* pheader, const KEY* pkey)
1028 : {
1029 1868 : return (POINTER_T) pkey - (POINTER_T) pheader;
1030 : }
1031 :
1032 150 : static const KEY* db_get_parent(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, const char* caller, db_err_msg **msg)
1033 : {
1034 150 : if (pkey->parent_keylist == 0) {
1035 : // they asked for the parent of "/", return "/"
1036 5 : return db_get_pkey(pheader, pheader->root_key, pstatus, caller, msg);
1037 : }
1038 :
1039 145 : if (!db_validate_data_offset(pheader, pkey->parent_keylist)) {
1040 0 : 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 0 : if (pstatus)
1042 0 : *pstatus = DB_CORRUPTED;
1043 0 : return NULL;
1044 : }
1045 :
1046 145 : const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
1047 :
1048 145 : if (pkeylist->first_key == 0 && pkeylist->num_keys != 0) {
1049 0 : 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 0 : if (pstatus)
1051 0 : *pstatus = DB_CORRUPTED;
1052 0 : return NULL;
1053 : }
1054 :
1055 145 : return db_get_pkey(pheader, pkeylist->parent, pstatus, caller, msg);
1056 : }
1057 :
1058 252 : static const KEY* db_enum_first_locked(const DATABASE_HEADER *pheader, const KEY* pkey, db_err_msg **msg)
1059 : {
1060 252 : HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
1061 :
1062 252 : if (pkey->type != TID_KEY) {
1063 0 : std::string path = db_get_path_locked(pheader, hKey);
1064 0 : 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 0 : return NULL;
1066 0 : }
1067 :
1068 252 : const KEYLIST *pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
1069 :
1070 252 : if (!pkeylist) {
1071 : // error
1072 0 : return NULL;
1073 : }
1074 :
1075 252 : if (pkeylist->num_keys == 0) {
1076 : // empty directory
1077 39 : return NULL;
1078 : }
1079 :
1080 213 : if (pkeylist->first_key == 0) {
1081 : // empty directory
1082 0 : return NULL;
1083 : }
1084 :
1085 213 : return db_get_pkey(pheader, pkeylist->first_key, NULL, "db_enum_first_locked", msg);
1086 : }
1087 :
1088 786 : static const KEY* db_enum_next_locked(const DATABASE_HEADER *pheader, const KEY* pdir, const KEY* pkey, db_err_msg **msg)
1089 : {
1090 786 : if (pkey->next_key == 0)
1091 164 : return NULL;
1092 :
1093 622 : return db_get_pkey(pheader, pkey->next_key, NULL, "db_enum_next_locked", msg);
1094 : }
1095 :
1096 0 : static const KEY* db_resolve_link_locked(const DATABASE_HEADER* pheader, const KEY* pkey, int* pstatus, db_err_msg** msg)
1097 : {
1098 0 : if (pkey->type != TID_LINK)
1099 0 : return pkey;
1100 :
1101 : // FIXME: need to validate pkey->data
1102 :
1103 0 : if (*((char *) pheader + pkey->data) == '/') {
1104 0 : return db_find_pkey_locked(pheader, NULL, (char*)pheader + pkey->data, pstatus, msg);
1105 : } else {
1106 0 : return db_find_pkey_locked(pheader, pkey, (char*)pheader + pkey->data, pstatus, msg);
1107 : }
1108 : }
1109 :
1110 : /*
1111 : static 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 :
1135 : static 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 :
1147 70 : 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)
1148 : {
1149 : int status;
1150 70 : 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 70 : if (hkey==0 || !db_validate_key_offset(pheader, hkey)) {
1161 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid hkey", hkey, path);
1162 0 : return false;
1163 : }
1164 :
1165 : /* check key type */
1166 70 : if (pkey->type <= 0 || pkey->type >= TID_LAST) {
1167 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", name \"%s\", invalid key type %d", hkey, path, pkey->name, pkey->type);
1168 0 : return false;
1169 : }
1170 :
1171 : /* check key name */
1172 70 : status = db_validate_name(pkey->name, FALSE, "db_validate_key", msg);
1173 70 : if (status != DB_SUCCESS) {
1174 : char newname[NAME_LENGTH];
1175 0 : sprintf(newname, "%p", pkey);
1176 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\": invalid name \"%s\" replaced with \"%s\"", hkey, path, pkey->name, newname);
1177 0 : mstrlcpy(pkey->name, newname, sizeof(pkey->name));
1178 0 : flag = false;
1179 : //return false;
1180 : }
1181 :
1182 : /* check parent */
1183 70 : if (pkey->parent_keylist != parenthkeylist) {
1184 0 : 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 0 : return false;
1186 : }
1187 :
1188 70 : if (!db_validate_data_offset(pheader, pkey->data)) {
1189 0 : 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 0 : return false;
1191 : }
1192 :
1193 : /* check key sizes */
1194 70 : if ((pkey->total_size < 0) || (pkey->total_size > pheader->data_size)) {
1195 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->total_size %d", hkey, path, pkey->total_size);
1196 0 : return false;
1197 : }
1198 :
1199 70 : if ((pkey->item_size < 0) || (pkey->item_size > pheader->data_size)) {
1200 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->item_size: %d", hkey, path, pkey->item_size);
1201 0 : return false;
1202 : }
1203 :
1204 70 : if ((pkey->num_values < 0) || (pkey->num_values > pheader->data_size)) {
1205 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->num_values: %d", hkey, path, pkey->num_values);
1206 0 : return false;
1207 : }
1208 :
1209 : /* check and correct key size */
1210 70 : if (pkey->total_size != pkey->item_size * pkey->num_values) {
1211 0 : 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 0 : pkey->total_size = pkey->item_size * pkey->num_values;
1213 0 : flag = false;
1214 : }
1215 :
1216 : /* check and correct key size */
1217 70 : if (pkey->data == 0 && pkey->total_size != 0) {
1218 0 : 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 0 : pkey->num_values = 0;
1220 0 : pkey->total_size = 0;
1221 0 : flag = false;
1222 : }
1223 :
1224 70 : if (pkey->type == TID_STRING || pkey->type == TID_LINK) {
1225 7 : const char* s = (char*)pheader + pkey->data;
1226 7 : if (!is_utf8(s)) {
1227 0 : 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 70 : 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 0 : if (pkey->total_size <= 2) {
1239 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK is an empty link", hkey, path);
1240 0 : flag = false;
1241 : //return false;
1242 : }
1243 : }
1244 :
1245 : /* check for too long link */
1246 70 : if (pkey->type == TID_LINK) {
1247 0 : if (pkey->total_size >= MAX_ODB_PATH) {
1248 0 : 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 0 : flag = false;
1250 : //return false;
1251 : }
1252 : }
1253 :
1254 : /* check for link loop */
1255 70 : if (pkey->type == TID_LINK) {
1256 0 : const char* link = (char*)pheader + pkey->data;
1257 0 : int link_len = strlen(link);
1258 0 : int path_len = strlen(path);
1259 0 : if (link_len == path_len) {
1260 : // check for link to itself
1261 0 : if (equal_ustring(link, path)) {
1262 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK to \"%s\" is a link to itself", hkey, path, link);
1263 0 : flag = false;
1264 : //return false;
1265 : }
1266 0 : } else if (link_len < path_len) {
1267 : // check for link to the "path" subdirectory
1268 : char tmp[MAX_ODB_PATH];
1269 0 : memcpy(tmp, path, link_len);
1270 0 : tmp[link_len] = 0;
1271 0 : if (equal_ustring(link, tmp) && path[link_len] == DIR_SEPARATOR) {
1272 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_LINK to \"%s\" is a loop", hkey, path, link);
1273 0 : flag = false;
1274 : //return false;
1275 : }
1276 : }
1277 : }
1278 :
1279 : /* check access mode */
1280 70 : if ((pkey->access_mode & ~(MODE_READ | MODE_WRITE | MODE_DELETE | MODE_EXCLUSIVE | MODE_ALLOC))) {
1281 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->access_mode %d", hkey, path, pkey->access_mode);
1282 0 : flag = false;
1283 : //return false;
1284 : }
1285 :
1286 : /* check if access time is in the future */
1287 70 : if (pkey->last_written > 0 && ((DWORD)pkey->last_written > ss_time() + 3600)) {
1288 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->last_written time %d", hkey, path, pkey->last_written);
1289 0 : flag = false;
1290 : //return false;
1291 : }
1292 :
1293 70 : if (pkey->type == TID_KEY) {
1294 24 : bool pkeylist_ok = true;
1295 : // FIXME: notice the kludged repair of pkeylist! K.O.
1296 24 : const KEYLIST *pkeylist = db_get_pkeylist(pheader, hkey, pkey, "db_validate_key", msg, true);
1297 :
1298 24 : if (!pkeylist) {
1299 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", invalid pkey->data %d", hkey, path, pkey->data);
1300 0 : flag = false;
1301 : } else {
1302 24 : if (pkeylist->parent != hkey) {
1303 0 : 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 0 : flag = false;
1305 0 : pkeylist_ok = false;
1306 : }
1307 :
1308 24 : if (pkeylist->num_keys < 0 || pkeylist->num_keys > pheader->key_size) {
1309 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->num_keys %d", hkey, path, pkeylist->num_keys);
1310 0 : flag = false;
1311 0 : pkeylist_ok = false;
1312 : }
1313 :
1314 24 : if (pkeylist->num_keys == 0 && pkeylist->first_key == 0) {
1315 : // empty key
1316 20 : } else if (pkeylist->first_key == 0 || !db_validate_key_offset(pheader, pkeylist->first_key)) {
1317 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid pkeylist->first_key %d", hkey, path, pkeylist->first_key);
1318 0 : flag = false;
1319 0 : pkeylist_ok = false;
1320 : }
1321 :
1322 24 : 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 24 : HNDLE subhkey = pkeylist->first_key;
1326 :
1327 24 : int count = 0;
1328 93 : while (subhkey != 0) {
1329 69 : KEY* subpkey = (KEY*)db_get_pkey(pheader, subhkey, NULL, "db_validate_key", msg);
1330 69 : if (!subpkey) {
1331 0 : db_msg(msg, MERROR, "db_validate_key", "hkey %d, path \"%s\", TID_KEY invalid subhkey %d", hkey, path, subhkey);
1332 0 : pkeylist_ok = false;
1333 0 : flag = false;
1334 0 : break;
1335 : }
1336 :
1337 69 : std::string buf;
1338 69 : buf += path;
1339 69 : buf += "/";
1340 69 : 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 69 : if (recurse) {
1345 64 : flag &= db_validate_and_repair_key_wlocked(pheader, recurse + 1, buf.c_str(), pkey->data, subhkey, subpkey, msg);
1346 : }
1347 :
1348 69 : count++;
1349 69 : subhkey = subpkey->next_key;
1350 69 : }
1351 :
1352 24 : if (count != pkeylist->num_keys) {
1353 0 : 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 0 : ((KEYLIST*)pkeylist)->num_keys = count;
1355 0 : flag = false;
1356 0 : pkeylist_ok = false;
1357 : }
1358 : }
1359 : }
1360 : }
1361 :
1362 70 : return flag;
1363 : }
1364 :
1365 : /*------------------------------------------------------------------*/
1366 :
1367 13 : DATABASE_CLIENT* db_get_my_client_locked(DATABASE* pdb)
1368 : {
1369 13 : assert(pdb);
1370 13 : assert(pdb->database_header);
1371 :
1372 13 : DATABASE_HEADER *pheader = pdb->database_header;
1373 :
1374 13 : int idx = pdb->client_index;
1375 :
1376 13 : if (idx < 0 || idx >= pheader->max_client_index || idx >= MAX_CLIENTS) {
1377 0 : 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 0 : ss_semaphore_release(pdb->semaphore);
1379 0 : abort();
1380 : }
1381 :
1382 13 : DATABASE_CLIENT* pclient = &pheader->client[idx]; // safe to dereference on "idx"
1383 :
1384 13 : if (pclient->name[0] == 0) {
1385 0 : 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 0 : ss_semaphore_release(pdb->semaphore);
1387 0 : abort();
1388 : }
1389 :
1390 13 : int pid = getpid();
1391 :
1392 13 : if (pclient->pid != pid) {
1393 0 : 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 0 : ss_semaphore_release(pdb->semaphore);
1395 0 : 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 13 : return pclient;
1410 : }
1411 :
1412 : /*------------------------------------------------------------------*/
1413 3 : static void db_validate_sizes()
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);
1431 : S(midas_thread_t);
1432 : // data buffers
1433 : S(EVENT_REQUEST);
1434 : S(BUFFER_CLIENT);
1435 : S(BUFFER_HEADER);
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);
1445 : S(DATABASE_CLIENT);
1446 : S(DATABASE_HEADER);
1447 : // misc structures
1448 : S(EVENT_HEADER);
1449 : S(RUNINFO);
1450 : S(EQUIPMENT_INFO);
1451 : S(EQUIPMENT_STATS);
1452 : S(BANK_HEADER);
1453 : S(BANK);
1454 : S(BANK32);
1455 : S(ANA_OUTPUT_INFO);
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
1465 : EQUIPMENT_INFO eq;
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 3 : }
1510 :
1511 : typedef struct {
1512 : DATABASE_HEADER * pheader;
1513 : int max_keys;
1514 : int num_keys;
1515 : HNDLE* hkeys;
1516 : int* counts;
1517 : int* modes;
1518 : int num_modified;
1519 : } UPDATE_OPEN_RECORDS;
1520 :
1521 67 : static int db_update_open_record_wlocked(const DATABASE_HEADER* xpheader, const KEY* xpkey, int level, void* voidp, db_err_msg **msg)
1522 : {
1523 67 : int found = 0;
1524 67 : int count = 0;
1525 : int status;
1526 : int k;
1527 67 : UPDATE_OPEN_RECORDS *uorp = (UPDATE_OPEN_RECORDS *)voidp;
1528 :
1529 67 : KEY* pkey = (KEY*)xpkey; // drop "const": we already have "allow_write"
1530 :
1531 67 : HNDLE hKey = db_pkey_to_hkey(uorp->pheader, pkey);
1532 :
1533 67 : for (k=0; k<uorp->num_keys; k++)
1534 0 : if (uorp->hkeys[k] == hKey) {
1535 0 : found = 1;
1536 0 : count = uorp->counts[k];
1537 0 : break;
1538 : }
1539 :
1540 67 : if (pkey->notify_count == 0 && !found)
1541 67 : return DB_SUCCESS; // no open record here
1542 :
1543 0 : std::string path = db_get_path_locked(uorp->pheader, hKey);
1544 0 : if (path == "") {
1545 0 : db_msg(msg, MINFO, "db_update_open_record", "Invalid hKey %d", hKey);
1546 0 : 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 0 : if (pkey->notify_count==0 && found) {
1566 0 : db_msg(msg, MINFO, "db_update_open_record", "Added missing open record flag to \"%s\"", path.c_str());
1567 0 : pkey->notify_count = count;
1568 0 : uorp->num_modified++;
1569 0 : return DB_SUCCESS;
1570 : }
1571 :
1572 0 : if (pkey->notify_count!=0 && !found) {
1573 0 : db_msg(msg, MINFO, "db_update_open_record", "Removed open record flag from \"%s\"", path.c_str());
1574 0 : pkey->notify_count = 0;
1575 0 : uorp->num_modified++;
1576 :
1577 : if (pkey->access_mode | MODE_EXCLUSIVE) {
1578 0 : status = db_set_mode_wlocked(uorp->pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 1, msg);
1579 0 : if (status != DB_SUCCESS) {
1580 0 : 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 0 : return DB_SUCCESS;
1582 : }
1583 0 : db_msg(msg, MINFO, "db_update_open_record", "Removed exclusive access mode from \"%s\"", path.c_str());
1584 : }
1585 0 : return DB_SUCCESS;
1586 : }
1587 :
1588 0 : if (pkey->notify_count != uorp->counts[k]) {
1589 0 : 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 0 : pkey->notify_count = count;
1591 0 : uorp->num_modified++;
1592 0 : return DB_SUCCESS;
1593 : }
1594 :
1595 0 : return DB_SUCCESS;
1596 0 : }
1597 :
1598 3 : static int db_validate_open_records_wlocked(DATABASE_HEADER* pheader, db_err_msg** msg)
1599 : {
1600 3 : int status = DB_SUCCESS;
1601 : UPDATE_OPEN_RECORDS uor;
1602 : int i, j, k;
1603 :
1604 3 : uor.max_keys = MAX_CLIENTS*MAX_OPEN_RECORDS;
1605 3 : uor.num_keys = 0;
1606 3 : uor.hkeys = (HNDLE*)calloc(uor.max_keys, sizeof(HNDLE));
1607 3 : uor.counts = (int*)calloc(uor.max_keys, sizeof(int));
1608 3 : uor.modes = (int*)calloc(uor.max_keys, sizeof(int));
1609 3 : uor.num_modified = 0;
1610 :
1611 3 : assert(uor.hkeys != NULL);
1612 3 : assert(uor.counts != NULL);
1613 3 : assert(uor.modes != NULL);
1614 :
1615 3 : uor.pheader = pheader;
1616 :
1617 6 : for (i = 0; i < pheader->max_client_index; i++) {
1618 3 : DATABASE_CLIENT* pclient = &pheader->client[i];
1619 3 : for (j = 0; j < pclient->max_index; j++)
1620 0 : if (pclient->open_record[j].handle) {
1621 0 : int found = 0;
1622 0 : for (k=0; k<uor.num_keys; k++) {
1623 0 : if (uor.hkeys[k] == pclient->open_record[j].handle) {
1624 0 : uor.counts[k]++;
1625 0 : found = 1;
1626 0 : break;
1627 : }
1628 : }
1629 0 : if (!found) {
1630 0 : uor.hkeys[uor.num_keys] = pclient->open_record[j].handle;
1631 0 : uor.counts[uor.num_keys] = 1;
1632 0 : uor.modes[uor.num_keys] = pclient->open_record[j].access_mode;
1633 0 : 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 3 : const KEY* proot = db_get_pkey(pheader, 0, &status, "db_validate_open_record", msg);
1644 :
1645 3 : if (proot) {
1646 3 : db_scan_tree_locked(pheader, proot, 0, db_update_open_record_wlocked, &uor, msg);
1647 : }
1648 :
1649 3 : if (uor.num_modified) {
1650 0 : db_msg(msg, MINFO, "db_validate_open_records", "Corrected %d ODB entries", uor.num_modified);
1651 : }
1652 :
1653 3 : free(uor.hkeys);
1654 3 : free(uor.counts);
1655 3 : free(uor.modes);
1656 :
1657 3 : return status;
1658 : }
1659 :
1660 : /*------------------------------------------------------------------*/
1661 3 : static bool db_validate_and_repair_db_wlocked(DATABASE_HEADER * pheader, db_err_msg **msg)
1662 : {
1663 3 : int total_size_key = 0;
1664 3 : int total_size_data = 0;
1665 : double ratio;
1666 : FREE_DESCRIP *pfree;
1667 3 : bool flag = true;
1668 :
1669 : /* validate size of data structures (miscompiled, 32/64 bit mismatch, etc */
1670 :
1671 3 : db_validate_sizes();
1672 :
1673 : /* validate the key free list */
1674 :
1675 3 : if (!db_validate_key_offset(pheader, pheader->first_free_key)) {
1676 0 : 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 0 : return false;
1678 : }
1679 :
1680 3 : pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
1681 :
1682 8 : while ((POINTER_T) pfree != (POINTER_T) pheader) {
1683 :
1684 5 : if (pfree->next_free != 0 && !db_validate_key_offset(pheader, pfree->next_free)) {
1685 0 : 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 0 : flag = false;
1687 0 : break;
1688 : }
1689 :
1690 5 : total_size_key += pfree->size;
1691 5 : FREE_DESCRIP *nextpfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
1692 :
1693 5 : if (pfree->next_free != 0 && nextpfree == pfree) {
1694 0 : 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 0 : pfree->next_free = 0;
1696 0 : flag = false;
1697 0 : break;
1698 : //return false;
1699 : }
1700 :
1701 5 : pfree = nextpfree;
1702 : }
1703 :
1704 3 : ratio = ((double) (pheader->key_size - total_size_key)) / ((double) pheader->key_size);
1705 3 : if (ratio > 0.9)
1706 0 : db_msg(msg, MERROR, "db_validate_db", "Warning: database key area is %.0f%% full", ratio * 100.0);
1707 :
1708 3 : if (total_size_key > pheader->key_size) {
1709 0 : 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 0 : flag = false;
1711 : }
1712 :
1713 : /* validate the data free list */
1714 :
1715 3 : if (!db_validate_data_offset(pheader, pheader->first_free_data)) {
1716 0 : 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 0 : 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 3 : pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
1723 :
1724 8 : while ((POINTER_T) pfree != (POINTER_T) pheader) {
1725 :
1726 5 : if (pfree->next_free != 0 && !db_validate_data_offset(pheader, pfree->next_free)) {
1727 0 : 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 0 : flag = false;
1729 0 : break;
1730 : //return false;
1731 : }
1732 :
1733 5 : total_size_data += pfree->size;
1734 5 : FREE_DESCRIP *nextpfree = (FREE_DESCRIP *) ((char *) pheader + pfree->next_free);
1735 :
1736 5 : if (pfree->next_free != 0 && nextpfree == pfree) {
1737 0 : 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 0 : pfree->next_free = 0;
1739 0 : flag = false;
1740 0 : break;
1741 : //return false;
1742 : }
1743 :
1744 5 : pfree = nextpfree;
1745 : }
1746 :
1747 3 : ratio = ((double) (pheader->data_size - total_size_data)) / ((double) pheader->data_size);
1748 3 : if (ratio > 0.9)
1749 0 : db_msg(msg, MERROR, "db_validate_db", "Warning: database data area is %.0f%% full", ratio * 100.0);
1750 :
1751 3 : if (total_size_data > pheader->data_size) {
1752 0 : 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 0 : flag = false;
1754 : //return false;
1755 : }
1756 :
1757 : /* validate the tree of keys, starting from the root key */
1758 :
1759 3 : if (!db_validate_key_offset(pheader, pheader->root_key)) {
1760 0 : 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 0 : return false;
1762 : }
1763 :
1764 3 : flag &= db_validate_and_repair_key_wlocked(pheader, 1, "", 0, pheader->root_key, (KEY *) ((char *) pheader + pheader->root_key), msg);
1765 :
1766 3 : if (!flag) {
1767 0 : db_msg(msg, MERROR, "db_validate_db", "Error: ODB corruption detected, see previous messages");
1768 : }
1769 :
1770 3 : return flag;
1771 : }
1772 :
1773 : #endif // LOCAL_ROUTINES
1774 :
1775 : /**dox***************************************************************/
1776 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
1777 :
1778 : /********************************************************************/
1779 : /**
1780 : Open an online database
1781 : @param database_name Database name.
1782 : @param database_size Initial size of database if not existing
1783 : @param client_name Name of this application
1784 : @param hDB ODB handle obtained via cm_get_experiment_database().
1785 : @return DB_SUCCESS, DB_CREATED, DB_INVALID_NAME, DB_NO_MEMORY,
1786 : DB_MEMSIZE_MISMATCH, DB_NO_SEMAPHORE, DB_INVALID_PARAM,
1787 : RPC_NET_ERROR
1788 : */
1789 3 : INT db_open_database(const char *xdatabase_name, INT database_size, HNDLE * hDB, const char *client_name)
1790 : {
1791 3 : if (rpc_is_remote())
1792 0 : return rpc_call(RPC_DB_OPEN_DATABASE, xdatabase_name, database_size, hDB, client_name);
1793 :
1794 : #ifdef LOCAL_ROUTINES
1795 : {
1796 : INT i, status;
1797 : HNDLE handle;
1798 : DATABASE_CLIENT *pclient;
1799 : BOOL shm_created;
1800 : DATABASE_HEADER *pheader;
1801 : KEY *pkey;
1802 : KEYLIST *pkeylist;
1803 : FREE_DESCRIP *pfree;
1804 : BOOL call_watchdog;
1805 : DWORD timeout;
1806 : char database_name[NAME_LENGTH];
1807 :
1808 : /* restrict name length */
1809 3 : mstrlcpy(database_name, xdatabase_name, NAME_LENGTH);
1810 :
1811 3 : int odb_size_limit = 100*1000*1000;
1812 3 : if (database_size < 0 || database_size > odb_size_limit) {
1813 0 : cm_msg(MERROR, "db_open_database", "invalid database size: %d bytes. ODB size limit is %d bytes", database_size, odb_size_limit);
1814 3 : return DB_INVALID_PARAM;
1815 : }
1816 :
1817 3 : if (strlen(client_name) >= NAME_LENGTH) {
1818 0 : cm_msg(MERROR, "db_open_database", "client name \'%s\' is longer than %d characters", client_name, NAME_LENGTH-1);
1819 0 : return DB_INVALID_PARAM;
1820 : }
1821 :
1822 3 : if (strchr(client_name, '/') != NULL) {
1823 0 : cm_msg(MERROR, "db_open_database", "client name \'%s\' should not contain the slash \'/\' character", client_name);
1824 0 : return DB_INVALID_PARAM;
1825 : }
1826 :
1827 : /* allocate new space for the new database descriptor */
1828 3 : if (_database_entries == 0) {
1829 3 : _database = (DATABASE *) malloc(sizeof(DATABASE));
1830 3 : assert(_database != NULL);
1831 3 : memset(_database, 0, sizeof(DATABASE));
1832 3 : if (_database == NULL) {
1833 0 : *hDB = 0;
1834 0 : return DB_NO_MEMORY;
1835 : }
1836 :
1837 3 : _database_entries = 1;
1838 3 : i = 0;
1839 : } else {
1840 : /* check if database already open */
1841 0 : for (i = 0; i < _database_entries; i++)
1842 0 : if (_database[i].attached && equal_ustring(_database[i].name, database_name)) {
1843 : /* check if database belongs to this thread */
1844 0 : *hDB = i + 1;
1845 0 : return DB_SUCCESS;
1846 : }
1847 :
1848 : /* check for a deleted entry */
1849 0 : for (i = 0; i < _database_entries; i++)
1850 0 : if (!_database[i].attached)
1851 0 : break;
1852 :
1853 : /* if not found, create new one */
1854 0 : if (i == _database_entries) {
1855 0 : _database = (DATABASE *) realloc(_database, sizeof(DATABASE) * (_database_entries + 1));
1856 0 : assert(_database != NULL);
1857 :
1858 0 : memset(&_database[_database_entries], 0, sizeof(DATABASE));
1859 :
1860 0 : _database_entries++;
1861 0 : if (_database == NULL) {
1862 0 : _database_entries--;
1863 0 : *hDB = 0;
1864 0 : return DB_NO_MEMORY;
1865 : }
1866 : }
1867 : }
1868 :
1869 3 : handle = (HNDLE) i;
1870 :
1871 : /* open shared memory region */
1872 3 : void* shm_adr = NULL;
1873 3 : size_t shm_size = 0;
1874 : HNDLE shm_handle;
1875 :
1876 3 : status = ss_shm_open(database_name, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2), &shm_adr, &shm_size, &shm_handle, TRUE);
1877 :
1878 3 : if (status == SS_NO_MEMORY || status == SS_FILE_ERROR) {
1879 0 : *hDB = 0;
1880 0 : return DB_INVALID_NAME;
1881 : }
1882 :
1883 3 : _database[handle].shm_adr = shm_adr;
1884 3 : _database[handle].shm_size = shm_size;
1885 3 : _database[handle].shm_handle = shm_handle;
1886 :
1887 3 : _database[handle].database_header = (DATABASE_HEADER *) shm_adr;
1888 :
1889 : /* shortcut to header */
1890 3 : pheader = _database[handle].database_header;
1891 :
1892 : /* save name */
1893 3 : strcpy(_database[handle].name, database_name);
1894 :
1895 3 : shm_created = (status == SS_CREATED);
1896 :
1897 : /* clear memeory for debugging */
1898 : /* memset(pheader, 0, sizeof(DATABASE_HEADER) + 2*ALIGN8(database_size/2)); */
1899 :
1900 3 : if (shm_created && pheader->name[0] == 0) {
1901 : /* setup header info if database was created */
1902 1 : memset(pheader, 0, sizeof(DATABASE_HEADER) + 2 * ALIGN8(database_size / 2));
1903 :
1904 1 : strcpy(pheader->name, database_name);
1905 1 : pheader->version = DATABASE_VERSION;
1906 1 : pheader->key_size = ALIGN8(database_size / 2);
1907 1 : pheader->data_size = ALIGN8(database_size / 2);
1908 1 : pheader->root_key = sizeof(DATABASE_HEADER);
1909 1 : pheader->first_free_key = sizeof(DATABASE_HEADER);
1910 1 : pheader->first_free_data = sizeof(DATABASE_HEADER) + pheader->key_size;
1911 :
1912 : /* set up free list */
1913 1 : pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_key);
1914 1 : pfree->size = pheader->key_size;
1915 1 : pfree->next_free = 0;
1916 :
1917 1 : pfree = (FREE_DESCRIP *) ((char *) pheader + pheader->first_free_data);
1918 1 : pfree->size = pheader->data_size;
1919 1 : pfree->next_free = 0;
1920 :
1921 : /* create root key */
1922 1 : pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_open_database_A");
1923 1 : assert(pkey);
1924 :
1925 : /* set key properties */
1926 1 : pkey->type = TID_KEY;
1927 1 : pkey->num_values = 1;
1928 1 : pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
1929 1 : strcpy(pkey->name, "root");
1930 1 : pkey->parent_keylist = 0;
1931 :
1932 : /* create keylist */
1933 1 : pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST), "db_open_database_B");
1934 1 : assert(pkeylist);
1935 :
1936 : /* store keylist in data field */
1937 1 : pkey->data = (POINTER_T) pkeylist - (POINTER_T) pheader;
1938 1 : pkey->item_size = sizeof(KEYLIST);
1939 1 : pkey->total_size = sizeof(KEYLIST);
1940 :
1941 1 : pkeylist->parent = (POINTER_T) pkey - (POINTER_T) pheader;
1942 1 : pkeylist->num_keys = 0;
1943 1 : pkeylist->first_key = 0;
1944 : }
1945 :
1946 : /* check database version */
1947 3 : if (pheader->version != DATABASE_VERSION) {
1948 0 : cm_msg(MERROR, "db_open_database",
1949 : "Different database format: Shared memory is %d, program is %d", pheader->version, DATABASE_VERSION);
1950 0 : return DB_VERSION_MISMATCH;
1951 : }
1952 :
1953 : /* check database size vs shared memory size */
1954 3 : if (_database[handle].shm_size < (int)sizeof(DATABASE_HEADER) + pheader->key_size + pheader->data_size) {
1955 0 : 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 0 : return DB_VERSION_MISMATCH;
1957 : }
1958 :
1959 : /* check root key */
1960 3 : if (!db_validate_key_offset(pheader, pheader->root_key)) {
1961 0 : cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key offset %d is invalid", pheader->root_key);
1962 0 : return DB_VERSION_MISMATCH;
1963 : } else {
1964 3 : pkey = (KEY*)((char*)pheader + pheader->root_key);
1965 :
1966 3 : if (pkey->type != TID_KEY) {
1967 0 : cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key type %d is not TID_KEY", pkey->type);
1968 0 : return DB_VERSION_MISMATCH;
1969 : }
1970 :
1971 3 : if (strcmp(pkey->name, "root") != 0) {
1972 0 : cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key name \"%s\" is not \"root\"", pkey->name);
1973 0 : 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 3 : db_err_msg* msg = NULL;
1983 3 : bool ok = db_validate_and_repair_key_wlocked(pheader, 0, "", 0, pheader->root_key, pkey, &msg);
1984 3 : if (msg)
1985 0 : db_flush_msg(&msg);
1986 3 : if (!ok) {
1987 0 : cm_msg(MERROR, "db_open_database", "Invalid, incompatible or corrupted database: root key is invalid");
1988 0 : return DB_VERSION_MISMATCH;
1989 : }
1990 : }
1991 :
1992 : /* set default mutex and semaphore timeout */
1993 3 : _database[handle].timeout = 10000;
1994 :
1995 : /* create mutexes for the database */
1996 3 : status = ss_mutex_create(&_database[handle].mutex, TRUE);
1997 3 : if (status != SS_SUCCESS && status != SS_CREATED) {
1998 0 : *hDB = 0;
1999 0 : return DB_NO_SEMAPHORE;
2000 : }
2001 :
2002 : /* create semaphore for the database */
2003 3 : status = ss_semaphore_create(database_name, &(_database[handle].semaphore));
2004 3 : if (status != SS_SUCCESS && status != SS_CREATED) {
2005 0 : *hDB = 0;
2006 0 : return DB_NO_SEMAPHORE;
2007 : }
2008 3 : _database[handle].lock_cnt = 0;
2009 :
2010 3 : _database[handle].protect = FALSE;
2011 3 : _database[handle].protect_read = FALSE;
2012 3 : _database[handle].protect_write = FALSE;
2013 :
2014 : /* first lock database */
2015 3 : status = db_lock_database(handle + 1);
2016 3 : if (status != DB_SUCCESS)
2017 0 : 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 3 : int num_clients = 0;
2031 3 : int max_client_index = 0;
2032 195 : for (i = 0; i < MAX_CLIENTS; i++) {
2033 192 : if (pheader->client[i].pid == 0)
2034 192 : continue;
2035 0 : num_clients++;
2036 0 : max_client_index = i + 1;
2037 : }
2038 3 : pheader->num_clients = num_clients;
2039 3 : 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 195 : for (i = 0; i < MAX_CLIENTS; i++) {
2045 192 : if (pheader->client[i].pid == 0)
2046 192 : continue;
2047 0 : if (!ss_pid_exists(pheader->client[i].pid)) {
2048 : char client_name_tmp[NAME_LENGTH];
2049 : int client_pid;
2050 :
2051 0 : mstrlcpy(client_name_tmp, pheader->client[i].name, sizeof(client_name_tmp));
2052 0 : 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 0 : memset(&(pheader->client[i]), 0, sizeof(DATABASE_CLIENT));
2059 :
2060 0 : 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 3 : for (i = 0; i < MAX_CLIENTS; i++)
2068 3 : if (pheader->client[i].pid == 0)
2069 3 : break;
2070 :
2071 3 : if (i == MAX_CLIENTS) {
2072 0 : db_unlock_database(handle + 1);
2073 0 : *hDB = 0;
2074 0 : cm_msg(MERROR, "db_open_database", "maximum number of clients exceeded");
2075 0 : return DB_NO_SLOT;
2076 : }
2077 :
2078 : /* store slot index in _database structure */
2079 3 : _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 3 : pheader->num_clients++;
2086 3 : if (i + 1 > pheader->max_client_index)
2087 3 : pheader->max_client_index = i + 1;
2088 :
2089 : /* setup database header and client structure */
2090 3 : pclient = &pheader->client[i];
2091 :
2092 3 : memset(pclient, 0, sizeof(DATABASE_CLIENT));
2093 3 : mstrlcpy(pclient->name, client_name, sizeof(pclient->name));
2094 3 : pclient->pid = ss_getpid();
2095 3 : pclient->num_open_records = 0;
2096 :
2097 3 : ss_suspend_get_odb_port(&pclient->port);
2098 :
2099 3 : pclient->last_activity = ss_millitime();
2100 :
2101 3 : cm_get_watchdog_params(&call_watchdog, &timeout);
2102 3 : pclient->watchdog_timeout = timeout;
2103 :
2104 : /* check ODB for corruption */
2105 3 : db_err_msg* msg = NULL;
2106 3 : bool ok = db_validate_and_repair_db_wlocked(pheader, &msg);
2107 3 : if (msg)
2108 0 : db_flush_msg(&msg);
2109 3 : 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 3 : _database[handle].database_data = _database[handle].database_header + 1;
2122 3 : _database[handle].attached = TRUE;
2123 3 : _database[handle].protect = FALSE;
2124 3 : _database[handle].protect_read = FALSE;
2125 3 : _database[handle].protect_write = FALSE;
2126 :
2127 3 : *hDB = (handle + 1);
2128 :
2129 3 : status = db_validate_open_records_wlocked(pheader, &msg);
2130 3 : if (status != DB_SUCCESS) {
2131 0 : db_unlock_database(handle + 1);
2132 0 : if (msg)
2133 0 : db_flush_msg(&msg);
2134 0 : cm_msg(MERROR, "db_open_database", "Error: db_validate_open_records() status %d", status);
2135 0 : return status;
2136 : }
2137 :
2138 3 : db_unlock_database(handle + 1);
2139 :
2140 3 : if (msg)
2141 0 : db_flush_msg(&msg);
2142 :
2143 3 : if (shm_created)
2144 3 : return DB_CREATED;
2145 : }
2146 : #endif /* LOCAL_ROUTINES */
2147 :
2148 0 : return DB_SUCCESS;
2149 : }
2150 :
2151 : /********************************************************************/
2152 : /**
2153 : Close a database
2154 : @param hDB ODB handle obtained via cm_get_experiment_database().
2155 : @return DB_SUCCESS, DB_INVALID_HANDLE, RPC_NET_ERROR
2156 : */
2157 3 : INT db_close_database(HNDLE hDB)
2158 : {
2159 3 : if (rpc_is_remote())
2160 0 : return rpc_call(RPC_DB_CLOSE_DATABASE, hDB);
2161 :
2162 : #ifdef LOCAL_ROUTINES
2163 : else {
2164 : INT destroy_flag, i, j;
2165 : char xname[256];
2166 :
2167 3 : if (hDB > _database_entries || hDB <= 0) {
2168 0 : cm_msg(MERROR, "db_close_database", "invalid database handle %d", hDB);
2169 0 : return DB_INVALID_HANDLE;
2170 : }
2171 :
2172 : /* flush database to disk */
2173 3 : db_flush_database(hDB);
2174 :
2175 : /* first lock database */
2176 3 : db_lock_database(hDB);
2177 :
2178 3 : DATABASE* pdb = &_database[hDB - 1];
2179 :
2180 3 : if (!pdb->attached) {
2181 0 : db_unlock_database(hDB);
2182 0 : cm_msg(MERROR, "db_close_database", "database not attached");
2183 0 : return DB_INVALID_HANDLE;
2184 : }
2185 :
2186 3 : DATABASE_HEADER *pheader = pdb->database_header;
2187 3 : DATABASE_CLIENT *pclient = db_get_my_client_locked(pdb);
2188 :
2189 3 : db_allow_write_locked(&_database[hDB-1], "db_close_database");
2190 :
2191 : /* close all open records */
2192 3 : for (i = 0; i < pclient->max_index; i++)
2193 0 : if (pclient->open_record[i].handle)
2194 0 : db_remove_open_record_wlocked(pdb, pheader, pclient->open_record[i].handle);
2195 :
2196 : /* mark entry in _database as empty */
2197 3 : pdb->attached = FALSE;
2198 :
2199 : /* clear entry from client structure in database header */
2200 3 : memset(pclient, 0, sizeof(DATABASE_CLIENT));
2201 :
2202 : /* calculate new max_client_index entry */
2203 195 : for (i = MAX_CLIENTS - 1; i >= 0; i--)
2204 192 : if (pheader->client[i].pid != 0)
2205 0 : break;
2206 3 : pheader->max_client_index = i + 1;
2207 :
2208 : /* count new number of clients */
2209 195 : for (i = MAX_CLIENTS - 1, j = 0; i >= 0; i--)
2210 192 : if (pheader->client[i].pid != 0)
2211 0 : j++;
2212 3 : pheader->num_clients = j;
2213 :
2214 3 : destroy_flag = (pheader->num_clients == 0);
2215 :
2216 : /* flush shared memory to disk before it gets deleted */
2217 3 : if (destroy_flag)
2218 3 : ss_shm_flush(pheader->name, pdb->shm_adr, pdb->shm_size, pdb->shm_handle, true);
2219 :
2220 3 : mstrlcpy(xname, pheader->name, sizeof(xname));
2221 :
2222 : /* unmap shared memory, delete it if we are the last */
2223 3 : ss_shm_close(xname, pdb->shm_adr, pdb->shm_size, pdb->shm_handle, destroy_flag);
2224 :
2225 3 : pheader = NULL; // after ss_shm_close(), pheader points nowhere
2226 3 : pdb->database_header = NULL; // ditto
2227 :
2228 : /* unlock database */
2229 3 : db_unlock_database(hDB);
2230 :
2231 : /* delete semaphore */
2232 3 : ss_semaphore_delete(pdb->semaphore, destroy_flag);
2233 :
2234 : /* delete mutex */
2235 3 : ss_mutex_delete(pdb->mutex);
2236 :
2237 : /* update _database_entries */
2238 3 : if (hDB == _database_entries)
2239 3 : _database_entries--;
2240 :
2241 3 : if (_database_entries > 0)
2242 0 : _database = (DATABASE *) realloc(_database, sizeof(DATABASE) * (_database_entries));
2243 : else {
2244 3 : free(_database);
2245 3 : _database = NULL;
2246 : }
2247 :
2248 : /* if we are the last one, also delete other semaphores */
2249 3 : if (destroy_flag) {
2250 : //extern INT _semaphore_elog, _semaphore_alarm, _semaphore_history, _semaphore_msg;
2251 : extern INT _semaphore_elog, _semaphore_alarm, _semaphore_history;
2252 :
2253 3 : if (_semaphore_elog)
2254 3 : ss_semaphore_delete(_semaphore_elog, TRUE);
2255 3 : if (_semaphore_alarm)
2256 3 : ss_semaphore_delete(_semaphore_alarm, TRUE);
2257 3 : if (_semaphore_history)
2258 3 : ss_semaphore_delete(_semaphore_history, TRUE);
2259 : //if (_semaphore_msg)
2260 : // ss_semaphore_delete(_semaphore_msg, TRUE);
2261 : }
2262 :
2263 : }
2264 : #endif /* LOCAL_ROUTINES */
2265 :
2266 3 : return DB_SUCCESS;
2267 : }
2268 :
2269 : /**dox***************************************************************/
2270 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
2271 :
2272 : /*------------------------------------------------------------------*/
2273 3 : INT db_flush_database(HNDLE hDB)
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 3 : if (rpc_is_remote())
2295 0 : return rpc_call(RPC_DB_FLUSH_DATABASE, hDB);
2296 :
2297 : #ifdef LOCAL_ROUTINES
2298 : else {
2299 : int status, size;
2300 3 : uint32_t flush_period = 60;
2301 3 : uint32_t last_flush = 0;
2302 :
2303 3 : if (hDB > _database_entries || hDB <= 0) {
2304 0 : cm_msg(MERROR, "db_flush_database", "invalid database handle");
2305 0 : return DB_INVALID_HANDLE;
2306 : }
2307 :
2308 : /* create keys if not present */
2309 3 : size = sizeof(flush_period);
2310 3 : db_get_value(hDB, 0, "/System/Flush/Flush period", &flush_period, &size, TID_UINT32, true);
2311 3 : size = sizeof(last_flush);
2312 3 : db_get_value(hDB, 0, "/System/Flush/Last flush", &last_flush, &size, TID_UINT32, true);
2313 :
2314 : HNDLE hkey;
2315 3 : status = db_find_key(hDB, 0, "/System/Flush/Last flush", &hkey);
2316 3 : if (status != DB_SUCCESS) {
2317 0 : cm_msg(MERROR, "db_flush_database", "Cannot obtain key /System/Flush/Last flush");
2318 0 : return DB_INVALID_HANDLE;
2319 : }
2320 :
2321 3 : db_lock_database(hDB);
2322 3 : DATABASE *pdb = &_database[hDB - 1];
2323 3 : DATABASE_HEADER *pheader = pdb->database_header;
2324 :
2325 3 : if (!_database[hDB - 1].attached) {
2326 0 : db_unlock_database(hDB);
2327 0 : cm_msg(MERROR, "db_flush_database", "invalid database handle");
2328 0 : return DB_INVALID_HANDLE;
2329 : }
2330 :
2331 3 : db_err_msg *msg = nullptr;
2332 :
2333 3 : const KEY *pkey = db_get_pkey(pheader, hkey, &status, "db_flush_database", &msg);
2334 :
2335 3 : if (!pkey) {
2336 0 : db_unlock_database(hDB);
2337 0 : if (msg)
2338 0 : db_flush_msg(&msg);
2339 0 : return CM_NO_CLIENT;
2340 : }
2341 :
2342 3 : size = sizeof(last_flush);
2343 3 : status = db_get_data_locked(pheader, pkey, 0, &last_flush, &size, TID_UINT32, &msg);
2344 :
2345 : /* only flush if period has expired */
2346 3 : if (ss_time() > last_flush + flush_period) {
2347 1 : db_allow_write_locked(pdb, "db_flush_database");
2348 :
2349 : /* update last flush time in ODB */
2350 1 : last_flush = ss_time();
2351 1 : 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 1 : ss_shm_flush(pheader->name, _database[hDB - 1].shm_adr, _database[hDB - 1].shm_size, _database[hDB - 1].shm_handle, false);
2355 : }
2356 :
2357 3 : db_unlock_database(hDB);
2358 : }
2359 : #endif /* LOCAL_ROUTINES */
2360 :
2361 3 : return DB_SUCCESS;
2362 : }
2363 :
2364 : /*------------------------------------------------------------------*/
2365 2 : INT db_close_all_databases(void)
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 2 : if (rpc_is_remote()) {
2386 0 : status = rpc_call(RPC_DB_CLOSE_ALL_DATABASES);
2387 0 : if (status != DB_SUCCESS)
2388 0 : return status;
2389 : }
2390 :
2391 2 : db_close_all_records();
2392 2 : db_unwatch_all();
2393 :
2394 : #ifdef LOCAL_ROUTINES
2395 : {
2396 : INT i;
2397 :
2398 4 : for (i = _database_entries; i > 0; i--)
2399 2 : db_close_database(i);
2400 : }
2401 : #endif /* LOCAL_ROUTINES */
2402 :
2403 2 : return DB_SUCCESS;
2404 : }
2405 :
2406 : /*------------------------------------------------------------------*/
2407 2 : INT 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 2 : if (rpc_is_remote())
2428 0 : return rpc_call(RPC_DB_SET_CLIENT_NAME, hDB, client_name);
2429 :
2430 : #ifdef LOCAL_ROUTINES
2431 : {
2432 2 : if (hDB > _database_entries || hDB <= 0) {
2433 0 : cm_msg(MERROR, "db_set_client_name", "invalid database handle %d", hDB);
2434 0 : return DB_INVALID_HANDLE;
2435 : }
2436 :
2437 2 : db_lock_database(hDB);
2438 :
2439 2 : DATABASE *pdb = &_database[hDB - 1];
2440 2 : DATABASE_CLIENT *pclient = db_get_my_client_locked(pdb);
2441 2 : mstrlcpy(pclient->name, client_name, sizeof(pclient->name));
2442 :
2443 2 : db_unlock_database(hDB);
2444 : }
2445 : #endif /* LOCAL_ROUTINES */
2446 :
2447 2 : return DB_SUCCESS;
2448 : }
2449 :
2450 : /**dox***************************************************************/
2451 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
2452 :
2453 : /********************************************************************/
2454 : /**
2455 : Lock a database for exclusive access via system semaphore calls.
2456 : @param hDB Handle to the database to lock
2457 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TIMEOUT
2458 : */
2459 :
2460 1103 : INT db_lock_database(HNDLE hDB)
2461 : {
2462 : #ifdef LOCAL_ROUTINES
2463 : int status;
2464 :
2465 1103 : if (hDB > _database_entries || hDB <= 0) {
2466 0 : cm_msg(MERROR, "db_lock_database", "invalid database handle %d, aborting...", hDB);
2467 0 : abort();
2468 : return DB_INVALID_HANDLE;
2469 : }
2470 :
2471 : /* obtain access mutex in multi-thread applications */
2472 1103 : status = ss_mutex_wait_for(_database[hDB - 1].mutex, _database[hDB - 1].timeout);
2473 1103 : if (status != SS_SUCCESS) {
2474 0 : cm_msg(MERROR, "db_lock_database", "cannot lock ODB mutex, timeout %d ms, status %d, aborting...", _database[hDB - 1].timeout, status);
2475 0 : abort();
2476 : }
2477 :
2478 : /* protect this function against recursive call from signal handlers */
2479 1103 : if (_database[hDB - 1].inside_lock_unlock) {
2480 0 : 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 0 : abort();
2482 : }
2483 :
2484 1103 : _database[hDB - 1].inside_lock_unlock = 1;
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 1103 : if (_database[hDB - 1].lock_cnt == 0) {
2502 791 : _database[hDB - 1].lock_cnt = 1;
2503 : /* wait max. 5 minutes for semaphore (required if locking process is being debugged) */
2504 791 : status = ss_semaphore_wait_for(_database[hDB - 1].semaphore, _database[hDB - 1].timeout);
2505 791 : if (status == SS_TIMEOUT) {
2506 0 : 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 0 : abort();
2509 : }
2510 791 : if (status != SS_SUCCESS) {
2511 0 : 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 0 : abort();
2513 : }
2514 : } else {
2515 312 : _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);
2523 : ss_stack_history_entry(str);
2524 : }
2525 : #endif
2526 :
2527 1103 : if (_database[hDB - 1].protect) {
2528 0 : if (_database[hDB - 1].database_header == NULL) {
2529 : int status;
2530 0 : assert(!_database[hDB - 1].protect_read);
2531 0 : assert(!_database[hDB - 1].protect_write);
2532 0 : 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 0 : if (status != SS_SUCCESS) {
2534 0 : cm_msg(MERROR, "db_lock_database", "cannot lock ODB, ss_shm_unprotect(TRUE,FALSE) failed with status %d, aborting...", status);
2535 0 : cm_msg_flush_buffer();
2536 0 : ss_semaphore_release(_database[hDB - 1].semaphore);
2537 0 : abort();
2538 : }
2539 0 : _database[hDB - 1].database_header = (DATABASE_HEADER *) _database[hDB - 1].shm_adr;
2540 0 : _database[hDB - 1].protect_read = TRUE;
2541 0 : _database[hDB - 1].protect_write = FALSE;
2542 : }
2543 : }
2544 :
2545 1103 : _database[hDB - 1].inside_lock_unlock = 0;
2546 :
2547 : #endif /* LOCAL_ROUTINES */
2548 :
2549 1103 : return DB_SUCCESS;
2550 : }
2551 :
2552 : #ifdef LOCAL_ROUTINES
2553 327 : INT db_allow_write_locked(DATABASE* p, const char* caller_name)
2554 : {
2555 327 : assert(p);
2556 327 : if (p->protect && !p->protect_write) {
2557 : int status;
2558 0 : assert(p->lock_cnt > 0);
2559 0 : assert(p->database_header != NULL);
2560 0 : assert(p->protect_read);
2561 0 : status = ss_shm_unprotect(p->shm_handle, &p->shm_adr, p->shm_size, TRUE, TRUE, caller_name);
2562 0 : if (status != SS_SUCCESS) {
2563 0 : cm_msg(MERROR, "db_allow_write_locked", "cannot write to ODB, ss_shm_unprotect(TRUE,TRUE) failed with status %d, aborting...", status);
2564 0 : cm_msg_flush_buffer();
2565 0 : ss_semaphore_release(p->semaphore);
2566 0 : abort();
2567 : }
2568 0 : p->database_header = (DATABASE_HEADER *) p->shm_adr;
2569 0 : p->protect_read = TRUE;
2570 0 : p->protect_write = TRUE;
2571 : }
2572 327 : return DB_SUCCESS;
2573 : }
2574 : #endif /* LOCAL_ROUTINES */
2575 :
2576 : /********************************************************************/
2577 : /**
2578 : Unlock a database via system semaphore calls.
2579 : @param hDB Handle to the database to unlock
2580 : @return DB_SUCCESS, DB_INVALID_HANDLE
2581 : */
2582 1103 : INT db_unlock_database(HNDLE hDB)
2583 : {
2584 : #ifdef LOCAL_ROUTINES
2585 :
2586 1103 : if (hDB > _database_entries || hDB <= 0) {
2587 0 : cm_msg(MERROR, "db_unlock_database", "invalid database handle %d", hDB);
2588 0 : 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);
2595 : ss_stack_history_entry(str);
2596 : }
2597 : #endif
2598 :
2599 : /* protect this function against recursive call from signal handlers */
2600 1103 : if (_database[hDB - 1].inside_lock_unlock) {
2601 0 : 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 0 : abort();
2603 : }
2604 :
2605 1103 : _database[hDB - 1].inside_lock_unlock = 1;
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 1103 : if (_database[hDB - 1].lock_cnt == 1) {
2615 791 : ss_semaphore_release(_database[hDB - 1].semaphore);
2616 :
2617 791 : if (_database[hDB - 1].protect && _database[hDB - 1].database_header) {
2618 : int status;
2619 0 : assert(_database[hDB - 1].protect_read);
2620 0 : assert(_database[hDB - 1].database_header);
2621 0 : _database[hDB - 1].database_header = NULL;
2622 0 : status = ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].shm_adr, _database[hDB - 1].shm_size);
2623 0 : if (status != SS_SUCCESS) {
2624 0 : cm_msg(MERROR, "db_unlock_database", "cannot unlock ODB, ss_shm_protect() failed with status %d, aborting...", status);
2625 0 : cm_msg_flush_buffer();
2626 0 : abort();
2627 : }
2628 0 : _database[hDB - 1].protect_read = FALSE;
2629 0 : _database[hDB - 1].protect_write = FALSE;
2630 : }
2631 : }
2632 :
2633 1103 : assert(_database[hDB - 1].lock_cnt > 0);
2634 1103 : _database[hDB - 1].lock_cnt--;
2635 :
2636 1103 : _database[hDB - 1].inside_lock_unlock = 0;
2637 :
2638 : /* release mutex for multi-thread applications */
2639 1103 : ss_mutex_release(_database[hDB - 1].mutex);
2640 :
2641 : #endif /* LOCAL_ROUTINES */
2642 1103 : return DB_SUCCESS;
2643 : }
2644 :
2645 : /********************************************************************/
2646 : #if 0
2647 : INT db_get_lock_cnt(HNDLE hDB)
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 :
2669 4 : INT db_set_lock_timeout(HNDLE hDB, int timeout_millisec)
2670 : {
2671 : #ifdef LOCAL_ROUTINES
2672 :
2673 : /* return zero if no ODB is open or we run remotely */
2674 4 : if (_database_entries == 0)
2675 0 : return 0;
2676 :
2677 4 : if (hDB > _database_entries || hDB <= 0) {
2678 0 : cm_msg(MERROR, "db_set_lock_timeout", "invalid database handle %d", hDB);
2679 0 : return 0;
2680 : }
2681 :
2682 4 : if (timeout_millisec > 0) {
2683 2 : _database[hDB - 1].timeout = timeout_millisec;
2684 : }
2685 :
2686 4 : return _database[hDB - 1].timeout;
2687 : #else
2688 : return 0;
2689 : #endif
2690 : }
2691 :
2692 : #ifdef LOCAL_ROUTINES
2693 :
2694 : /**
2695 : Update last activity time
2696 : */
2697 0 : INT db_update_last_activity(DWORD millitime)
2698 : {
2699 0 : bool found = false;
2700 0 : int pid = ss_getpid();
2701 0 : for (int i = 0; i < _database_entries; i++) {
2702 0 : if (_database[i].attached) {
2703 0 : db_lock_database(i + 1);
2704 0 : db_allow_write_locked(&_database[i], "db_update_last_activity");
2705 0 : assert(_database[i].database_header);
2706 : /* update the last_activity entry to show that we are alive */
2707 0 : for (int j=0; j<_database[i].database_header->max_client_index; j++) {
2708 0 : DATABASE_CLIENT* pdbclient = _database[i].database_header->client + j;
2709 : //printf("client %d pid %d vs our pid %d\n", j, pdbclient->pid, pid);
2710 0 : if (pdbclient->pid == pid) {
2711 0 : pdbclient->last_activity = millitime;
2712 0 : found = true;
2713 : }
2714 : }
2715 0 : db_unlock_database(i + 1);
2716 : }
2717 : }
2718 :
2719 0 : if (!found) {
2720 0 : 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 0 : abort();
2722 : }
2723 :
2724 0 : return DB_SUCCESS;
2725 : }
2726 :
2727 : #endif // LOCAL_ROUTINES
2728 :
2729 : #ifdef LOCAL_ROUTINES
2730 0 : static void db_delete_client_wlocked(DATABASE_HEADER* pheader, int jclient, db_err_msg** msg)
2731 : {
2732 0 : DATABASE_CLIENT* pdbclient = &pheader->client[jclient];
2733 :
2734 : /* decrement notify_count for open records and clear exclusive mode */
2735 : int k;
2736 0 : for (k = 0; k < pdbclient->max_index; k++)
2737 0 : if (pdbclient->open_record[k].handle) {
2738 0 : KEY* pkey = (KEY *) ((char *) pheader + pdbclient->open_record[k].handle);
2739 0 : if (pkey->notify_count > 0)
2740 0 : pkey->notify_count--;
2741 :
2742 0 : if (pdbclient->open_record[k].access_mode & MODE_WRITE)
2743 0 : 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 0 : memset(pdbclient, 0, sizeof(DATABASE_CLIENT));
2748 :
2749 : /* calculate new max_client_index entry */
2750 0 : for (k = MAX_CLIENTS - 1; k >= 0; k--)
2751 0 : if (pheader->client[k].pid != 0)
2752 0 : break;
2753 0 : pheader->max_client_index = k + 1;
2754 :
2755 : /* count new number of clients */
2756 : int nc;
2757 0 : for (k = MAX_CLIENTS - 1, nc = 0; k >= 0; k--)
2758 0 : if (pheader->client[k].pid != 0)
2759 0 : nc++;
2760 0 : pheader->num_clients = nc;
2761 0 : }
2762 : #endif
2763 :
2764 : #ifdef LOCAL_ROUTINES
2765 2 : static int db_delete_client_info_wlocked(HNDLE hDB, DATABASE_HEADER* pheader, int pid, db_err_msg** msg)
2766 : {
2767 2 : if (!pid)
2768 2 : pid = ss_getpid();
2769 :
2770 : char str[256];
2771 2 : int status = 0;
2772 :
2773 2 : sprintf(str, "System/Clients/%0d", pid);
2774 2 : KEY* pkey = (KEY*)db_find_pkey_locked(pheader, NULL, str, &status, msg);
2775 2 : if (!pkey) {
2776 0 : return status;
2777 : }
2778 :
2779 2 : status = db_set_mode_wlocked(pheader, pkey, MODE_READ | MODE_WRITE | MODE_DELETE, 2, msg);
2780 2 : HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
2781 2 : status = db_delete_key1(hDB, hKey, 1, TRUE);
2782 2 : int32_t data = 0;
2783 2 : db_set_value_wlocked(pheader, hDB, 0, "/System/Client Notify", &data, sizeof(data), 1, TID_INT32, msg);
2784 :
2785 2 : return status;
2786 : }
2787 : #endif
2788 :
2789 : /********************************************************************/
2790 : /**
2791 : Delete client info from database
2792 : @param hDB Database handle
2793 : @param pid PID of entry to delete, zero for this process.
2794 : @return CM_SUCCESS
2795 : */
2796 2 : int db_delete_client_info(HNDLE hDB, int pid)
2797 : {
2798 : #ifdef LOCAL_ROUTINES
2799 2 : if (hDB > _database_entries || hDB <= 0) {
2800 0 : cm_msg(MERROR, "db_delete_client_info", "invalid database handle");
2801 0 : return DB_INVALID_HANDLE;
2802 : }
2803 :
2804 2 : if (!_database[hDB - 1].attached) {
2805 0 : cm_msg(MERROR, "db_delete_client_info", "invalid database handle");
2806 0 : return DB_INVALID_HANDLE;
2807 : }
2808 :
2809 : /* lock database */
2810 2 : db_lock_database(hDB);
2811 :
2812 2 : DATABASE *pdb = &_database[hDB - 1];
2813 2 : DATABASE_HEADER *pheader = pdb->database_header;
2814 :
2815 2 : db_allow_write_locked(pdb, "db_delete_client_info");
2816 :
2817 2 : db_err_msg* msg = NULL;
2818 :
2819 2 : int status = db_delete_client_info_wlocked(hDB, pheader, pid, &msg);
2820 :
2821 2 : db_unlock_database(hDB);
2822 :
2823 2 : if (msg)
2824 0 : db_flush_msg(&msg);
2825 :
2826 2 : return status;
2827 : #else
2828 : return DB_SUCCESS;
2829 : #endif
2830 : }
2831 :
2832 0 : void db_cleanup(const char *who, DWORD actual_time, BOOL wrong_interval)
2833 : {
2834 : #ifdef LOCAL_ROUTINES
2835 : int i;
2836 : /* check online databases */
2837 0 : for (i = 0; i < _database_entries; i++) {
2838 0 : DATABASE *pdb = &_database[i];
2839 0 : if (!pdb->attached) // empty slot
2840 0 : continue;
2841 :
2842 0 : db_lock_database(i + 1);
2843 0 : db_allow_write_locked(pdb, "db_cleanup");
2844 :
2845 0 : DATABASE_HEADER* pheader = pdb->database_header;
2846 0 : DATABASE_CLIENT* pclient = db_get_my_client_locked(pdb);
2847 :
2848 : /* update the last_activity entry to show that we are alive */
2849 0 : pclient->last_activity = actual_time;
2850 :
2851 : /* don't check other clients if interval is stange */
2852 0 : if (wrong_interval) {
2853 0 : db_unlock_database(i + 1);
2854 0 : continue;
2855 : }
2856 :
2857 0 : db_err_msg *msg = NULL;
2858 :
2859 : /* now check other clients */
2860 : int j;
2861 0 : for (j = 0; j < pheader->max_client_index; j++) {
2862 0 : DATABASE_CLIENT *pdbclient = &pheader->client[j];
2863 0 : int client_pid = pdbclient->pid;
2864 0 : if (client_pid == 0) // empty slot
2865 0 : continue;
2866 :
2867 0 : if (!ss_pid_exists(client_pid)) {
2868 0 : 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 0 : db_delete_client_wlocked(pheader, j, &msg);
2871 0 : db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2872 :
2873 0 : continue;
2874 : }
2875 :
2876 : /* now make again the check with the buffer locked */
2877 0 : actual_time = ss_millitime();
2878 0 : if ((pdbclient->watchdog_timeout &&
2879 0 : actual_time > pdbclient->last_activity &&
2880 0 : actual_time - pdbclient->last_activity > pdbclient->watchdog_timeout)
2881 : ) {
2882 0 : 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 0 : pdbclient->name, pheader->name, client_pid,
2884 : who,
2885 0 : (actual_time - pdbclient->last_activity) / 1000.0,
2886 0 : pdbclient->watchdog_timeout / 1000.0);
2887 :
2888 0 : db_delete_client_wlocked(pheader, j, &msg);
2889 0 : db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2890 : }
2891 : }
2892 :
2893 0 : db_unlock_database(i + 1);
2894 0 : if (msg)
2895 0 : db_flush_msg(&msg);
2896 : }
2897 : #endif
2898 0 : }
2899 :
2900 : #ifdef LOCAL_ROUTINES
2901 :
2902 0 : void db_cleanup2(const char* client_name, int ignore_timeout, DWORD actual_time, const char *who)
2903 : {
2904 : /* check online databases */
2905 : int i;
2906 0 : for (i = 0; i < _database_entries; i++) {
2907 0 : if (_database[i].attached) {
2908 : /* update the last_activity entry to show that we are alive */
2909 :
2910 0 : db_lock_database(i + 1);
2911 :
2912 0 : db_err_msg *msg = NULL;
2913 :
2914 0 : DATABASE* pdb = &_database[i];
2915 :
2916 0 : db_allow_write_locked(pdb, "db_cleanup2");
2917 :
2918 0 : DWORD now = ss_millitime();
2919 :
2920 0 : DATABASE_HEADER* pheader = pdb->database_header;
2921 0 : DATABASE_CLIENT* pclient = db_get_my_client_locked(pdb);
2922 0 : pclient->last_activity = now;
2923 :
2924 : /* now check other clients */
2925 : int j;
2926 0 : for (j = 0; j < pheader->max_client_index; j++) {
2927 0 : DATABASE_CLIENT* pdbclient = &pheader->client[j];
2928 0 : if (j == pdb->client_index) // do not check ourselves
2929 0 : continue;
2930 0 : if (!pdbclient->pid) // empty slot
2931 0 : continue;
2932 0 : if ((client_name == NULL || client_name[0] == 0
2933 0 : || strncmp(pdbclient->name, client_name, strlen(client_name)) == 0)) {
2934 0 : int client_pid = pdbclient->pid;
2935 :
2936 0 : if (!ss_pid_exists(client_pid)) {
2937 0 : 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 0 : pdbclient->name, pheader->name,
2939 : client_pid,
2940 : who);
2941 :
2942 0 : db_delete_client_wlocked(pheader, j, &msg);
2943 0 : db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2944 :
2945 : /* go again though whole list */
2946 0 : j = 0;
2947 0 : continue;
2948 : }
2949 :
2950 : DWORD interval;
2951 0 : if (ignore_timeout)
2952 0 : interval = 2 * WATCHDOG_INTERVAL;
2953 : else
2954 0 : interval = pdbclient->watchdog_timeout;
2955 :
2956 : /* If client process has no activity, clear its buffer entry. */
2957 :
2958 0 : if ((interval > 0 && now - pdbclient->last_activity > interval)) {
2959 0 : 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 0 : pdbclient->name, pheader->name,
2961 : who,
2962 0 : (now - pdbclient->last_activity) / 1000.0, interval / 1000.0);
2963 :
2964 0 : db_delete_client_wlocked(pheader, j, &msg);
2965 0 : db_delete_client_info_wlocked(i+1, pheader, client_pid, &msg);
2966 :
2967 : /* go again though whole list */
2968 0 : j = 0;
2969 0 : continue;
2970 : }
2971 : }
2972 : }
2973 :
2974 0 : db_unlock_database(i + 1);
2975 0 : if (msg)
2976 0 : db_flush_msg(&msg);
2977 : }
2978 : }
2979 0 : }
2980 :
2981 4 : void db_set_watchdog_params(DWORD timeout)
2982 : {
2983 : /* set watchdog flag of all open databases */
2984 8 : for (int i = _database_entries; i > 0; i--) {
2985 :
2986 4 : db_lock_database(i);
2987 :
2988 4 : DATABASE* pdb = &_database[i - 1];
2989 :
2990 4 : if (!pdb->attached) {
2991 0 : db_unlock_database(i);
2992 0 : continue;
2993 : }
2994 :
2995 4 : DATABASE_CLIENT *pclient = db_get_my_client_locked(pdb);
2996 :
2997 4 : db_allow_write_locked(pdb, "db_set_watchdog_params");
2998 :
2999 : /* clear entry from client structure in buffer header */
3000 4 : pclient->watchdog_timeout = timeout;
3001 :
3002 : /* show activity */
3003 4 : pclient->last_activity = ss_millitime();
3004 :
3005 4 : db_unlock_database(i);
3006 : }
3007 4 : }
3008 :
3009 : /********************************************************************/
3010 : /**
3011 : Return watchdog information about specific client
3012 : @param hDB ODB handle
3013 : @param client_name ODB client name
3014 : @param timeout Timeout for this application in seconds
3015 : @param last Last time watchdog was called in msec
3016 : @return CM_SUCCESS, CM_NO_CLIENT, DB_INVALID_HANDLE
3017 : */
3018 :
3019 0 : INT db_get_watchdog_info(HNDLE hDB, const char *client_name, DWORD * timeout, DWORD * last)
3020 : {
3021 0 : if (hDB > _database_entries || hDB <= 0) {
3022 0 : cm_msg(MERROR, "db_get_watchdog_info", "invalid database handle");
3023 0 : return DB_INVALID_HANDLE;
3024 : }
3025 :
3026 0 : if (!_database[hDB - 1].attached) {
3027 0 : cm_msg(MERROR, "db_get_watchdog_info", "invalid database handle");
3028 0 : return DB_INVALID_HANDLE;
3029 : }
3030 :
3031 : /* lock database */
3032 0 : db_lock_database(hDB);
3033 :
3034 0 : DATABASE *pdb = &_database[hDB - 1];
3035 0 : DATABASE_HEADER *pheader = pdb->database_header;
3036 :
3037 : /* find client */
3038 0 : for (int i = 0; i < pheader->max_client_index; i++) {
3039 0 : DATABASE_CLIENT *pclient = &pheader->client[i];
3040 0 : if (pclient->pid && equal_ustring(pclient->name, client_name)) {
3041 0 : *timeout = pclient->watchdog_timeout;
3042 0 : *last = ss_millitime() - pclient->last_activity;
3043 0 : db_unlock_database(hDB);
3044 0 : return DB_SUCCESS;
3045 : }
3046 : }
3047 :
3048 0 : *timeout = *last = 0;
3049 :
3050 0 : db_unlock_database(hDB);
3051 :
3052 0 : return CM_NO_CLIENT;
3053 : }
3054 :
3055 : /********************************************************************/
3056 : /**
3057 : Check if a client with a /system/client/xxx entry has
3058 : a valid entry in the ODB client table. If not, remove
3059 : that client from the /system/client tree.
3060 : @param hDB Handle to online database
3061 : @param hKeyClient Handle to client key
3062 : @return CM_SUCCESS, CM_NO_CLIENT
3063 : */
3064 0 : INT db_check_client(HNDLE hDB, HNDLE hKeyClient)
3065 : {
3066 0 : if (hDB > _database_entries || hDB <= 0) {
3067 0 : cm_msg(MERROR, "db_check_client", "invalid database handle");
3068 0 : return DB_INVALID_HANDLE;
3069 : }
3070 :
3071 0 : if (!_database[hDB - 1].attached) {
3072 0 : cm_msg(MERROR, "db_check_client", "invalid database handle");
3073 0 : return DB_INVALID_HANDLE;
3074 : }
3075 :
3076 : int status;
3077 :
3078 0 : db_lock_database(hDB);
3079 :
3080 0 : db_err_msg* msg = NULL;
3081 :
3082 0 : DATABASE *pdb = &_database[hDB - 1];
3083 0 : DATABASE_HEADER *pheader = pdb->database_header;
3084 :
3085 0 : const KEY* pkey = db_get_pkey(pheader, hKeyClient, &status, "db_check_client", &msg);
3086 :
3087 0 : if (!pkey) {
3088 0 : db_unlock_database(hDB);
3089 0 : if (msg)
3090 0 : db_flush_msg(&msg);
3091 0 : return CM_NO_CLIENT;
3092 : }
3093 :
3094 0 : int client_pid = atoi(pkey->name);
3095 :
3096 0 : const KEY* pkey_name = db_find_pkey_locked(pheader, pkey, "Name", &status, &msg);
3097 :
3098 0 : if (!pkey_name) {
3099 0 : db_unlock_database(hDB);
3100 0 : if (msg)
3101 0 : db_flush_msg(&msg);
3102 0 : return CM_NO_CLIENT;
3103 : }
3104 :
3105 : char name[256];
3106 0 : name[0] = 0;
3107 0 : int size = sizeof(name);
3108 0 : 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 0 : if (status != DB_SUCCESS) {
3113 0 : db_unlock_database(hDB);
3114 0 : if (msg)
3115 0 : db_flush_msg(&msg);
3116 0 : return CM_NO_CLIENT;
3117 : }
3118 :
3119 0 : bool dead = false;
3120 0 : bool found = false;
3121 :
3122 : /* loop through clients */
3123 0 : for (int i = 0; i < pheader->max_client_index; i++) {
3124 0 : DATABASE_CLIENT *pclient = &pheader->client[i];
3125 0 : if (pclient->pid == client_pid) {
3126 0 : found = true;
3127 0 : break;
3128 : }
3129 : }
3130 :
3131 0 : if (found) {
3132 : /* check that the client is still running: PID still exists */
3133 0 : if (!ss_pid_exists(client_pid)) {
3134 0 : dead = true;
3135 : }
3136 : }
3137 :
3138 0 : status = DB_SUCCESS;
3139 :
3140 0 : if (!found || dead) {
3141 : /* client not found : delete ODB stucture */
3142 :
3143 0 : db_allow_write_locked(pdb, "db_check_client");
3144 :
3145 0 : status = db_delete_client_info_wlocked(hDB, pheader, client_pid, &msg);
3146 :
3147 0 : if (status != DB_SUCCESS)
3148 0 : 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 0 : else if (!found)
3150 0 : 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 0 : else if (dead)
3152 0 : 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 :
3154 0 : status = CM_NO_CLIENT;
3155 : }
3156 :
3157 0 : db_unlock_database(hDB);
3158 0 : if (msg)
3159 0 : db_flush_msg(&msg);
3160 :
3161 0 : return status;
3162 : }
3163 :
3164 : #endif // LOCAL_ROUTINES
3165 :
3166 : /********************************************************************/
3167 : /**
3168 : Protect a database for read/write access outside of the \b db_xxx functions
3169 : @param hDB ODB handle obtained via cm_get_experiment_database().
3170 : @return DB_SUCCESS, DB_INVALID_HANDLE
3171 : */
3172 0 : INT db_protect_database(HNDLE hDB)
3173 : {
3174 0 : if (rpc_is_remote())
3175 0 : return DB_SUCCESS;
3176 :
3177 : #ifdef LOCAL_ROUTINES
3178 0 : if (hDB > _database_entries || hDB <= 0) {
3179 0 : cm_msg(MERROR, "db_protect_database", "invalid database handle %d", hDB);
3180 0 : return DB_INVALID_HANDLE;
3181 : }
3182 :
3183 0 : _database[hDB - 1].protect = TRUE;
3184 0 : ss_shm_protect(_database[hDB - 1].shm_handle, _database[hDB - 1].database_header, _database[hDB - 1].shm_size);
3185 0 : _database[hDB - 1].database_header = NULL;
3186 : #endif /* LOCAL_ROUTINES */
3187 0 : return DB_SUCCESS;
3188 : }
3189 :
3190 : /*---- helper routines ---------------------------------------------*/
3191 :
3192 1301 : const char *extract_key(const char *key_list, char *key_name, int key_name_length)
3193 : {
3194 1301 : int i = 0;
3195 :
3196 1301 : if (*key_list == '/')
3197 982 : key_list++;
3198 :
3199 14849 : while (*key_list && *key_list != '/' && ++i < key_name_length)
3200 13548 : *key_name++ = *key_list++;
3201 1301 : *key_name = 0;
3202 :
3203 1301 : return key_list;
3204 : }
3205 :
3206 4627 : BOOL equal_ustring(const char *str1, const char *str2)
3207 : {
3208 4627 : if (str1 == NULL && str2 != NULL)
3209 0 : return FALSE;
3210 4627 : if (str1 != NULL && str2 == NULL)
3211 0 : return FALSE;
3212 4627 : if (str1 == NULL && str2 == NULL)
3213 0 : return TRUE;
3214 4627 : if (strlen(str1) != strlen(str2))
3215 3158 : return FALSE;
3216 :
3217 12397 : while (*str1)
3218 11419 : if (toupper(*str1++) != toupper(*str2++))
3219 491 : return FALSE;
3220 :
3221 978 : if (*str2)
3222 0 : return FALSE;
3223 :
3224 978 : return TRUE;
3225 : }
3226 :
3227 0 : BOOL ends_with_ustring(const char *str, const char *suffix)
3228 : {
3229 0 : int len_str = strlen(str);
3230 0 : int len_suffix = strlen(suffix);
3231 :
3232 : // suffix is longer than the string
3233 0 : if (len_suffix > len_str)
3234 0 : return FALSE;
3235 :
3236 0 : return equal_ustring(str + len_str - len_suffix, suffix);
3237 : }
3238 :
3239 : //utility function to match string against wildcard pattern
3240 0 : BOOL strmatch(char* pattern, char* str){
3241 0 : switch(*pattern){
3242 0 : case 0:
3243 0 : if(*str == 0){
3244 : //end of pattern
3245 0 : return true;
3246 : }else
3247 0 : return false;
3248 0 : case '*':
3249 : {
3250 0 : int i=0;
3251 : // check for end of the string
3252 0 : if(pattern[1] == 0) return true;
3253 : // loop on all possible matches
3254 0 : while(str[i] != 0) if(strmatch(pattern + 1, str+(i++))) return true;
3255 0 : return false;
3256 : }
3257 0 : case '?':
3258 0 : if(*str == 0){
3259 : //end of string
3260 0 : return false;
3261 : } else {
3262 0 : return strmatch(pattern+1, str+1);
3263 : }
3264 0 : default:
3265 0 : if(*str == 0){
3266 : //end of string
3267 0 : return false;
3268 0 : } else if(toupper(*str) == toupper(*pattern)){
3269 : //recursion
3270 0 : return strmatch(pattern+1, str+1);
3271 : } else {
3272 0 : return false;
3273 : }
3274 : }
3275 : }
3276 :
3277 : //utility function to extract array indexes
3278 0 : void strarrayindex(char* odbpath, int* index1, int* index2){
3279 : char* pc;
3280 :
3281 0 : *index1 = *index2 = 0;
3282 0 : if (odbpath[strlen(odbpath) - 1] == ']') {
3283 0 : if (strchr(odbpath, '[')) {
3284 0 : if (*(strchr(odbpath, '[') + 1) == '*')
3285 0 : *index1 = -1;
3286 0 : else if (strchr((strchr(odbpath, '[') + 1), '.') ||
3287 0 : strchr((strchr(odbpath, '[') + 1), '-')) {
3288 0 : *index1 = atoi(strchr(odbpath, '[') + 1);
3289 0 : pc = strchr(odbpath, '[') + 1;
3290 0 : while (*pc != '.' && *pc != '-')
3291 0 : pc++;
3292 0 : while (*pc == '.' || *pc == '-')
3293 0 : pc++;
3294 0 : *index2 = atoi(pc);
3295 : } else
3296 0 : *index1 = atoi(strchr(odbpath, '[') + 1);
3297 : }
3298 :
3299 : //remove everything after bracket
3300 0 : *strchr(odbpath, '[') = 0;
3301 : }
3302 0 : }
3303 :
3304 : /********************************************************************/
3305 : /**
3306 : Create a new key in a database
3307 : @param hDB ODB handle obtained via cm_get_experiment_database().
3308 : @param hKey Key handle to start with, 0 for root
3309 : @param key_name Name of key in the form "/key/key/key"
3310 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types)
3311 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_INVALID_PARAM, DB_FULL, DB_KEY_EXIST, DB_NO_ACCESS
3312 : */
3313 68 : INT db_create_key(HNDLE hDB, HNDLE hKey, const char *key_name, DWORD type)
3314 : {
3315 :
3316 68 : if (rpc_is_remote())
3317 0 : return rpc_call(RPC_DB_CREATE_KEY, hDB, hKey, key_name, type);
3318 :
3319 : #ifdef LOCAL_ROUTINES
3320 : {
3321 : int status;
3322 :
3323 68 : if (hDB > _database_entries || hDB <= 0) {
3324 0 : cm_msg(MERROR, "db_create_key", "invalid database handle");
3325 0 : return DB_INVALID_HANDLE;
3326 : }
3327 :
3328 68 : if (!_database[hDB - 1].attached) {
3329 0 : cm_msg(MERROR, "db_create_key", "invalid database handle");
3330 0 : return DB_INVALID_HANDLE;
3331 : }
3332 :
3333 : /* lock database */
3334 68 : db_lock_database(hDB);
3335 :
3336 68 : DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
3337 :
3338 68 : db_err_msg *msg = NULL;
3339 :
3340 68 : KEY* pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_create_key", &msg);
3341 :
3342 68 : if (!pkey) {
3343 0 : db_unlock_database(hDB);
3344 0 : if (msg)
3345 0 : db_flush_msg(&msg);
3346 0 : return DB_INVALID_HANDLE;
3347 : }
3348 :
3349 68 : db_allow_write_locked(&_database[hDB-1], "db_create_key");
3350 :
3351 68 : KEY* newpkey = NULL;
3352 :
3353 68 : status = db_create_key_wlocked(pheader, pkey, key_name, type, &newpkey, &msg);
3354 :
3355 68 : db_unlock_database(hDB);
3356 :
3357 68 : if (msg)
3358 7 : db_flush_msg(&msg);
3359 :
3360 68 : return status;
3361 : }
3362 : #endif /* LOCAL_ROUTINES */
3363 :
3364 : return DB_SUCCESS;
3365 : }
3366 :
3367 : #ifdef LOCAL_ROUTINES
3368 :
3369 138 : int 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 138 : if (pnewkey)
3374 138 : *pnewkey = NULL;
3375 :
3376 138 : if (parentKey== NULL) {
3377 0 : parentKey = (KEY*) db_get_pkey(pheader, pheader->root_key, &status, "db_create_key_wlocked", msg);
3378 0 : if (!parentKey) {
3379 0 : return status;
3380 : }
3381 : }
3382 :
3383 138 : status = db_validate_name(key_name, TRUE, "db_create_key_wlocked", msg);
3384 138 : if (status != DB_SUCCESS) {
3385 4 : return status;
3386 : }
3387 :
3388 : /* check type */
3389 134 : if (type <= 0 || type >= TID_LAST) {
3390 0 : 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 0 : return DB_INVALID_PARAM;
3392 : }
3393 :
3394 134 : const KEY* pdir = parentKey;
3395 :
3396 134 : if (pdir->type != TID_KEY) {
3397 0 : 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 0 : return DB_NO_KEY;
3399 : }
3400 :
3401 134 : KEY* pcreated = NULL;
3402 :
3403 134 : 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 */
3410 202 : pkey_name = extract_key(pkey_name, name, sizeof(name));
3411 :
3412 202 : status = db_validate_name(name, FALSE, "db_create_key", msg);
3413 202 : if (status != DB_SUCCESS) {
3414 4 : return status;
3415 : }
3416 :
3417 : /* do not allow empty names, like '/dir/dir//dir/' */
3418 199 : if (name[0] == 0) {
3419 0 : return DB_INVALID_PARAM;
3420 : }
3421 :
3422 : /* check if parent or current directory */
3423 199 : if (strcmp(name, "..") == 0) {
3424 0 : pdir = db_get_parent(pheader, pdir, &status, "db_create_key", msg);
3425 0 : if (!pdir) {
3426 0 : return status;
3427 : }
3428 0 : continue;
3429 : }
3430 199 : if (strcmp(name, ".") == 0)
3431 0 : continue;
3432 :
3433 199 : KEY* pprev = NULL;
3434 199 : const KEY* pitem = db_enum_first_locked(pheader, pdir, msg);
3435 :
3436 779 : while (pitem) {
3437 629 : if (equal_ustring(name, pitem->name)) {
3438 49 : break;
3439 : }
3440 580 : pprev = (KEY*)pitem;
3441 580 : pitem = db_enum_next_locked(pheader, pdir, pitem, msg);
3442 : }
3443 :
3444 199 : if (!pitem) {
3445 : /* not found: create new key */
3446 :
3447 : /* check parent for write access */
3448 150 : const KEY* pkeyparent = db_get_parent(pheader, pdir, &status, "db_create_key", msg);
3449 150 : if (!pkeyparent) {
3450 0 : return status;
3451 : }
3452 :
3453 150 : if (!(pkeyparent->access_mode & MODE_WRITE) || (pkeyparent->access_mode & MODE_EXCLUSIVE)) {
3454 0 : return DB_NO_ACCESS;
3455 : }
3456 :
3457 150 : KEYLIST* pkeylist = (KEYLIST*)db_get_pkeylist(pheader, db_pkey_to_hkey(pheader, pdir), pdir, "db_create_key_wlocked", msg);
3458 :
3459 150 : if (!pkeylist) {
3460 0 : return DB_CORRUPTED;
3461 : }
3462 :
3463 150 : pkeylist->num_keys++;
3464 :
3465 150 : if (*pkey_name == '/' || type == TID_KEY) {
3466 : /* create new key with keylist */
3467 38 : KEY* pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_create_key_subdir");
3468 :
3469 38 : if (pkey == NULL) {
3470 0 : if (pkeylist->num_keys > 0)
3471 0 : pkeylist->num_keys--;
3472 :
3473 0 : 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 0 : return DB_FULL;
3475 : }
3476 :
3477 : /* append key to key list */
3478 38 : if (pprev)
3479 25 : pprev->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
3480 : else
3481 13 : pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
3482 :
3483 : /* set key properties */
3484 38 : pkey->type = TID_KEY;
3485 38 : pkey->num_values = 1;
3486 38 : pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
3487 38 : mstrlcpy(pkey->name, name, sizeof(pkey->name));
3488 38 : pkey->parent_keylist = (POINTER_T) pkeylist - (POINTER_T) pheader;
3489 :
3490 : /* find space for new keylist */
3491 38 : pkeylist = (KEYLIST *) malloc_key(pheader, sizeof(KEYLIST), "db_create_key_B");
3492 :
3493 38 : if (pkeylist == NULL) {
3494 0 : 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 0 : return DB_FULL;
3496 : }
3497 :
3498 : /* store keylist in data field */
3499 38 : pkey->data = (POINTER_T) pkeylist - (POINTER_T) pheader;
3500 38 : pkey->item_size = sizeof(KEYLIST);
3501 38 : pkey->total_size = sizeof(KEYLIST);
3502 :
3503 38 : pkeylist->parent = (POINTER_T) pkey - (POINTER_T) pheader;
3504 38 : pkeylist->num_keys = 0;
3505 38 : pkeylist->first_key = 0;
3506 :
3507 38 : pcreated = pkey;
3508 38 : pdir = pkey; // descend into newly created subdirectory
3509 38 : } else {
3510 : /* create new key with data */
3511 112 : KEY* pkey = (KEY *) malloc_key(pheader, sizeof(KEY), "db_create_key_data");
3512 :
3513 112 : if (pkey == NULL) {
3514 0 : if (pkeylist->num_keys > 0)
3515 0 : pkeylist->num_keys--;
3516 :
3517 0 : 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 0 : return DB_FULL;
3519 : }
3520 :
3521 : /* append key to key list */
3522 112 : if (pprev)
3523 89 : pprev->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
3524 : else
3525 23 : pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
3526 :
3527 112 : pkey->type = type;
3528 112 : pkey->num_values = 1;
3529 112 : pkey->access_mode = MODE_READ | MODE_WRITE | MODE_DELETE;
3530 112 : mstrlcpy(pkey->name, name, sizeof(pkey->name));
3531 112 : pkey->parent_keylist = (POINTER_T) pkeylist - (POINTER_T) pheader;
3532 :
3533 : /* allocate data */
3534 112 : pkey->item_size = rpc_tid_size(type);
3535 112 : if (pkey->item_size > 0) {
3536 87 : void* data = malloc_data(pheader, pkey->item_size);
3537 87 : if (data == NULL) {
3538 0 : pkey->total_size = 0;
3539 0 : 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 0 : return DB_FULL;
3541 : }
3542 :
3543 87 : pkey->total_size = pkey->item_size;
3544 87 : pkey->data = (POINTER_T)data - (POINTER_T)pheader;
3545 : } else {
3546 : /* first data is empty */
3547 25 : pkey->item_size = 0;
3548 25 : pkey->total_size = 0;
3549 25 : pkey->data = 0;
3550 : }
3551 112 : pcreated = pkey;
3552 : }
3553 : } else {
3554 : /* key found: descend */
3555 :
3556 : /* resolve links */
3557 49 : if (pitem->type == TID_LINK && pkey_name[0]) {
3558 0 : pdir = db_resolve_link_locked(pheader, pitem, &status, msg);
3559 0 : if (!pdir) {
3560 0 : return status;
3561 : }
3562 0 : continue;
3563 : }
3564 :
3565 49 : if (!(*pkey_name == '/')) {
3566 1 : if (pitem->type != type) {
3567 0 : 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 0 : return DB_TYPE_MISMATCH;
3569 : }
3570 : //db_print_pkey(pheader, pitem);
3571 :
3572 1 : if (pnewkey)
3573 1 : *pnewkey = (KEY*)pitem;
3574 :
3575 1 : return DB_KEY_EXIST;
3576 : }
3577 :
3578 48 : if (pitem->type != TID_KEY) {
3579 0 : 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 0 : return DB_TYPE_MISMATCH;
3581 : }
3582 :
3583 48 : pdir = pitem;
3584 : }
3585 198 : } while (*pkey_name == '/');
3586 :
3587 130 : assert(pcreated != NULL);
3588 :
3589 130 : if (pnewkey)
3590 130 : *pnewkey = pcreated;
3591 :
3592 130 : return DB_SUCCESS;
3593 : }
3594 :
3595 : #endif /* LOCAL_ROUTINES */
3596 :
3597 : /********************************************************************/
3598 : /**
3599 : Create a link to a key or set the destination of and existing link.
3600 : @param hDB ODB handle obtained via cm_get_experiment_database().
3601 : @param hKey Key handle to start with, 0 for root
3602 : @param link_name Name of key in the form "/key/key/key"
3603 : @param destination Destination of link in the form "/key/key/key"
3604 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FULL, DB_KEY_EXIST, DB_NO_ACCESS, DB_INVALID_NAME
3605 : */
3606 11 : INT db_create_link(HNDLE hDB, HNDLE hKey, const char *link_name, const char *destination)
3607 : {
3608 : HNDLE hkey;
3609 : int status;
3610 :
3611 11 : if (rpc_is_remote())
3612 0 : return rpc_call(RPC_DB_CREATE_LINK, hDB, hKey, link_name, destination);
3613 :
3614 11 : if (destination == NULL) {
3615 0 : cm_msg(MERROR, "db_create_link", "link destination name is NULL");
3616 0 : return DB_INVALID_NAME;
3617 : }
3618 :
3619 11 : if (destination[0] != '/') {
3620 1 : cm_msg(MERROR, "db_create_link", "link destination name \'%s\' should start with \'/\', relative links are forbidden", destination);
3621 1 : return DB_INVALID_NAME;
3622 : }
3623 :
3624 10 : if (strlen(destination) < 1) {
3625 0 : cm_msg(MERROR, "db_create_link", "link destination name \'%s\' is too short", destination);
3626 0 : return DB_INVALID_NAME;
3627 : }
3628 :
3629 10 : if ((destination[0] == '/') && (destination[1] == 0)) {
3630 0 : cm_msg(MERROR, "db_create_link", "links to \"/\" are forbidden");
3631 0 : return DB_INVALID_NAME;
3632 : }
3633 :
3634 : /* check if destination exists */
3635 10 : status = db_find_key(hDB, hKey, destination, &hkey);
3636 10 : if (status != DB_SUCCESS) {
3637 2 : cm_msg(MERROR, "db_create_link", "Link destination \"%s\" does not exist", destination);
3638 2 : return DB_NO_KEY;
3639 : }
3640 :
3641 : //printf("db_create_link: [%s] hkey %d\n", destination, hkey);
3642 :
3643 : /* check if link already exists */
3644 8 : status = db_find_link(hDB, hKey, link_name, &hkey);
3645 8 : if (status != DB_SUCCESS) {
3646 : // create new link
3647 8 : status = db_set_value(hDB, hKey, link_name, destination, strlen(destination) + 1, 1, TID_LINK);
3648 : } else {
3649 : // check if key is TID_LINK
3650 : KEY key;
3651 0 : db_get_key(hDB, hkey, &key);
3652 0 : if (key.type != TID_LINK) {
3653 0 : cm_msg(MERROR, "db_create_link", "Existing key \"%s\" is not a link", link_name);
3654 0 : return DB_KEY_EXIST;
3655 : }
3656 :
3657 : // modify existing link
3658 0 : status = db_set_link_data(hDB, hkey, destination, strlen(destination)+1, 1, TID_LINK);
3659 : }
3660 :
3661 8 : return status;
3662 : }
3663 :
3664 : /********************************************************************/
3665 : /**
3666 : Delete a subtree, using level information (only called internally by db_delete_key())
3667 : @internal
3668 : @param hDB ODB handle obtained via cm_get_experiment_database().
3669 : @param hKey Key handle to start with, 0 for root
3670 : @param level Recursion level, must be zero when
3671 : @param follow_links Follow links when TRUE called from a user routine
3672 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_OPEN_RECORD
3673 : */
3674 256 : INT db_delete_key1(HNDLE hDB, HNDLE hKey, INT level, BOOL follow_links)
3675 : {
3676 : #ifdef LOCAL_ROUTINES
3677 : {
3678 : DATABASE_HEADER *pheader;
3679 : KEYLIST *pkeylist;
3680 : KEY *pkey, *pnext_key, *pkey_tmp;
3681 : HNDLE hKeyLink;
3682 : BOOL deny_delete;
3683 : INT status;
3684 256 : BOOL locked = FALSE;
3685 :
3686 256 : if (hDB > _database_entries || hDB <= 0) {
3687 0 : cm_msg(MERROR, "db_delete_key1", "invalid database handle");
3688 196 : return DB_INVALID_HANDLE;
3689 : }
3690 :
3691 256 : if (!_database[hDB - 1].attached) {
3692 0 : cm_msg(MERROR, "db_delete_key1", "invalid database handle");
3693 0 : return DB_INVALID_HANDLE;
3694 : }
3695 :
3696 256 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
3697 0 : cm_msg(MERROR, "db_delete_key1", "invalid key handle");
3698 0 : return DB_INVALID_HANDLE;
3699 : }
3700 :
3701 : /* lock database at the top level */
3702 256 : if (level == 0) {
3703 13 : db_lock_database(hDB);
3704 13 : locked = TRUE;
3705 : }
3706 :
3707 256 : pheader = _database[hDB - 1].database_header;
3708 :
3709 : /* check if hKey argument is correct */
3710 256 : if (!db_validate_hkey(pheader, hKey)) {
3711 0 : if (locked)
3712 0 : db_unlock_database(hDB);
3713 0 : return DB_INVALID_HANDLE;
3714 : }
3715 :
3716 256 : pkey = (KEY *) ((char *) pheader + hKey);
3717 :
3718 : /* check if someone has opened key or parent */
3719 256 : if (level == 0)
3720 : do {
3721 : #ifdef CHECK_OPEN_RECORD
3722 45 : if (pkey->notify_count) {
3723 0 : if (locked)
3724 0 : db_unlock_database(hDB);
3725 0 : return DB_OPEN_RECORD;
3726 : }
3727 : #endif
3728 45 : if (pkey->parent_keylist == 0)
3729 13 : break;
3730 :
3731 32 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
3732 : // FIXME: validate pkeylist->parent
3733 32 : pkey = (KEY *) ((char *) pheader + pkeylist->parent);
3734 : } while (TRUE);
3735 :
3736 256 : pkey = (KEY *) ((char *) pheader + hKey);
3737 256 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
3738 :
3739 256 : deny_delete = FALSE;
3740 :
3741 : /* first recures subtree for keys */
3742 256 : if (pkey->type == TID_KEY && pkeylist->first_key) {
3743 110 : pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
3744 :
3745 : do {
3746 139 : pnext_key = (KEY *) (POINTER_T) pkey->next_key; // FIXME: what is this casting of hKey to pointer?
3747 :
3748 139 : status = db_delete_key1(hDB, (POINTER_T) pkey - (POINTER_T) pheader, level + 1, follow_links);
3749 :
3750 139 : if (status == DB_NO_ACCESS)
3751 0 : deny_delete = TRUE;
3752 :
3753 139 : if (pnext_key) {
3754 : // FIXME: validate pnext_key
3755 29 : pkey = (KEY *) ((char *) pheader + (POINTER_T) pnext_key);
3756 : }
3757 139 : } while (pnext_key);
3758 : }
3759 :
3760 : /* follow links if requested */
3761 256 : if (pkey->type == TID_LINK && follow_links) {
3762 103 : status = db_find_key1(hDB, 0, (char *) pheader + pkey->data, &hKeyLink);
3763 103 : if (status == DB_SUCCESS && follow_links < 100)
3764 102 : db_delete_key1(hDB, hKeyLink, level + 1, follow_links + 1);
3765 :
3766 103 : if (follow_links == 100)
3767 1 : cm_msg(MERROR, "db_delete_key1", "try to delete cyclic link");
3768 : }
3769 :
3770 : /* check if hKey argument is correct */
3771 256 : if (!db_validate_hkey(pheader, hKey)) {
3772 0 : if (locked)
3773 0 : db_unlock_database(hDB);
3774 0 : return DB_INVALID_HANDLE;
3775 : }
3776 :
3777 256 : pkey = (KEY *) ((char *) pheader + hKey);
3778 :
3779 : /* return if key was already deleted by cyclic link */
3780 256 : if (pkey->parent_keylist == 0) {
3781 196 : if (locked)
3782 0 : db_unlock_database(hDB);
3783 196 : return DB_SUCCESS;
3784 : }
3785 :
3786 : /* now delete key */
3787 60 : if (hKey != pheader->root_key) {
3788 60 : if (!(pkey->access_mode & MODE_DELETE) || deny_delete) {
3789 0 : if (locked)
3790 0 : db_unlock_database(hDB);
3791 0 : return DB_NO_ACCESS;
3792 : }
3793 : #ifdef CHECK_OPEN_RECORD
3794 60 : if (pkey->notify_count) {
3795 0 : if (locked)
3796 0 : db_unlock_database(hDB);
3797 0 : return DB_OPEN_RECORD;
3798 : }
3799 : #endif
3800 60 : db_allow_write_locked(&_database[hDB - 1], "db_delete_key1");
3801 :
3802 : /* delete key data */
3803 60 : if (pkey->type == TID_KEY)
3804 17 : free_key(pheader, (char *) pheader + pkey->data, pkey->total_size);
3805 : else
3806 43 : free_data(pheader, (char *) pheader + pkey->data, pkey->total_size, "db_delete_key1");
3807 :
3808 : /* unlink key from list */
3809 60 : pnext_key = (KEY *) (POINTER_T) pkey->next_key;
3810 60 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
3811 :
3812 60 : if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
3813 : /* key is first in list */
3814 49 : pkeylist->first_key = (POINTER_T) pnext_key;
3815 : } else {
3816 : /* find predecessor */
3817 11 : pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
3818 38 : while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey)
3819 27 : pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
3820 11 : pkey_tmp->next_key = (POINTER_T) pnext_key;
3821 : }
3822 :
3823 : /* delete key */
3824 60 : free_key(pheader, pkey, sizeof(KEY));
3825 60 : pkeylist->num_keys--;
3826 : }
3827 :
3828 60 : if (locked)
3829 13 : db_unlock_database(hDB);
3830 : }
3831 : #endif /* LOCAL_ROUTINES */
3832 :
3833 60 : return DB_SUCCESS;
3834 : }
3835 :
3836 : /********************************************************************/
3837 : /**
3838 : Delete a subtree in a database starting from a key (including this key).
3839 : \code
3840 : ...
3841 : status = db_find_link(hDB, 0, str, &hkey);
3842 : if (status != DB_SUCCESS)
3843 : {
3844 : cm_msg(MINFO,"my_delete"," "Cannot find key %s", str);
3845 : return;
3846 : }
3847 :
3848 : status = db_delete_key(hDB, hkey, FALSE);
3849 : if (status != DB_SUCCESS)
3850 : {
3851 : cm_msg(MERROR,"my_delete"," "Cannot delete key %s", str);
3852 : return;
3853 : }
3854 : ...
3855 : \endcode
3856 : @param hDB ODB handle obtained via cm_get_experiment_database().
3857 : @param hKey for key where search starts, zero for root.
3858 : @param follow_links Follow links when TRUE.
3859 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_OPEN_RECORD
3860 : */
3861 13 : INT db_delete_key(HNDLE hDB, HNDLE hKey, BOOL follow_links)
3862 : {
3863 13 : if (rpc_is_remote())
3864 0 : return rpc_call(RPC_DB_DELETE_KEY, hDB, hKey, follow_links);
3865 :
3866 13 : return db_delete_key1(hDB, hKey, 0, follow_links);
3867 : }
3868 :
3869 : #ifdef LOCAL_ROUTINES
3870 377 : static 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 377 : if (pkey == NULL) {
3873 7 : pkey = db_get_pkey(pheader, pheader->root_key, pstatus, "db_find_key", msg);
3874 7 : if (!pkey) {
3875 0 : return NULL;
3876 : }
3877 : }
3878 :
3879 377 : HNDLE hKey = db_pkey_to_hkey(pheader, pkey);
3880 :
3881 377 : if (pkey->type != TID_KEY) {
3882 0 : DWORD tid = pkey->type;
3883 0 : std::string path = db_get_path_locked(pheader, hKey);
3884 0 : 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 0 : if (pstatus)
3886 0 : *pstatus = DB_NO_KEY;
3887 0 : return NULL;
3888 0 : }
3889 :
3890 377 : if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
3891 0 : if (!(pkey->access_mode & MODE_READ)) {
3892 0 : if (pstatus)
3893 0 : *pstatus = DB_NO_ACCESS;
3894 0 : return NULL;
3895 : }
3896 0 : return pkey;
3897 : }
3898 :
3899 377 : const KEYLIST *pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
3900 377 : if (!pkeylist) {
3901 0 : if (pstatus)
3902 0 : *pstatus = DB_CORRUPTED;
3903 0 : return NULL;
3904 : }
3905 :
3906 377 : HNDLE last_good_hkey = hKey;
3907 :
3908 377 : const char *pkey_name = key_name;
3909 : do {
3910 774 : assert(pkeylist!=NULL); // should never happen!
3911 :
3912 : char str[MAX_ODB_PATH];
3913 :
3914 : /* extract single subkey from key_name */
3915 774 : pkey_name = extract_key(pkey_name, str, sizeof(str));
3916 :
3917 : /* strip trailing '[n]' */
3918 774 : if (strchr(str, '[') && str[strlen(str) - 1] == ']')
3919 0 : *strchr(str, '[') = 0;
3920 :
3921 : /* check if parent or current directory */
3922 774 : if (strcmp(str, "..") == 0) {
3923 0 : if (pkey->parent_keylist) {
3924 0 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
3925 : // FIXME: validate pkeylist->parent
3926 0 : pkey = (KEY *) ((char *) pheader + pkeylist->parent);
3927 : }
3928 0 : continue;
3929 : }
3930 774 : if (strcmp(str, ".") == 0)
3931 0 : continue;
3932 :
3933 774 : last_good_hkey = hKey;
3934 :
3935 774 : hKey = pkeylist->first_key;
3936 :
3937 774 : if (hKey == 0) {
3938 : // empty subdirectory
3939 12 : if (pstatus)
3940 12 : *pstatus = DB_NO_KEY;
3941 118 : return NULL;
3942 : }
3943 :
3944 : int i;
3945 2421 : for (i = 0; i < pkeylist->num_keys; i++) {
3946 2421 : pkey = db_get_pkey(pheader, hKey, pstatus, "db_find_key", msg);
3947 :
3948 2421 : if (!pkey) {
3949 0 : std::string path = db_get_path_locked(pheader, last_good_hkey);
3950 0 : 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 0 : return NULL;
3952 0 : }
3953 :
3954 2421 : if (!db_validate_key_offset(pheader, pkey->next_key)) {
3955 0 : std::string path = db_get_path_locked(pheader, hKey);
3956 0 : 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 0 : if (pstatus)
3958 0 : *pstatus = DB_CORRUPTED;
3959 0 : return NULL;
3960 0 : }
3961 :
3962 2421 : if (equal_ustring(str, pkey->name))
3963 658 : break;
3964 :
3965 1763 : if (pkey->next_key == 0) {
3966 104 : if (pstatus)
3967 104 : *pstatus = DB_NO_KEY;
3968 104 : return NULL;
3969 : }
3970 :
3971 1659 : hKey = pkey->next_key;
3972 : }
3973 :
3974 658 : if (i == pkeylist->num_keys) {
3975 0 : if (pstatus)
3976 0 : *pstatus = DB_NO_KEY;
3977 0 : return NULL;
3978 : }
3979 :
3980 : /* resolve links */
3981 658 : if (pkey->type == TID_LINK) {
3982 : /* copy destination, strip '/' */
3983 2 : mstrlcpy(str, (char *) pheader + pkey->data, sizeof(str));
3984 2 : if (str[strlen(str) - 1] == '/')
3985 0 : str[strlen(str) - 1] = 0;
3986 :
3987 : /* if link is pointer to array index, return link instead of destination */
3988 2 : if (str[strlen(str) - 1] == ']')
3989 0 : break;
3990 :
3991 : /* append rest of key name if existing */
3992 2 : if (pkey_name[0]) {
3993 0 : mstrlcat(str, pkey_name, sizeof(str));
3994 0 : 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 2 : int status = 0;
3998 2 : const KEY *plink = db_find_pkey_locked(pheader, NULL, str, &status, msg);
3999 2 : if (pstatus) {
4000 2 : if (status == DB_NO_KEY)
4001 1 : *pstatus = DB_INVALID_LINK;
4002 : else
4003 1 : *pstatus = status;
4004 : }
4005 2 : return plink;
4006 : }
4007 : }
4008 :
4009 : /* key found: check if last in chain */
4010 656 : if (*pkey_name == '/') {
4011 397 : if (pkey->type != TID_KEY) {
4012 0 : if (pstatus)
4013 0 : *pstatus = DB_NO_KEY;
4014 0 : return NULL;
4015 : }
4016 : }
4017 :
4018 656 : if (pkey->type == TID_KEY) {
4019 : /* descend one level */
4020 438 : pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_find_key", msg);
4021 :
4022 438 : if (!pkeylist) {
4023 0 : if (pstatus)
4024 0 : *pstatus = DB_CORRUPTED;
4025 0 : return NULL;
4026 : }
4027 : } else {
4028 218 : pkeylist = NULL;
4029 : }
4030 :
4031 656 : } while (*pkey_name == '/' && *(pkey_name + 1));
4032 :
4033 259 : return pkey;
4034 : }
4035 :
4036 219 : static 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 219 : const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_find_key", msg);
4040 219 : if (!pkey) {
4041 0 : if (subhKey)
4042 0 : *subhKey = 0;
4043 0 : return status;
4044 : }
4045 :
4046 219 : const KEY* plink = db_find_pkey_locked(pheader, pkey, key_name, &status, msg);
4047 :
4048 219 : if (!plink) {
4049 28 : *subhKey = 0;
4050 28 : return status;
4051 : }
4052 :
4053 191 : *subhKey = db_pkey_to_hkey(pheader, plink);
4054 :
4055 191 : return DB_SUCCESS;
4056 : }
4057 : #endif /* LOCAL_ROUTINES */
4058 :
4059 : /********************************************************************/
4060 : /**
4061 : Returns key handle for a key with a specific name.
4062 :
4063 : Keys can be accessed by their name including the directory
4064 : or by a handle. A key handle is an internal offset to the shared memory
4065 : where the ODB lives and allows a much faster access to a key than via its
4066 : name.
4067 :
4068 : The function db_find_key() must be used to convert a key name to a handle.
4069 : Most other database functions use this key handle in various operations.
4070 : \code
4071 : HNDLE hkey, hsubkey;
4072 : // use full name, start from root
4073 : db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
4074 : // start from subdirectory
4075 : db_find_key(hDB, 0, "/Runinfo", &hkey);
4076 : db_find_key(hdb, hkey, "Run number", &hsubkey);
4077 : \endcode
4078 : @param hDB ODB handle obtained via cm_get_experiment_database().
4079 : @param hKey Handle for key where search starts, zero for root.
4080 : @param key_name Name of key to search, can contain directories.
4081 : @param subhKey Returned handle of key, zero if key cannot be found.
4082 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_NO_KEY
4083 : */
4084 219 : INT db_find_key(HNDLE hDB, HNDLE hKey, const char *key_name, HNDLE * subhKey)
4085 : {
4086 219 : if (rpc_is_remote())
4087 0 : 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 219 : *subhKey = 0;
4095 :
4096 219 : if (hDB > _database_entries || hDB <= 0) {
4097 0 : cm_msg(MERROR, "db_find_key", "invalid database handle");
4098 0 : return DB_INVALID_HANDLE;
4099 : }
4100 :
4101 219 : if (!_database[hDB - 1].attached) {
4102 0 : cm_msg(MERROR, "db_find_key", "invalid database handle");
4103 0 : return DB_INVALID_HANDLE;
4104 : }
4105 :
4106 219 : db_err_msg *msg = NULL;
4107 :
4108 219 : db_lock_database(hDB);
4109 :
4110 219 : pheader = _database[hDB - 1].database_header;
4111 :
4112 219 : status = db_find_key_locked(pheader, hKey, key_name, subhKey, &msg);
4113 :
4114 219 : db_unlock_database(hDB);
4115 :
4116 219 : if (msg)
4117 0 : db_flush_msg(&msg);
4118 :
4119 219 : return status;
4120 : }
4121 : #endif /* LOCAL_ROUTINES */
4122 :
4123 : return DB_SUCCESS;
4124 : }
4125 :
4126 : /**dox***************************************************************/
4127 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
4128 :
4129 : /*------------------------------------------------------------------*/
4130 103 : INT 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 103 : if (rpc_is_remote())
4154 0 : return rpc_call(RPC_DB_FIND_KEY, hDB, hKey, key_name, subhKey);
4155 :
4156 : #ifdef LOCAL_ROUTINES
4157 : {
4158 : DATABASE_HEADER *pheader;
4159 : KEYLIST *pkeylist;
4160 : KEY *pkey;
4161 : const char *pkey_name;
4162 : INT i;
4163 :
4164 103 : *subhKey = 0;
4165 :
4166 103 : if (hDB > _database_entries || hDB <= 0) {
4167 0 : cm_msg(MERROR, "db_find_key", "invalid database handle");
4168 0 : return DB_INVALID_HANDLE;
4169 : }
4170 :
4171 103 : if (!_database[hDB - 1].attached) {
4172 0 : cm_msg(MERROR, "db_find_key", "invalid database handle");
4173 0 : return DB_INVALID_HANDLE;
4174 : }
4175 :
4176 103 : pheader = _database[hDB - 1].database_header;
4177 103 : if (!hKey)
4178 103 : hKey = pheader->root_key;
4179 :
4180 : /* check if hKey argument is correct */
4181 103 : if (!db_validate_hkey(pheader, hKey)) {
4182 0 : return DB_INVALID_HANDLE;
4183 : }
4184 :
4185 103 : pkey = (KEY *) ((char *) pheader + hKey);
4186 :
4187 103 : if (pkey->type != TID_KEY) {
4188 0 : cm_msg(MERROR, "db_find_key", "key has no subkeys");
4189 0 : *subhKey = 0;
4190 0 : return DB_NO_KEY;
4191 : }
4192 103 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4193 :
4194 103 : if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
4195 0 : if (!(pkey->access_mode & MODE_READ)) {
4196 0 : *subhKey = 0;
4197 0 : return DB_NO_ACCESS;
4198 : }
4199 :
4200 0 : *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4201 :
4202 0 : return DB_SUCCESS;
4203 : }
4204 :
4205 103 : pkey_name = key_name;
4206 : do {
4207 : char str[MAX_ODB_PATH];
4208 :
4209 : /* extract single subkey from key_name */
4210 206 : pkey_name = extract_key(pkey_name, str, sizeof(str));
4211 :
4212 : /* check if parent or current directory */
4213 206 : if (strcmp(str, "..") == 0) {
4214 0 : if (pkey->parent_keylist) {
4215 0 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4216 : // FIXME: validate pkeylist->parent
4217 0 : pkey = (KEY *) ((char *) pheader + pkeylist->parent);
4218 : }
4219 0 : continue;
4220 : }
4221 206 : if (strcmp(str, ".") == 0)
4222 0 : continue;
4223 :
4224 : /* check if key is in keylist */
4225 : // FIXME: validate pkeylist->first_key
4226 206 : pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
4227 :
4228 1030 : for (i = 0; i < pkeylist->num_keys; i++) {
4229 1030 : if (equal_ustring(str, pkey->name))
4230 206 : break;
4231 :
4232 : // FIXME: validate pkey->next_key
4233 824 : pkey = (KEY *) ((char *) pheader + pkey->next_key);
4234 : }
4235 :
4236 206 : if (i == pkeylist->num_keys) {
4237 0 : *subhKey = 0;
4238 1 : return DB_NO_KEY;
4239 : }
4240 :
4241 : /* resolve links */
4242 206 : if (pkey->type == TID_LINK) {
4243 : /* copy destination, strip '/' */
4244 1 : strcpy(str, (char *) pheader + pkey->data);
4245 1 : if (str[strlen(str) - 1] == '/')
4246 0 : str[strlen(str) - 1] = 0;
4247 :
4248 : /* append rest of key name if existing */
4249 1 : if (pkey_name[0]) {
4250 0 : strcat(str, pkey_name);
4251 0 : return db_find_key1(hDB, 0, str, subhKey);
4252 : } else {
4253 : /* if last key in chain is a link, return its destination */
4254 1 : return db_find_link1(hDB, 0, str, subhKey);
4255 : }
4256 : }
4257 :
4258 : /* key found: check if last in chain */
4259 205 : if (*pkey_name == '/') {
4260 103 : if (pkey->type != TID_KEY) {
4261 0 : *subhKey = 0;
4262 0 : return DB_NO_KEY;
4263 : }
4264 : }
4265 :
4266 : /* descend one level */
4267 205 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4268 :
4269 205 : } while (*pkey_name == '/' && *(pkey_name + 1));
4270 :
4271 102 : *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4272 : }
4273 : #endif /* LOCAL_ROUTINES */
4274 :
4275 102 : return DB_SUCCESS;
4276 : }
4277 :
4278 : /*------------------------------------------------------------------*/
4279 107 : INT 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 107 : if (rpc_is_remote())
4307 0 : return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
4308 :
4309 : #ifdef LOCAL_ROUTINES
4310 : {
4311 : DATABASE_HEADER *pheader;
4312 : KEYLIST *pkeylist;
4313 : KEY *pkey;
4314 : const char *pkey_name;
4315 : INT i;
4316 :
4317 107 : *subhKey = 0;
4318 :
4319 107 : if (hDB > _database_entries || hDB <= 0) {
4320 0 : cm_msg(MERROR, "db_find_link", "Invalid database handle");
4321 0 : return DB_INVALID_HANDLE;
4322 : }
4323 :
4324 107 : if (!_database[hDB - 1].attached) {
4325 0 : cm_msg(MERROR, "db_find_link", "invalid database handle");
4326 0 : return DB_INVALID_HANDLE;
4327 : }
4328 :
4329 107 : db_lock_database(hDB);
4330 :
4331 107 : pheader = _database[hDB - 1].database_header;
4332 107 : if (!hKey)
4333 11 : hKey = pheader->root_key;
4334 :
4335 : /* check if hKey argument is correct */
4336 107 : if (!db_validate_hkey(pheader, hKey)) {
4337 0 : db_unlock_database(hDB);
4338 0 : return DB_INVALID_HANDLE;
4339 : }
4340 :
4341 107 : pkey = (KEY *) ((char *) pheader + hKey);
4342 :
4343 107 : if (pkey->type != TID_KEY) {
4344 0 : db_unlock_database(hDB);
4345 0 : cm_msg(MERROR, "db_find_link", "key has no subkeys");
4346 0 : return DB_NO_KEY;
4347 : }
4348 107 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4349 :
4350 107 : if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
4351 0 : if (!(pkey->access_mode & MODE_READ)) {
4352 0 : *subhKey = 0;
4353 0 : db_unlock_database(hDB);
4354 0 : return DB_NO_ACCESS;
4355 : }
4356 :
4357 0 : *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4358 :
4359 0 : db_unlock_database(hDB);
4360 0 : return DB_SUCCESS;
4361 : }
4362 :
4363 107 : pkey_name = key_name;
4364 : do {
4365 : char str[MAX_ODB_PATH];
4366 :
4367 : /* extract single subkey from key_name */
4368 117 : pkey_name = extract_key(pkey_name, str, sizeof(str));
4369 :
4370 : /* check if parent or current directory */
4371 117 : if (strcmp(str, "..") == 0) {
4372 0 : if (pkey->parent_keylist) {
4373 0 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4374 : // FIXME: validate pkeylist->parent
4375 0 : pkey = (KEY *) ((char *) pheader + pkeylist->parent);
4376 : }
4377 0 : continue;
4378 : }
4379 117 : if (strcmp(str, ".") == 0)
4380 0 : continue;
4381 :
4382 : /* check if key is in keylist */
4383 : // FIXME: validate pkeylist->first_key
4384 117 : pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
4385 :
4386 540 : for (i = 0; i < pkeylist->num_keys; i++) {
4387 481 : if (!db_validate_key_offset(pheader, pkey->next_key)) {
4388 0 : int pkey_next_key = pkey->next_key;
4389 0 : db_unlock_database(hDB);
4390 0 : 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 0 : *subhKey = 0;
4392 59 : return DB_CORRUPTED;
4393 : }
4394 :
4395 481 : if (equal_ustring(str, pkey->name))
4396 58 : break;
4397 :
4398 423 : pkey = (KEY *) ((char *) pheader + pkey->next_key); // FIXME: pkey->next_key could be zero
4399 : }
4400 :
4401 117 : if (i == pkeylist->num_keys) {
4402 59 : *subhKey = 0;
4403 59 : db_unlock_database(hDB);
4404 59 : return DB_NO_KEY;
4405 : }
4406 :
4407 : /* resolve links if not last in chain */
4408 58 : if (pkey->type == TID_LINK && *pkey_name == '/') {
4409 : /* copy destination, strip '/' */
4410 0 : strcpy(str, (char *) pheader + pkey->data);
4411 0 : if (str[strlen(str) - 1] == '/')
4412 0 : str[strlen(str) - 1] = 0;
4413 :
4414 : /* append rest of key name */
4415 0 : strcat(str, pkey_name);
4416 0 : db_unlock_database(hDB);
4417 0 : return db_find_link(hDB, 0, str, subhKey);
4418 : }
4419 :
4420 : /* key found: check if last in chain */
4421 58 : if ((*pkey_name == '/')) {
4422 10 : if (pkey->type != TID_KEY) {
4423 0 : *subhKey = 0;
4424 0 : db_unlock_database(hDB);
4425 0 : return DB_NO_KEY;
4426 : }
4427 : }
4428 :
4429 : /* descend one level */
4430 58 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4431 :
4432 58 : } while (*pkey_name == '/' && *(pkey_name + 1));
4433 :
4434 48 : *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4435 :
4436 48 : db_unlock_database(hDB);
4437 : }
4438 : #endif /* LOCAL_ROUTINES */
4439 :
4440 48 : return DB_SUCCESS;
4441 : }
4442 :
4443 : /*------------------------------------------------------------------*/
4444 1 : INT 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 1 : if (rpc_is_remote())
4468 0 : return rpc_call(RPC_DB_FIND_LINK, hDB, hKey, key_name, subhKey);
4469 :
4470 : #ifdef LOCAL_ROUTINES
4471 : {
4472 : DATABASE_HEADER *pheader;
4473 : KEYLIST *pkeylist;
4474 : KEY *pkey;
4475 : const char *pkey_name;
4476 : INT i;
4477 :
4478 1 : *subhKey = 0;
4479 :
4480 1 : if (hDB > _database_entries || hDB <= 0) {
4481 0 : cm_msg(MERROR, "db_find_link", "Invalid database handle");
4482 0 : return DB_INVALID_HANDLE;
4483 : }
4484 :
4485 1 : if (!_database[hDB - 1].attached) {
4486 0 : cm_msg(MERROR, "db_find_link", "invalid database handle");
4487 0 : return DB_INVALID_HANDLE;
4488 : }
4489 :
4490 1 : pheader = _database[hDB - 1].database_header;
4491 1 : if (!hKey)
4492 1 : hKey = pheader->root_key;
4493 :
4494 : /* check if hKey argument is correct */
4495 1 : if (!db_validate_hkey(pheader, hKey)) {
4496 0 : return DB_INVALID_HANDLE;
4497 : }
4498 :
4499 1 : pkey = (KEY *) ((char *) pheader + hKey);
4500 :
4501 1 : if (pkey->type != TID_KEY) {
4502 0 : cm_msg(MERROR, "db_find_link", "key has no subkeys");
4503 0 : return DB_NO_KEY;
4504 : }
4505 1 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4506 :
4507 1 : if (key_name[0] == 0 || strcmp(key_name, "/") == 0) {
4508 0 : if (!(pkey->access_mode & MODE_READ)) {
4509 0 : *subhKey = 0;
4510 0 : return DB_NO_ACCESS;
4511 : }
4512 :
4513 0 : *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4514 :
4515 0 : return DB_SUCCESS;
4516 : }
4517 :
4518 1 : pkey_name = key_name;
4519 : do {
4520 : char str[MAX_ODB_PATH];
4521 : /* extract single subkey from key_name */
4522 2 : pkey_name = extract_key(pkey_name, str, sizeof(str));
4523 :
4524 : /* check if parent or current directory */
4525 2 : if (strcmp(str, "..") == 0) {
4526 0 : if (pkey->parent_keylist) {
4527 0 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4528 : // FIXME: validate pkeylist->parent
4529 0 : pkey = (KEY *) ((char *) pheader + pkeylist->parent);
4530 : }
4531 0 : continue;
4532 : }
4533 2 : if (strcmp(str, ".") == 0)
4534 0 : continue;
4535 :
4536 : /* check if key is in keylist */
4537 : // FIXME: validate pkeylist->first_key
4538 2 : pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
4539 :
4540 9 : for (i = 0; i < pkeylist->num_keys; i++) {
4541 9 : if (!db_validate_key_offset(pheader, pkey->next_key)) {
4542 0 : 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 0 : *subhKey = 0;
4544 0 : return DB_CORRUPTED;
4545 : }
4546 :
4547 9 : if (equal_ustring(str, pkey->name))
4548 2 : break;
4549 :
4550 7 : pkey = (KEY *) ((char *) pheader + pkey->next_key); // FIXME: pkey->next_key could be zero
4551 : }
4552 :
4553 2 : if (i == pkeylist->num_keys) {
4554 0 : *subhKey = 0;
4555 0 : return DB_NO_KEY;
4556 : }
4557 :
4558 : /* resolve links if not last in chain */
4559 2 : if (pkey->type == TID_LINK && *pkey_name == '/') {
4560 : /* copy destination, strip '/' */
4561 0 : strcpy(str, (char *) pheader + pkey->data);
4562 0 : if (str[strlen(str) - 1] == '/')
4563 0 : str[strlen(str) - 1] = 0;
4564 :
4565 : /* append rest of key name */
4566 0 : strcat(str, pkey_name);
4567 0 : return db_find_link1(hDB, 0, str, subhKey);
4568 : }
4569 :
4570 : /* key found: check if last in chain */
4571 2 : if ((*pkey_name == '/')) {
4572 1 : if (pkey->type != TID_KEY) {
4573 0 : *subhKey = 0;
4574 0 : return DB_NO_KEY;
4575 : }
4576 : }
4577 :
4578 : /* descend one level */
4579 2 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
4580 :
4581 2 : } while (*pkey_name == '/' && *(pkey_name + 1));
4582 :
4583 1 : *subhKey = (POINTER_T) pkey - (POINTER_T) pheader;
4584 : }
4585 : #endif /* LOCAL_ROUTINES */
4586 :
4587 1 : return DB_SUCCESS;
4588 : }
4589 :
4590 : /*------------------------------------------------------------------*/
4591 0 : INT db_find_keys(HNDLE hDB, HNDLE hKeyRoot, char* odbpath, std::vector<HNDLE> &hKeyVector)
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 0 : HNDLE hKey, hSubKey=0;
4616 : KEY key;
4617 : INT status;
4618 0 : char *pattern = NULL;
4619 0 : char *subkeypath = NULL;
4620 0 : char *parentkeypath = NULL;
4621 : char localpath[256];
4622 :
4623 : //make work on a local copy od odbpath to allow recursion
4624 0 : strcpy(localpath, odbpath);
4625 0 : parentkeypath = localpath;
4626 :
4627 0 : char *wildcard = strpbrk(localpath, "*?");
4628 0 : if(wildcard){
4629 : //wildcard found
4630 0 : subkeypath = strchr(wildcard, '/');
4631 0 : if(subkeypath){
4632 : //truncate the string at slash
4633 0 : *subkeypath = 0;
4634 0 : subkeypath++;
4635 : }
4636 0 : parentkeypath = strrchr(localpath, '/');
4637 0 : if(parentkeypath){
4638 : //truncate there too
4639 0 : *parentkeypath = 0;
4640 0 : pattern = parentkeypath+1;
4641 0 : if((parentkeypath-1) == localpath){
4642 : //path starts with '/', no parent path
4643 0 : parentkeypath = NULL;
4644 : } else {
4645 0 : parentkeypath = localpath;
4646 : }
4647 : } else {
4648 : //wildcard at top level, start with pattern
4649 0 : pattern = localpath;
4650 0 : parentkeypath = NULL;
4651 : }
4652 : }
4653 :
4654 : //if available search for parent key path
4655 0 : if(parentkeypath){
4656 0 : status = db_find_key(hDB, hKeyRoot, parentkeypath, &hKey);
4657 0 : if (status != DB_SUCCESS)
4658 0 : return status;
4659 : } else {
4660 0 : hKey = hKeyRoot;
4661 : }
4662 :
4663 : //if a pattern is found
4664 0 : if(pattern){
4665 : //try match all subkeys
4666 0 : status = DB_NO_KEY;
4667 0 : for (int i=0 ; ; i++)
4668 : {
4669 0 : db_enum_key(hDB, hKey, i, &hSubKey);
4670 0 : if (!hSubKey)
4671 0 : break; // end of list reached
4672 0 : db_get_key(hDB, hSubKey, &key);
4673 :
4674 0 : if(strmatch(pattern, key.name)){
4675 : //match
4676 0 : if(!subkeypath){
4677 : //found
4678 0 : hKeyVector.push_back(hSubKey);
4679 :
4680 0 : } else if (key.type == TID_KEY){
4681 : //recurse with hSubKey as root key and subkeypath as path
4682 0 : int subkeystatus = db_find_keys(hDB, hSubKey, subkeypath, hKeyVector);
4683 0 : if (subkeystatus != DB_NO_KEY)
4684 0 : status = subkeystatus;
4685 :
4686 0 : if (status != DB_SUCCESS && status != DB_NO_KEY)
4687 0 : break;
4688 : }
4689 : }
4690 0 : }
4691 :
4692 0 : return status;
4693 : } else {
4694 : //no pattern: hKey matches!
4695 0 : db_get_key(hDB, hKey, &key);
4696 0 : hKeyVector.push_back(hKey);
4697 :
4698 0 : return DB_SUCCESS;
4699 : }
4700 :
4701 : }
4702 :
4703 : /*------------------------------------------------------------------*/
4704 0 : INT db_get_parent(HNDLE hDB, HNDLE hKey, HNDLE * parenthKey)
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 0 : if (rpc_is_remote())
4724 0 : return rpc_call(RPC_DB_GET_PARENT, hDB, hKey, parenthKey);
4725 :
4726 : #ifdef LOCAL_ROUTINES
4727 : {
4728 :
4729 : DATABASE_HEADER *pheader;
4730 : const KEY *pkey;
4731 :
4732 0 : if (hDB > _database_entries || hDB <= 0) {
4733 0 : cm_msg(MERROR, "db_get_parent", "invalid database handle");
4734 0 : return DB_INVALID_HANDLE;
4735 : }
4736 :
4737 0 : if (!_database[hDB - 1].attached) {
4738 0 : cm_msg(MERROR, "db_get_parent", "invalid database handle");
4739 0 : return DB_INVALID_HANDLE;
4740 : }
4741 :
4742 0 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
4743 0 : cm_msg(MERROR, "db_get_parent", "invalid key handle");
4744 0 : return DB_INVALID_HANDLE;
4745 : }
4746 :
4747 0 : db_lock_database(hDB);
4748 :
4749 0 : pheader = _database[hDB - 1].database_header;
4750 0 : pkey = (const KEY *) ((char *) pheader + hKey);
4751 :
4752 : /* find parent key */
4753 0 : const KEYLIST *pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4754 :
4755 0 : if (!db_validate_hkey(pheader, pkeylist->parent)) {
4756 0 : db_unlock_database(hDB);
4757 0 : return DB_INVALID_HANDLE;
4758 : }
4759 :
4760 0 : pkey = (const KEY *) ((char *) pheader + pkeylist->parent);
4761 :
4762 0 : *parenthKey = (POINTER_T) pkey - (POINTER_T) pheader;
4763 :
4764 0 : db_unlock_database(hDB);
4765 : }
4766 : #endif
4767 :
4768 0 : return DB_SUCCESS;
4769 : }
4770 :
4771 : /*------------------------------------------------------------------*/
4772 0 : INT db_scan_tree(HNDLE hDB, HNDLE hKey, INT level, INT(*callback) (HNDLE, HNDLE, KEY *, INT, void *), void *info)
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 :
4804 0 : status = db_get_link(hDB, hKey, &key);
4805 0 : if (status != DB_SUCCESS)
4806 0 : return status;
4807 :
4808 0 : status = callback(hDB, hKey, &key, level, info);
4809 0 : if (status == 0)
4810 0 : return status;
4811 :
4812 0 : if (key.type == TID_KEY) {
4813 0 : for (i = 0;; i++) {
4814 0 : db_enum_link(hDB, hKey, i, &hSubkey);
4815 :
4816 0 : if (!hSubkey)
4817 0 : break;
4818 :
4819 0 : db_scan_tree(hDB, hSubkey, level + 1, callback, info);
4820 : }
4821 : }
4822 :
4823 0 : return DB_SUCCESS;
4824 : }
4825 :
4826 : #ifdef LOCAL_ROUTINES
4827 :
4828 67 : int 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 67 : assert(pkey != NULL);
4831 67 : assert(level < MAX_ODB_PATH);
4832 :
4833 67 : int status = callback(pheader, pkey, level, info, msg);
4834 67 : if (status == 0)
4835 0 : return status;
4836 :
4837 67 : if (pkey->type == TID_KEY) {
4838 21 : const KEY* subkey = db_enum_first_locked(pheader, pkey, msg);
4839 85 : while (subkey != NULL) {
4840 64 : db_scan_tree_locked(pheader, subkey, level + 1, callback, info, msg);
4841 64 : subkey = db_enum_next_locked(pheader, pkey, subkey, msg);
4842 : }
4843 : }
4844 :
4845 67 : return DB_SUCCESS;
4846 : }
4847 :
4848 : #endif // LOCAL_ROUTINES
4849 :
4850 : /*------------------------------------------------------------------*/
4851 8 : INT db_scan_tree_link(HNDLE hDB, HNDLE hKey, INT level, void (*callback) (HNDLE, HNDLE, KEY *, INT, void *), void *info)
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 :
4884 8 : status = db_get_key(hDB, hKey, &key);
4885 8 : if (status != DB_SUCCESS)
4886 0 : return status;
4887 :
4888 8 : callback(hDB, hKey, &key, level, info);
4889 :
4890 8 : if (key.type == TID_KEY) {
4891 8 : for (i = 0;; i++) {
4892 8 : db_enum_link(hDB, hKey, i, &hSubkey);
4893 :
4894 8 : if (!hSubkey)
4895 4 : break;
4896 :
4897 4 : db_scan_tree_link(hDB, hSubkey, level + 1, callback, info);
4898 : }
4899 : }
4900 :
4901 8 : return DB_SUCCESS;
4902 : }
4903 :
4904 : #ifdef LOCAL_ROUTINES
4905 : /*------------------------------------------------------------------*/
4906 4 : static std::string db_get_path_locked(const DATABASE_HEADER* pheader, const KEY* pkey)
4907 : {
4908 8 : 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 14 : if (pkey->type <= 0 || pkey->type >= TID_LAST) {
4914 : char buf[256];
4915 0 : sprintf(buf, "(INVALID_KEY_TYPE_%d)", pkey->type);
4916 0 : std::string xpath;
4917 0 : xpath += buf;
4918 0 : if (path.length() > 0) {
4919 0 : xpath += "/";
4920 0 : xpath += path;
4921 : }
4922 0 : return xpath;
4923 0 : }
4924 :
4925 : /* add key name in front of path */
4926 14 : std::string str = path;
4927 14 : path = "";
4928 14 : if (pkey->name[0] == 0) {
4929 0 : path += "(EMPTY_NAME)";
4930 : } else {
4931 14 : path += pkey->name;
4932 : }
4933 14 : if (str.length() > 0)
4934 10 : path += "/";
4935 14 : path += str;
4936 :
4937 14 : if (!pkey->parent_keylist) {
4938 0 : return path;
4939 : }
4940 :
4941 14 : if (!db_validate_data_offset(pheader, pkey->parent_keylist)) {
4942 0 : return "(INVALID_PARENT_KEYLIST)/" + path;
4943 : }
4944 :
4945 : /* find parent key */
4946 14 : const KEYLIST* pkeylist = (const KEYLIST *) ((char *) pheader + pkey->parent_keylist);
4947 :
4948 14 : if (pkeylist->parent == pheader->root_key) {
4949 4 : return "/" + path;
4950 : }
4951 :
4952 10 : if (pkeylist->parent == 0) {
4953 0 : return "(NULL_PARENT)/" + path;
4954 : }
4955 :
4956 10 : if (!db_validate_key_offset(pheader, pkeylist->parent)) {
4957 0 : return "(INVALID_PARENT)/" + path;
4958 : }
4959 :
4960 10 : pkey = (const KEY *) ((char *) pheader + pkeylist->parent);
4961 24 : };
4962 :
4963 : /* NOT REACHED */
4964 4 : }
4965 :
4966 : /*------------------------------------------------------------------*/
4967 4 : static 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 4 : if (!hKey)
4972 0 : hKey = pheader->root_key;
4973 :
4974 4 : if (hKey == pheader->root_key) {
4975 0 : return "/";
4976 : }
4977 :
4978 : /* check if hKey argument is correct */
4979 4 : if (hKey == 0) {
4980 0 : return "(ZERO_HKEY)";
4981 : }
4982 :
4983 : /* check if hKey argument is correct */
4984 4 : if (!db_validate_key_offset(pheader, hKey)) {
4985 0 : return "(INVALID_HKEY)";
4986 : }
4987 :
4988 4 : const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
4989 :
4990 4 : return db_get_path_locked(pheader, pkey);
4991 : }
4992 : #endif /* LOCAL_ROUTINES */
4993 :
4994 : /*------------------------------------------------------------------*/
4995 0 : INT 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 0 : if (rpc_is_remote())
5020 0 : return rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, buf_size);
5021 :
5022 : #ifdef LOCAL_ROUTINES
5023 : {
5024 : DATABASE_HEADER *pheader;
5025 :
5026 0 : if (hDB > _database_entries || hDB <= 0) {
5027 0 : cm_msg(MERROR, "db_get_path", "invalid database handle");
5028 0 : return DB_INVALID_HANDLE;
5029 : }
5030 :
5031 0 : if (!_database[hDB - 1].attached) {
5032 0 : cm_msg(MERROR, "db_get_path", "invalid database handle");
5033 0 : return DB_INVALID_HANDLE;
5034 : }
5035 :
5036 0 : db_lock_database(hDB);
5037 :
5038 0 : pheader = _database[hDB - 1].database_header;
5039 :
5040 0 : std::string xpath = db_get_path_locked(pheader, hKey);
5041 :
5042 0 : db_unlock_database(hDB);
5043 :
5044 0 : mstrlcpy(path, xpath.c_str(), buf_size);
5045 :
5046 0 : return DB_SUCCESS;
5047 0 : }
5048 : #endif /* LOCAL_ROUTINES */
5049 :
5050 : return DB_SUCCESS;
5051 : }
5052 :
5053 : /*------------------------------------------------------------------*/
5054 4 : std::string db_get_path(HNDLE hDB, HNDLE hKey)
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 4 : if (rpc_is_remote()) {
5070 : char path[MAX_ODB_PATH];
5071 0 : int status = rpc_call(RPC_DB_GET_PATH, hDB, hKey, path, sizeof(path));
5072 0 : if (status != DB_SUCCESS) {
5073 0 : sprintf(path, "(RPC_DB_GET_PATH status %d)", status);
5074 : }
5075 0 : return path;
5076 : }
5077 :
5078 : #ifdef LOCAL_ROUTINES
5079 : {
5080 : DATABASE_HEADER *pheader;
5081 :
5082 4 : if (hDB > _database_entries || hDB <= 0) {
5083 0 : cm_msg(MERROR, "db_get_path", "invalid database handle");
5084 0 : return "(DB_INVALID_HANDLE)";
5085 : }
5086 :
5087 4 : if (!_database[hDB - 1].attached) {
5088 0 : cm_msg(MERROR, "db_get_path", "invalid database handle");
5089 0 : return "(DB_INVALID_HANDLE)";
5090 : }
5091 :
5092 4 : db_lock_database(hDB);
5093 :
5094 4 : pheader = _database[hDB - 1].database_header;
5095 :
5096 4 : std::string xpath = db_get_path_locked(pheader, hKey);
5097 :
5098 4 : db_unlock_database(hDB);
5099 :
5100 4 : return xpath;
5101 4 : }
5102 : #endif /* LOCAL_ROUTINES */
5103 :
5104 : return "(no LOCAL_ROUTINES)";
5105 : }
5106 :
5107 : #ifdef LOCAL_ROUTINES
5108 : /*------------------------------------------------------------------*/
5109 0 : static int db_find_open_records(HNDLE hDB, HNDLE hKey, KEY * key, INT level, void *xresult)
5110 : {
5111 : /* check if this key has notify count set */
5112 0 : if (key->notify_count) {
5113 :
5114 0 : db_lock_database(hDB);
5115 :
5116 0 : DATABASE_HEADER *pheader = _database[hDB - 1].database_header;
5117 :
5118 0 : std::string path = db_get_path_locked(pheader, hKey);
5119 :
5120 0 : 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 0 : int count = 0;
5125 0 : for (int i = 0; i < pheader->max_client_index; i++) {
5126 0 : DATABASE_CLIENT *pclient = &pheader->client[i];
5127 0 : for (int j = 0; j < pclient->max_index; j++)
5128 0 : if (pclient->open_record[j].handle == hKey) {
5129 0 : count++;
5130 0 : line += " \"";
5131 0 : line += pclient->name;
5132 0 : 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 0 : if (count < 1) {
5138 0 : line += " a deleted client";
5139 : }
5140 :
5141 0 : line += "\n";
5142 :
5143 0 : std::string *result = (std::string*)xresult;
5144 0 : *result += line;
5145 :
5146 0 : db_unlock_database(hDB);
5147 0 : }
5148 0 : return DB_SUCCESS;
5149 : }
5150 :
5151 0 : static int db_fix_open_records(HNDLE hDB, HNDLE hKey, KEY * key, INT level, void *xresult)
5152 : {
5153 0 : std::string *result = (std::string*)xresult;
5154 :
5155 : /* check if this key has notify count set */
5156 0 : if (key->notify_count) {
5157 0 : db_lock_database(hDB);
5158 0 : DATABASE_HEADER *pheader = _database[hDB - 1].database_header;
5159 0 : db_allow_write_locked(&_database[hDB - 1], "db_fix_open_records");
5160 :
5161 : int i;
5162 0 : for (i = 0; i < pheader->max_client_index; i++) {
5163 0 : DATABASE_CLIENT *pclient = &pheader->client[i];
5164 : int j;
5165 0 : for (j = 0; j < pclient->max_index; j++)
5166 0 : if (pclient->open_record[j].handle == hKey)
5167 0 : break;
5168 0 : if (j < pclient->max_index)
5169 0 : break;
5170 : }
5171 0 : if (i == pheader->max_client_index) {
5172 : /* check if hKey argument is correct */
5173 0 : if (!db_validate_hkey(pheader, hKey)) {
5174 0 : db_unlock_database(hDB);
5175 0 : return DB_SUCCESS;
5176 : }
5177 :
5178 : /* reset notify count */
5179 0 : KEY *pkey = (KEY *) ((char *) pheader + hKey);
5180 0 : pkey->notify_count = 0;
5181 :
5182 0 : std::string path = db_get_path_locked(pheader, hKey);
5183 0 : *result += path;
5184 0 : *result += " fixed\n";
5185 0 : }
5186 :
5187 0 : db_unlock_database(hDB);
5188 : }
5189 0 : return DB_SUCCESS;
5190 : }
5191 : #endif /* LOCAL_ROUTINES */
5192 :
5193 0 : INT db_get_open_records(HNDLE hDB, HNDLE hKey, char *str, INT buf_size, BOOL fix)
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 0 : str[0] = 0;
5217 :
5218 0 : if (rpc_is_remote())
5219 0 : return rpc_call(RPC_DB_GET_OPEN_RECORDS, hDB, hKey, str, buf_size);
5220 :
5221 0 : std::string result;
5222 :
5223 : #ifdef LOCAL_ROUTINES
5224 :
5225 0 : if (fix)
5226 0 : db_scan_tree(hDB, hKey, 0, db_fix_open_records, &result); // FIXME: should use db_scan_tree_wlocked()
5227 : else
5228 0 : db_scan_tree(hDB, hKey, 0, db_find_open_records, &result); // FIXME: should use db_scan_tree_locked()
5229 :
5230 : #endif
5231 :
5232 0 : mstrlcpy(str, result.c_str(), buf_size);
5233 :
5234 0 : return DB_SUCCESS;
5235 0 : }
5236 :
5237 : /**dox***************************************************************/
5238 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
5239 :
5240 :
5241 : /********************************************************************/
5242 : /**
5243 : Set value of a single key.
5244 :
5245 : The function sets a single value or a whole array to a ODB key.
5246 : Since the data buffer is of type void, no type checking can be performed by the
5247 : compiler. Therefore the type has to be explicitly supplied, which is checked
5248 : against the type stored in the ODB. key_name can contain the full path of a key
5249 : (like: "/Equipment/Trigger/Settings/Level1") while hkey is zero which refers
5250 : to the root, or hkey can refer to a sub-directory (like /Equipment/Trigger)
5251 : and key_name is interpreted relative to that directory like "Settings/Level1".
5252 : \code
5253 : INT level1;
5254 : db_set_value(hDB, 0, "/Equipment/Trigger/Settings/Level1",
5255 : &level1, sizeof(level1), 1, TID_INT32);
5256 : \endcode
5257 : @param hDB ODB handle obtained via cm_get_experiment_database().
5258 : @param hKeyRoot Handle for key where search starts, zero for root.
5259 : @param key_name Name of key to search, can contain directories.
5260 : @param data Address of data.
5261 : @param data_size Size of data (in bytes).
5262 : @param num_values Number of data elements.
5263 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types)
5264 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH
5265 : */
5266 102 : INT 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 102 : if (rpc_is_remote())
5270 0 : 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 102 : if (num_values == 0)
5277 0 : return DB_INVALID_PARAM;
5278 :
5279 102 : db_lock_database(hDB);
5280 :
5281 102 : DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
5282 :
5283 102 : db_allow_write_locked(&_database[hDB-1], "db_set_value");
5284 :
5285 102 : db_err_msg* msg = NULL;
5286 :
5287 102 : KEY* pkey_root = (KEY*)db_get_pkey(pheader, hKeyRoot, &status, "db_set_value", &msg);
5288 :
5289 102 : if (pkey_root) {
5290 102 : status = db_set_value_wlocked(pheader, hDB, pkey_root, key_name, data, data_size, num_values, type, &msg);
5291 : }
5292 :
5293 102 : db_unlock_database(hDB);
5294 102 : if (msg)
5295 0 : db_flush_msg(&msg);
5296 :
5297 102 : return status;
5298 : }
5299 : #endif /* LOCAL_ROUTINES */
5300 :
5301 : return DB_SUCCESS;
5302 : }
5303 :
5304 : #ifdef LOCAL_ROUTINES
5305 119 : 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)
5306 : {
5307 : INT status;
5308 :
5309 119 : if (num_values == 0)
5310 0 : return DB_INVALID_PARAM;
5311 :
5312 119 : KEY* pkey = (KEY*)db_find_pkey_locked(pheader, pkey_root, key_name, &status, msg);
5313 :
5314 119 : if (!pkey) {
5315 70 : status = db_create_key_wlocked(pheader, pkey_root, key_name, type, &pkey, msg);
5316 70 : if (status != DB_SUCCESS && status != DB_CREATED)
5317 0 : return status;
5318 : }
5319 :
5320 119 : if (data_size == 0) {
5321 0 : db_msg(msg, MERROR, "db_set_value", "zero data size not allowed");
5322 0 : return DB_TYPE_MISMATCH;
5323 : }
5324 :
5325 119 : if (type != TID_STRING && type != TID_LINK && data_size != rpc_tid_size(type) * num_values) {
5326 0 : 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 0 : return DB_TYPE_MISMATCH;
5328 : }
5329 :
5330 119 : status = db_check_set_data_locked(pheader, pkey, data, data_size, num_values, type, "db_set_value", msg);
5331 :
5332 119 : if (status != DB_SUCCESS)
5333 0 : return status;
5334 :
5335 119 : status = db_set_data_wlocked(pheader, pkey, data, data_size, num_values, type, "db_set_value", msg);
5336 :
5337 119 : if (status != DB_SUCCESS)
5338 0 : return status;
5339 :
5340 119 : db_notify_clients_locked(pheader, hDB, db_pkey_to_hkey(pheader, pkey), -1, TRUE, msg);
5341 :
5342 119 : return DB_SUCCESS;
5343 : }
5344 : #endif /* LOCAL_ROUTINES */
5345 :
5346 : /********************************************************************/
5347 : /**
5348 : Set single value of an array.
5349 :
5350 : The function sets a single value of an ODB key which is an array.
5351 : key_name can contain the full path of a key
5352 : (like: "/Equipment/Trigger/Settings/Level1") while hkey is zero which refers
5353 : to the root, or hkey can refer to a sub-directory (like /Equipment/Trigger)
5354 : and key_name is interpreted relative to that directory like "Settings/Level1".
5355 : \code
5356 : INT level1;
5357 : db_set_value_index(hDB, 0, "/Equipment/Trigger/Settings/Level1",
5358 : &level1, sizeof(level1), 15, TID_INT32, FALSE);
5359 : \endcode
5360 : @param hDB ODB handle obtained via cm_get_experiment_database().
5361 : @param hKeyRoot Handle for key where search starts, zero for root.
5362 : @param key_name Name of key to search, can contain directories.
5363 : @param data Address of data.
5364 : @param data_size Size of data (in bytes).
5365 : @param index Array index of value.
5366 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types)
5367 : @param truncate Truncate array to current index if TRUE
5368 : @return \<same as db_set_data_index\>
5369 : */
5370 0 : INT 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 0 : status = db_find_key(hDB, hKeyRoot, key_name, &hkey);
5377 0 : if (!hkey) {
5378 0 : status = db_create_key(hDB, hKeyRoot, key_name, type);
5379 0 : status = db_find_key(hDB, hKeyRoot, key_name, &hkey);
5380 0 : if (status != DB_SUCCESS)
5381 0 : return status;
5382 : } else
5383 0 : if (trunc) {
5384 0 : status = db_set_num_values(hDB, hkey, idx + 1);
5385 0 : if (status != DB_SUCCESS)
5386 0 : return status;
5387 : }
5388 :
5389 0 : return db_set_data_index(hDB, hkey, data, data_size, idx, type);
5390 : }
5391 :
5392 : /********************************************************************/
5393 : /**
5394 : Get value of a single key.
5395 :
5396 : The function returns single values or whole arrays which are contained
5397 : in an ODB key. Since the data buffer is of type void, no type checking can be
5398 : performed by the compiler. Therefore the type has to be explicitly supplied,
5399 : which is checked against the type stored in the ODB. key_name can contain the
5400 : full path of a key (like: "/Equipment/Trigger/Settings/Level1") while hkey is
5401 : zero which refers to the root, or hkey can refer to a sub-directory
5402 : (like: /Equipment/Trigger) and key_name is interpreted relative to that directory
5403 : like "Settings/Level1".
5404 : \code
5405 : INT level1, size;
5406 : size = sizeof(level1);
5407 : db_get_value(hDB, 0, "/Equipment/Trigger/Settings/Level1",
5408 : &level1, &size, TID_INT32, 0);
5409 : \endcode
5410 : @param hDB ODB handle obtained via cm_get_experiment_database().
5411 : @param hKeyRoot Handle for key where search starts, zero for root.
5412 : @param key_name Name of key to search, can contain directories.
5413 : @param data Address of data.
5414 : @param buf_size Maximum buffer size on input, number of written bytes on return.
5415 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types)
5416 : @param create If TRUE, create key if not existing
5417 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH,
5418 : DB_TRUNCATED, DB_NO_KEY
5419 : */
5420 35 : INT db_get_value(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, void *data, INT * buf_size, DWORD type, BOOL create)
5421 : {
5422 35 : if (rpc_is_remote())
5423 0 : 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 35 : if (hDB > _database_entries || hDB <= 0) {
5430 0 : cm_msg(MERROR, "db_get_value", "invalid database handle %d", hDB);
5431 0 : return DB_INVALID_HANDLE;
5432 : }
5433 :
5434 35 : if (!_database[hDB - 1].attached) {
5435 0 : cm_msg(MERROR, "db_get_value", "invalid database handle %d", hDB);
5436 0 : return DB_INVALID_HANDLE;
5437 : }
5438 :
5439 : /* check if key name contains index */
5440 : char keyname[MAX_ODB_PATH];
5441 35 : mstrlcpy(keyname, key_name, sizeof(keyname));
5442 35 : int idx = -1;
5443 35 : if (strchr(keyname, '[') && strchr(keyname, ']')) {
5444 : char* p;
5445 4 : for (p = strchr(keyname, '[') + 1; *p && *p != ']'; p++)
5446 2 : if (!isdigit(*p))
5447 0 : break;
5448 :
5449 2 : if (*p && *p == ']') {
5450 2 : idx = atoi(strchr(keyname, '[') + 1);
5451 2 : *strchr(keyname, '[') = 0;
5452 : }
5453 : }
5454 :
5455 : /* now lock database */
5456 35 : db_lock_database(hDB);
5457 :
5458 35 : DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
5459 35 : db_err_msg* msg = NULL;
5460 :
5461 35 : const KEY* pkey_root = db_get_pkey(pheader, hKeyRoot, &status, "db_get_value", &msg);
5462 :
5463 35 : if (!pkey_root) {
5464 0 : db_unlock_database(hDB);
5465 0 : if (msg)
5466 0 : db_flush_msg(&msg);
5467 0 : return status;
5468 : }
5469 :
5470 35 : const KEY* pkey = db_find_pkey_locked(pheader, pkey_root, keyname, &status, &msg);
5471 :
5472 35 : if (!pkey) {
5473 18 : if (create) {
5474 14 : 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 14 : if (type == TID_STRING || type == TID_LINK)
5480 1 : size = *buf_size;
5481 : else
5482 13 : size = rpc_tid_size(type);
5483 :
5484 14 : status = db_set_value_wlocked(pheader, hDB, (KEY*)pkey_root, keyname, data, *buf_size, *buf_size / size, type, &msg);
5485 14 : db_unlock_database(hDB);
5486 14 : if (msg)
5487 0 : db_flush_msg(&msg);
5488 14 : return status;
5489 : } else {
5490 4 : db_unlock_database(hDB);
5491 4 : if (msg)
5492 0 : db_flush_msg(&msg);
5493 4 : return DB_NO_KEY;
5494 : }
5495 : }
5496 :
5497 17 : status = db_get_data_locked(pheader, pkey, idx, data, buf_size, type, &msg);
5498 :
5499 17 : db_unlock_database(hDB);
5500 17 : if (msg)
5501 0 : db_flush_msg(&msg);
5502 :
5503 17 : return status;
5504 : }
5505 : #endif /* LOCAL_ROUTINES */
5506 :
5507 : return DB_SUCCESS;
5508 : }
5509 :
5510 : #ifdef LOCAL_ROUTINES
5511 60 : 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)
5512 : {
5513 : /* check for correct type */
5514 60 : if (pkey->type != (type)) {
5515 0 : 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 0 : return DB_TYPE_MISMATCH;
5517 : }
5518 :
5519 : /* check for correct type */
5520 60 : if (pkey->type == TID_KEY) {
5521 0 : 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 0 : return DB_TYPE_MISMATCH;
5523 : }
5524 :
5525 : /* check for read access */
5526 60 : if (!(pkey->access_mode & MODE_READ)) {
5527 0 : db_msg(msg, MERROR, "db_get_data_locked", "odb entry \"%s\" has no read access", db_get_path_locked(pheader, pkey).c_str());
5528 0 : return DB_NO_ACCESS;
5529 : }
5530 :
5531 : /* check if buffer is too small */
5532 60 : if ((idx == -1 && pkey->num_values * pkey->item_size > *buf_size) || (idx != -1 && pkey->item_size > *buf_size)) {
5533 0 : memcpy(data, (char *) pheader + pkey->data, *buf_size);
5534 0 : 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 0 : return DB_TRUNCATED;
5536 : }
5537 :
5538 : /* check if index in boundaries */
5539 60 : if (idx != -1 && idx >= pkey->num_values) {
5540 0 : 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 0 : return DB_INVALID_PARAM;
5542 : }
5543 :
5544 : /* copy key data */
5545 60 : if (idx == -1) {
5546 56 : memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
5547 56 : *buf_size = pkey->num_values * pkey->item_size;
5548 : } else {
5549 4 : memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, pkey->item_size);
5550 4 : *buf_size = pkey->item_size;
5551 : }
5552 :
5553 60 : return DB_SUCCESS;
5554 : }
5555 : #endif /* LOCAL_ROUTINES */
5556 :
5557 : /********************************************************************/
5558 : /**
5559 : Enumerate subkeys from a key, follow links.
5560 :
5561 : hkey must correspond to a valid ODB directory. The index is
5562 : usually incremented in a loop until the last key is reached. Information about the
5563 : sub-keys can be obtained with db_get_key(). If a returned key is of type
5564 : TID_KEY, it contains itself sub-keys. To scan a whole ODB sub-tree, the
5565 : function db_scan_tree() can be used.
5566 : \code
5567 : INT i;
5568 : HNDLE hkey, hsubkey;
5569 : KEY key;
5570 : db_find_key(hdb, 0, "/Runinfo", &hkey);
5571 : for (i=0 ; ; i++)
5572 : {
5573 : db_enum_key(hdb, hkey, i, &hsubkey);
5574 : if (!hSubkey)
5575 : break; // end of list reached
5576 : // print key name
5577 : db_get_key(hdb, hkey, &key);
5578 : printf("%s\n", key.name);
5579 : }
5580 : \endcode
5581 : @param hDB ODB handle obtained via cm_get_experiment_database().
5582 : @param hKey Handle for key where search starts, zero for root.
5583 : @param idx Subkey index, sould be initially 0, then
5584 : incremented in each call until
5585 : *subhKey becomes zero and the function
5586 : returns DB_NO_MORE_SUBKEYS
5587 : @param subkey_handle Handle of subkey which can be used in
5588 : db_get_key() and db_get_data()
5589 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_MORE_SUBKEYS
5590 : */
5591 44 : INT db_enum_key(HNDLE hDB, HNDLE hKey, INT idx, HNDLE * subkey_handle)
5592 : {
5593 44 : if (rpc_is_remote())
5594 0 : return rpc_call(RPC_DB_ENUM_KEY, hDB, hKey, idx, subkey_handle);
5595 :
5596 : #ifdef LOCAL_ROUTINES
5597 : {
5598 : DATABASE_HEADER *pheader;
5599 : INT i;
5600 : char str[256];
5601 : HNDLE parent;
5602 :
5603 44 : if (hDB > _database_entries || hDB <= 0) {
5604 0 : cm_msg(MERROR, "db_enum_key", "invalid database handle");
5605 6 : return DB_INVALID_HANDLE;
5606 : }
5607 :
5608 44 : if (!_database[hDB - 1].attached) {
5609 0 : cm_msg(MERROR, "db_enum_key", "invalid database handle");
5610 0 : return DB_INVALID_HANDLE;
5611 : }
5612 :
5613 44 : *subkey_handle = 0;
5614 :
5615 : /* first lock database */
5616 44 : db_lock_database(hDB);
5617 :
5618 44 : pheader = _database[hDB - 1].database_header;
5619 44 : if (!hKey)
5620 3 : hKey = pheader->root_key;
5621 :
5622 : /* check if hKey argument is correct */
5623 44 : if (!db_validate_hkey(pheader, hKey)) {
5624 0 : db_unlock_database(hDB);
5625 0 : return DB_INVALID_HANDLE;
5626 : }
5627 :
5628 44 : db_err_msg *msg = NULL;
5629 : int status;
5630 :
5631 44 : const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_enum_key", &msg);
5632 :
5633 44 : if (!pkey) {
5634 0 : db_unlock_database(hDB);
5635 0 : db_flush_msg(&msg);
5636 0 : return DB_NO_MORE_SUBKEYS;
5637 : }
5638 :
5639 44 : if (pkey->type != TID_KEY) {
5640 0 : db_unlock_database(hDB);
5641 0 : return DB_NO_MORE_SUBKEYS;
5642 : }
5643 :
5644 44 : const KEYLIST* pkeylist = db_get_pkeylist(pheader, hKey, pkey, "db_enum_key", &msg);
5645 :
5646 44 : if (!pkeylist) {
5647 0 : db_unlock_database(hDB);
5648 0 : db_flush_msg(&msg);
5649 0 : return DB_NO_MORE_SUBKEYS;
5650 : }
5651 :
5652 44 : if (idx >= pkeylist->num_keys) {
5653 6 : db_unlock_database(hDB);
5654 6 : return DB_NO_MORE_SUBKEYS;
5655 : }
5656 :
5657 38 : pkey = db_get_pkey(pheader, pkeylist->first_key, &status, "db_enum_key", &msg);
5658 :
5659 38 : if (!pkey) {
5660 0 : std::string path = db_get_path_locked(pheader, hKey);
5661 0 : HNDLE xfirst_key = pkeylist->first_key;
5662 0 : db_unlock_database(hDB);
5663 0 : if (msg)
5664 0 : db_flush_msg(&msg);
5665 0 : cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" invalid first_key %d", hKey, path.c_str(), xfirst_key);
5666 0 : return DB_NO_MORE_SUBKEYS;
5667 0 : }
5668 :
5669 183 : for (i = 0; i < idx; i++) {
5670 145 : if (pkey->next_key == 0) {
5671 0 : std::string path = db_get_path_locked(pheader, hKey);
5672 0 : db_unlock_database(hDB);
5673 0 : cm_msg(MERROR, "db_enum_key", "hkey %d path \"%s\" unexpected end of key list at index %d", hKey, path.c_str(), i);
5674 0 : return DB_NO_MORE_SUBKEYS;
5675 0 : }
5676 :
5677 145 : pkey = db_get_pkey(pheader, pkey->next_key, &status, "db_enum_key", &msg);
5678 :
5679 145 : if (!pkey) {
5680 0 : std::string path = db_get_path_locked(pheader, hKey);
5681 0 : db_unlock_database(hDB);
5682 0 : db_flush_msg(&msg);
5683 0 : 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 0 : return DB_NO_MORE_SUBKEYS;
5685 0 : }
5686 : }
5687 :
5688 : /* resolve links */
5689 38 : if (pkey->type == TID_LINK) {
5690 0 : strcpy(str, (char *) pheader + pkey->data);
5691 :
5692 : /* no not resolve if link to array index */
5693 0 : if (strlen(str) > 0 && str[strlen(str) - 1] == ']') {
5694 0 : *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5695 0 : db_unlock_database(hDB);
5696 0 : return DB_SUCCESS;
5697 : }
5698 :
5699 0 : if (*str == '/') {
5700 : /* absolute path */
5701 0 : db_unlock_database(hDB);
5702 0 : return db_find_key(hDB, 0, str, subkey_handle);
5703 : } else {
5704 : /* relative path */
5705 0 : if (pkey->parent_keylist) {
5706 0 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
5707 0 : parent = pkeylist->parent;
5708 0 : db_unlock_database(hDB);
5709 0 : return db_find_key(hDB, parent, str, subkey_handle);
5710 : } else {
5711 0 : db_unlock_database(hDB);
5712 0 : return db_find_key(hDB, 0, str, subkey_handle);
5713 : }
5714 : }
5715 : }
5716 :
5717 38 : *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5718 38 : db_unlock_database(hDB);
5719 : }
5720 : #endif /* LOCAL_ROUTINES */
5721 :
5722 38 : return DB_SUCCESS;
5723 : }
5724 :
5725 : /**dox***************************************************************/
5726 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
5727 :
5728 :
5729 : /*------------------------------------------------------------------*/
5730 36 : INT db_enum_link(HNDLE hDB, HNDLE hKey, INT idx, HNDLE * subkey_handle)
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 36 : if (rpc_is_remote())
5758 0 : return rpc_call(RPC_DB_ENUM_LINK, hDB, hKey, idx, subkey_handle);
5759 :
5760 : #ifdef LOCAL_ROUTINES
5761 : {
5762 : DATABASE_HEADER *pheader;
5763 : KEYLIST *pkeylist;
5764 : KEY *pkey;
5765 : INT i;
5766 :
5767 36 : if (hDB > _database_entries || hDB <= 0) {
5768 0 : cm_msg(MERROR, "db_enum_link", "invalid database handle");
5769 0 : return DB_INVALID_HANDLE;
5770 : }
5771 :
5772 36 : if (!_database[hDB - 1].attached) {
5773 0 : cm_msg(MERROR, "db_enum_link", "invalid database handle");
5774 0 : return DB_INVALID_HANDLE;
5775 : }
5776 :
5777 36 : *subkey_handle = 0;
5778 :
5779 : /* first lock database */
5780 36 : db_lock_database(hDB);
5781 :
5782 36 : pheader = _database[hDB - 1].database_header;
5783 36 : if (!hKey)
5784 0 : hKey = pheader->root_key;
5785 :
5786 : /* check if hKey argument is correct */
5787 36 : if (!db_validate_hkey(pheader, hKey)) {
5788 0 : db_unlock_database(hDB);
5789 0 : return DB_INVALID_HANDLE;
5790 : }
5791 :
5792 36 : pkey = (KEY *) ((char *) pheader + hKey);
5793 :
5794 36 : if (pkey->type != TID_KEY) {
5795 0 : db_unlock_database(hDB);
5796 0 : return DB_NO_MORE_SUBKEYS;
5797 : }
5798 36 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
5799 :
5800 36 : if (idx >= pkeylist->num_keys) {
5801 10 : db_unlock_database(hDB);
5802 10 : return DB_NO_MORE_SUBKEYS;
5803 : }
5804 :
5805 : // FIXME: validate pkeylist->first_key
5806 26 : pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
5807 98 : for (i = 0; i < idx; i++) {
5808 : // FIXME: validate pkey->next_key
5809 72 : pkey = (KEY *) ((char *) pheader + pkey->next_key);
5810 : }
5811 :
5812 26 : *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5813 26 : db_unlock_database(hDB);
5814 : }
5815 : #endif /* LOCAL_ROUTINES */
5816 :
5817 26 : return DB_SUCCESS;
5818 : }
5819 :
5820 : /*------------------------------------------------------------------*/
5821 0 : INT db_get_next_link(HNDLE hDB, HNDLE hKey, HNDLE * subkey_handle)
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 0 : if (rpc_is_remote())
5845 0 : return rpc_call(RPC_DB_GET_NEXT_LINK, hDB, hKey, subkey_handle);
5846 :
5847 : #ifdef LOCAL_ROUTINES
5848 : {
5849 : DATABASE_HEADER *pheader;
5850 : KEYLIST *pkeylist;
5851 : KEY *pkey;
5852 : INT descent;
5853 :
5854 0 : if (hDB > _database_entries || hDB <= 0) {
5855 0 : cm_msg(MERROR, "db_enum_link", "invalid database handle");
5856 0 : return DB_INVALID_HANDLE;
5857 : }
5858 :
5859 0 : if (!_database[hDB - 1].attached) {
5860 0 : cm_msg(MERROR, "db_enum_link", "invalid database handle");
5861 0 : return DB_INVALID_HANDLE;
5862 : }
5863 :
5864 0 : *subkey_handle = 0;
5865 :
5866 : /* first lock database */
5867 0 : db_lock_database(hDB);
5868 :
5869 0 : pheader = _database[hDB - 1].database_header;
5870 0 : if (!hKey)
5871 0 : hKey = pheader->root_key;
5872 :
5873 : /* check if hKey argument is correct */
5874 0 : if (!db_validate_hkey(pheader, hKey)) {
5875 0 : db_unlock_database(hDB);
5876 0 : return DB_INVALID_HANDLE;
5877 : }
5878 :
5879 0 : pkey = (KEY *) ((char *) pheader + hKey);
5880 :
5881 0 : descent = TRUE;
5882 : do {
5883 0 : if (pkey->type != TID_KEY || !descent) {
5884 0 : if (pkey->next_key) {
5885 : /* key has next key, return it */
5886 : // FIXME: validate pkey->next_key
5887 0 : pkey = (KEY *) ((char *) pheader + pkey->next_key);
5888 :
5889 0 : if (pkey->type != TID_KEY) {
5890 0 : *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5891 0 : db_unlock_database(hDB);
5892 0 : return DB_SUCCESS;
5893 : }
5894 :
5895 : /* key has subkeys, so descent on the next iteration */
5896 0 : descent = TRUE;
5897 : } else {
5898 0 : if (pkey->parent_keylist == 0) {
5899 : /* return if we are back to the root key */
5900 0 : db_unlock_database(hDB);
5901 0 : return DB_NO_MORE_SUBKEYS;
5902 : }
5903 :
5904 : /* key is last in list, traverse up */
5905 0 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
5906 :
5907 : // FIXME: validate pkeylist->parent
5908 0 : pkey = (KEY *) ((char *) pheader + pkeylist->parent);
5909 0 : descent = FALSE;
5910 : }
5911 : } else {
5912 0 : if (descent) {
5913 : /* find first subkey */
5914 0 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
5915 :
5916 0 : if (pkeylist->num_keys == 0) {
5917 : /* if key has no subkeys, look for next key on this level */
5918 0 : descent = FALSE;
5919 : } else {
5920 : /* get first subkey */
5921 : // FIXME: validate pkeylist->first_key
5922 0 : pkey = (KEY *) ((char *) pheader + pkeylist->first_key);
5923 :
5924 0 : if (pkey->type != TID_KEY) {
5925 0 : *subkey_handle = (POINTER_T) pkey - (POINTER_T) pheader;
5926 0 : db_unlock_database(hDB);
5927 0 : return DB_SUCCESS;
5928 : }
5929 : }
5930 : }
5931 : }
5932 :
5933 : } while (TRUE);
5934 : }
5935 : #endif /* LOCAL_ROUTINES */
5936 :
5937 : return DB_SUCCESS;
5938 : }
5939 :
5940 : /**dox***************************************************************/
5941 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
5942 :
5943 : /********************************************************************/
5944 :
5945 : #ifdef LOCAL_ROUTINES
5946 :
5947 243 : static INT db_get_key_locked(const DATABASE_HEADER* pheader, HNDLE hKey, KEY * key, db_err_msg** msg)
5948 : {
5949 243 : if (!hKey)
5950 0 : hKey = pheader->root_key;
5951 :
5952 : /* check if hKey argument is correct */
5953 243 : if (!db_validate_hkey(pheader, hKey)) {
5954 0 : return DB_INVALID_HANDLE;
5955 : }
5956 :
5957 243 : const KEY* pkey = (const KEY *) ((char *) pheader + hKey);
5958 :
5959 243 : if (pkey->type < 1 || pkey->type >= TID_LAST) {
5960 0 : int pkey_type = pkey->type;
5961 0 : db_msg(msg, MERROR, "db_get_key", "hkey %d invalid key type %d", hKey, pkey_type);
5962 0 : return DB_INVALID_HANDLE;
5963 : }
5964 :
5965 : /* check for link to array index */
5966 243 : if (pkey->type == TID_LINK) {
5967 : char link_name[MAX_ODB_PATH];
5968 0 : mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
5969 0 : if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
5970 0 : if (strchr(link_name, '[') == NULL)
5971 0 : return DB_INVALID_LINK;
5972 :
5973 : HNDLE hkeylink;
5974 0 : if (db_find_key_locked(pheader, 0, link_name, &hkeylink, msg) != DB_SUCCESS)
5975 0 : return DB_INVALID_LINK;
5976 0 : int status = db_get_key_locked(pheader, hkeylink, key, msg);
5977 0 : key->num_values = 1; // fake number of values
5978 0 : return status;
5979 : }
5980 : }
5981 :
5982 243 : memcpy(key, pkey, sizeof(KEY));
5983 :
5984 243 : return DB_SUCCESS;
5985 : }
5986 : #endif /* LOCAL_ROUTINES */
5987 :
5988 : /********************************************************************/
5989 : /**
5990 : Get key structure from a handle.
5991 :
5992 : KEY structure has following format:
5993 : \code
5994 : typedef struct {
5995 : DWORD type; // TID_xxx type
5996 : INT num_values; // number of values
5997 : char name[NAME_LENGTH]; // name of variable
5998 : INT data; // Address of variable (offset)
5999 : INT total_size; // Total size of data block
6000 : INT item_size; // Size of single data item
6001 : WORD access_mode; // Access mode
6002 : WORD notify_count; // Notify counter
6003 : INT next_key; // Address of next key
6004 : INT parent_keylist; // keylist to which this key belongs
6005 : INT last_written; // Time of last write action
6006 : } KEY;
6007 : \endcode
6008 : Most of these values are used for internal purposes, the values which are of
6009 : public interest are type, name, num_values, item_size and total_size.
6010 : For keys which contain a single value, num_values equals to one and total_size equals to item_size. For keys which contain an array of strings (TID_STRING),
6011 : item_size equals to the length of one string.
6012 : \code
6013 : KEY key;
6014 : HNDLE hkey;
6015 : db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
6016 : db_get_key(hDB, hkey, &key);
6017 : printf("The run number is of type %s\n", rpc_tid_name(key.type));
6018 : \endcode
6019 : @param hDB ODB handle obtained via cm_get_experiment_database().
6020 : @param hKey Handle for key where search starts, zero for root. If Key is a link to an array element, this link is resolved. In this case function returns the key of the link destination and num_values is set to 1.
6021 : @param key Pointer to KEY stucture.
6022 : @return DB_SUCCESS, DB_INVALID_HANDLE
6023 : */
6024 243 : INT db_get_key(HNDLE hDB, HNDLE hKey, KEY * key)
6025 : {
6026 243 : if (rpc_is_remote())
6027 0 : 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 243 : if (hDB > _database_entries || hDB <= 0) {
6035 0 : cm_msg(MERROR, "db_get_key", "invalid database handle");
6036 0 : return DB_INVALID_HANDLE;
6037 : }
6038 :
6039 243 : if (!_database[hDB - 1].attached) {
6040 0 : cm_msg(MERROR, "db_get_key", "invalid database handle");
6041 0 : return DB_INVALID_HANDLE;
6042 : }
6043 :
6044 243 : if (hKey < (int) sizeof(DATABASE_HEADER) && hKey != 0) {
6045 0 : cm_msg(MERROR, "db_get_key", "invalid key handle");
6046 0 : return DB_INVALID_HANDLE;
6047 : }
6048 :
6049 243 : db_err_msg *msg = NULL;
6050 :
6051 243 : db_lock_database(hDB);
6052 :
6053 243 : pheader = _database[hDB - 1].database_header;
6054 :
6055 243 : status = db_get_key_locked(pheader, hKey, key, &msg);
6056 :
6057 243 : db_unlock_database(hDB);
6058 :
6059 243 : if (msg)
6060 0 : db_flush_msg(&msg);
6061 :
6062 243 : return status;
6063 : }
6064 : #endif /* LOCAL_ROUTINES */
6065 :
6066 : return DB_SUCCESS;
6067 : }
6068 :
6069 : /********************************************************************/
6070 : /**
6071 : Same as db_get_key, but it does not follow links
6072 : @param hDB ODB handle obtained via cm_get_experiment_database().
6073 : @param hKey Handle for key where search starts, zero for root.
6074 : @param key Pointer to KEY stucture.
6075 : @return DB_SUCCESS, DB_INVALID_HANDLE
6076 : */
6077 20 : INT db_get_link(HNDLE hDB, HNDLE hKey, KEY * key)
6078 : {
6079 20 : if (rpc_is_remote())
6080 0 : return rpc_call(RPC_DB_GET_LINK, hDB, hKey, key);
6081 :
6082 : #ifdef LOCAL_ROUTINES
6083 : {
6084 : DATABASE_HEADER *pheader;
6085 :
6086 20 : if (hDB > _database_entries || hDB <= 0) {
6087 0 : cm_msg(MERROR, "db_get_link", "invalid database handle");
6088 0 : return DB_INVALID_HANDLE;
6089 : }
6090 :
6091 20 : if (!_database[hDB - 1].attached) {
6092 0 : cm_msg(MERROR, "db_get_link", "invalid database handle");
6093 0 : return DB_INVALID_HANDLE;
6094 : }
6095 :
6096 20 : if (hKey < (int) sizeof(DATABASE_HEADER) && hKey != 0) {
6097 0 : cm_msg(MERROR, "db_get_link", "invalid key handle");
6098 0 : return DB_INVALID_HANDLE;
6099 : }
6100 :
6101 20 : db_err_msg *msg = NULL;
6102 :
6103 20 : db_lock_database(hDB);
6104 :
6105 20 : pheader = _database[hDB - 1].database_header;
6106 :
6107 20 : int status = DB_SUCCESS;
6108 :
6109 20 : const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_get_link", &msg);
6110 20 : if (pkey) {
6111 20 : memcpy(key, pkey, sizeof(KEY));
6112 : } else {
6113 0 : memset(key, 0, sizeof(KEY));
6114 : //abort();
6115 : }
6116 :
6117 20 : db_unlock_database(hDB);
6118 :
6119 20 : if (msg)
6120 0 : db_flush_msg(&msg);
6121 :
6122 20 : return status;
6123 : }
6124 : #endif /* LOCAL_ROUTINES */
6125 :
6126 : return DB_SUCCESS;
6127 : }
6128 :
6129 : /********************************************************************/
6130 : /**
6131 : Get time when key was last modified
6132 : @param hDB ODB handle obtained via cm_get_experiment_database().
6133 : @param hKey Handle of key to operate on
6134 : @param delta Seconds since last update
6135 : @return DB_SUCCESS, DB_INVALID_HANDLE
6136 : */
6137 0 : INT db_get_key_time(HNDLE hDB, HNDLE hKey, DWORD * delta)
6138 : {
6139 0 : if (rpc_is_remote())
6140 0 : return rpc_call(RPC_DB_GET_KEY_TIME, hDB, hKey, delta);
6141 :
6142 : #ifdef LOCAL_ROUTINES
6143 : {
6144 : DATABASE_HEADER *pheader;
6145 : KEY *pkey;
6146 :
6147 0 : if (hDB > _database_entries || hDB <= 0) {
6148 0 : cm_msg(MERROR, "db_get_key", "invalid database handle");
6149 0 : return DB_INVALID_HANDLE;
6150 : }
6151 :
6152 0 : if (!_database[hDB - 1].attached) {
6153 0 : cm_msg(MERROR, "db_get_key", "invalid database handle");
6154 0 : return DB_INVALID_HANDLE;
6155 : }
6156 :
6157 0 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
6158 0 : cm_msg(MERROR, "db_get_key", "invalid key handle");
6159 0 : return DB_INVALID_HANDLE;
6160 : }
6161 :
6162 0 : db_lock_database(hDB);
6163 :
6164 0 : pheader = _database[hDB - 1].database_header;
6165 :
6166 : /* check if hKey argument is correct */
6167 0 : if (!db_validate_hkey(pheader, hKey)) {
6168 0 : db_unlock_database(hDB);
6169 0 : return DB_INVALID_HANDLE;
6170 : }
6171 :
6172 0 : pkey = (KEY *) ((char *) pheader + hKey);
6173 :
6174 0 : *delta = ss_time() - pkey->last_written;
6175 :
6176 0 : db_unlock_database(hDB);
6177 :
6178 : }
6179 : #endif /* LOCAL_ROUTINES */
6180 :
6181 0 : return DB_SUCCESS;
6182 : }
6183 :
6184 : /********************************************************************/
6185 : /**
6186 : Get key info (separate values instead of structure)
6187 : @param hDB ODB handle obtained via cm_get_experiment_database().
6188 : @param hKey Handle of key to operate on
6189 : @param name Key name
6190 : @param name_size Size of the give name (done with sizeof())
6191 : @param type Key type (see @ref Midas_Data_Types).
6192 : @param num_values Number of values in key.
6193 : @param item_size Size of individual key value (used for strings)
6194 : @return DB_SUCCESS, DB_INVALID_HANDLE
6195 : */
6196 0 : INT db_get_key_info(HNDLE hDB, HNDLE hKey, char *name, INT name_size, INT * type, INT * num_values, INT * item_size)
6197 : {
6198 0 : if (rpc_is_remote())
6199 0 : 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;
6205 : KEYLIST *pkeylist;
6206 :
6207 0 : if (hDB > _database_entries || hDB <= 0) {
6208 0 : cm_msg(MERROR, "db_get_key_info", "invalid database handle");
6209 0 : return DB_INVALID_HANDLE;
6210 : }
6211 :
6212 0 : if (!_database[hDB - 1].attached) {
6213 0 : cm_msg(MERROR, "db_get_key_info", "invalid database handle");
6214 0 : return DB_INVALID_HANDLE;
6215 : }
6216 :
6217 0 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
6218 0 : cm_msg(MERROR, "db_get_key_info", "invalid key handle");
6219 0 : return DB_INVALID_HANDLE;
6220 : }
6221 :
6222 0 : db_lock_database(hDB);
6223 :
6224 0 : pheader = _database[hDB - 1].database_header;
6225 :
6226 : /* check if hKey argument is correct */
6227 0 : if (!db_validate_hkey(pheader, hKey)) {
6228 0 : db_unlock_database(hDB);
6229 0 : return DB_INVALID_HANDLE;
6230 : }
6231 :
6232 0 : pkey = (KEY *) ((char *) pheader + hKey);
6233 :
6234 0 : if ((INT) strlen(pkey->name) + 1 > name_size) {
6235 : /* truncate name */
6236 0 : memcpy(name, pkey->name, name_size - 1);
6237 0 : name[name_size] = 0;
6238 : } else
6239 0 : strcpy(name, pkey->name);
6240 :
6241 : /* convert "root" to "/" */
6242 0 : if (strcmp(name, "root") == 0)
6243 0 : strcpy(name, "/");
6244 :
6245 0 : *type = pkey->type;
6246 0 : *num_values = pkey->num_values;
6247 0 : *item_size = pkey->item_size;
6248 :
6249 0 : if (pkey->type == TID_KEY) {
6250 0 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
6251 0 : *num_values = pkeylist->num_keys;
6252 : }
6253 :
6254 0 : db_unlock_database(hDB);
6255 : }
6256 : #endif /* LOCAL_ROUTINES */
6257 :
6258 0 : return DB_SUCCESS;
6259 : }
6260 :
6261 : /**dox***************************************************************/
6262 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
6263 :
6264 :
6265 : /*------------------------------------------------------------------*/
6266 0 : INT db_rename_key(HNDLE hDB, HNDLE hKey, const char *name)
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 0 : if (rpc_is_remote())
6289 0 : return rpc_call(RPC_DB_RENAME_KEY, hDB, hKey, name);
6290 :
6291 : #ifdef LOCAL_ROUTINES
6292 : {
6293 : DATABASE_HEADER *pheader;
6294 : KEY *pkey;
6295 : int status;
6296 :
6297 0 : if (hDB > _database_entries || hDB <= 0) {
6298 0 : cm_msg(MERROR, "db_rename_key", "invalid database handle");
6299 0 : return DB_INVALID_HANDLE;
6300 : }
6301 :
6302 0 : if (!_database[hDB - 1].attached) {
6303 0 : cm_msg(MERROR, "db_rename_key", "invalid database handle");
6304 0 : return DB_INVALID_HANDLE;
6305 : }
6306 :
6307 0 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
6308 0 : cm_msg(MERROR, "db_rename_key", "invalid key handle");
6309 0 : return DB_INVALID_HANDLE;
6310 : }
6311 :
6312 0 : db_err_msg* msg = NULL;
6313 0 : status = db_validate_name(name, FALSE, "db_rename_key", &msg);
6314 0 : if (msg)
6315 0 : db_flush_msg(&msg);
6316 0 : if (status != DB_SUCCESS)
6317 0 : return status;
6318 :
6319 0 : if (name == NULL) {
6320 0 : cm_msg(MERROR, "db_rename_key", "key name is NULL");
6321 0 : return DB_INVALID_NAME;
6322 : }
6323 :
6324 0 : if (strlen(name) < 1) {
6325 0 : cm_msg(MERROR, "db_rename_key", "key name is too short");
6326 0 : return DB_INVALID_NAME;
6327 : }
6328 :
6329 0 : if (strchr(name, '/')) {
6330 0 : cm_msg(MERROR, "db_rename_key", "key name may not contain \"/\"");
6331 0 : return DB_INVALID_NAME;
6332 : }
6333 :
6334 0 : db_lock_database(hDB);
6335 :
6336 0 : pheader = _database[hDB - 1].database_header;
6337 :
6338 : /* check if hKey argument is correct */
6339 0 : if (!db_validate_hkey(pheader, hKey)) {
6340 0 : db_unlock_database(hDB);
6341 0 : return DB_INVALID_HANDLE;
6342 : }
6343 :
6344 0 : pkey = (KEY *) ((char *) pheader + hKey);
6345 :
6346 0 : if (!pkey->type) {
6347 0 : int pkey_type = pkey->type;
6348 0 : db_unlock_database(hDB);
6349 0 : cm_msg(MERROR, "db_rename_key", "hkey %d invalid key type %d", hKey, pkey_type);
6350 0 : return DB_INVALID_HANDLE;
6351 : }
6352 :
6353 0 : db_allow_write_locked(&_database[hDB - 1], "db_rename_key");
6354 :
6355 0 : mstrlcpy(pkey->name, name, NAME_LENGTH);
6356 :
6357 0 : db_unlock_database(hDB);
6358 :
6359 : }
6360 : #endif /* LOCAL_ROUTINES */
6361 :
6362 0 : return DB_SUCCESS;
6363 : }
6364 :
6365 : /*------------------------------------------------------------------*/
6366 0 : INT db_reorder_key(HNDLE hDB, HNDLE hKey, INT idx)
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 0 : if (rpc_is_remote())
6391 0 : return rpc_call(RPC_DB_REORDER_KEY, hDB, hKey, idx);
6392 :
6393 : #ifdef LOCAL_ROUTINES
6394 : {
6395 : DATABASE_HEADER *pheader;
6396 : KEY *pkey, *pnext_key, *pkey_tmp;
6397 : KEYLIST *pkeylist;
6398 : INT i;
6399 :
6400 0 : if (hDB > _database_entries || hDB <= 0) {
6401 0 : cm_msg(MERROR, "db_rename_key", "invalid database handle");
6402 0 : return DB_INVALID_HANDLE;
6403 : }
6404 :
6405 0 : if (!_database[hDB - 1].attached) {
6406 0 : cm_msg(MERROR, "db_rename_key", "invalid database handle");
6407 0 : return DB_INVALID_HANDLE;
6408 : }
6409 :
6410 0 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
6411 0 : cm_msg(MERROR, "db_rename_key", "invalid key handle");
6412 0 : return DB_INVALID_HANDLE;
6413 : }
6414 :
6415 0 : db_lock_database(hDB);
6416 :
6417 0 : pheader = _database[hDB - 1].database_header;
6418 :
6419 : /* check if hKey argument is correct */
6420 0 : if (!db_validate_hkey(pheader, hKey)) {
6421 0 : db_unlock_database(hDB);
6422 0 : return DB_INVALID_HANDLE;
6423 : }
6424 :
6425 0 : pkey = (KEY *) ((char *) pheader + hKey);
6426 :
6427 0 : if (!pkey->type) {
6428 0 : int pkey_type = pkey->type;
6429 0 : db_unlock_database(hDB);
6430 0 : cm_msg(MERROR, "db_reorder_key", "hkey %d invalid key type %d", hKey, pkey_type);
6431 0 : return DB_INVALID_HANDLE;
6432 : }
6433 :
6434 0 : if (!(pkey->access_mode & MODE_WRITE)) {
6435 0 : db_unlock_database(hDB);
6436 0 : return DB_NO_ACCESS;
6437 : }
6438 :
6439 : /* check if someone has opened key or parent */
6440 : do {
6441 : #ifdef CHECK_OPEN_RECORD
6442 0 : if (pkey->notify_count) {
6443 0 : db_unlock_database(hDB);
6444 0 : return DB_OPEN_RECORD;
6445 : }
6446 : #endif
6447 0 : if (pkey->parent_keylist == 0)
6448 0 : break;
6449 :
6450 0 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
6451 : // FIXME: validate pkeylist->parent
6452 0 : pkey = (KEY *) ((char *) pheader + pkeylist->parent);
6453 : } while (TRUE);
6454 :
6455 0 : db_allow_write_locked(&_database[hDB - 1], "db_reorder_key");
6456 :
6457 0 : pkey = (KEY *) ((char *) pheader + hKey); // NB: hKey is already validated
6458 0 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
6459 :
6460 : /* first remove key from list */
6461 0 : pnext_key = (KEY *) (POINTER_T) pkey->next_key; // FIXME: what is this pointer cast?
6462 :
6463 0 : if ((KEY *) ((char *) pheader + pkeylist->first_key) == pkey) {
6464 : /* key is first in list */
6465 0 : pkeylist->first_key = (POINTER_T) pnext_key;
6466 : } else {
6467 : /* find predecessor */
6468 : // FIXME: validate pkeylist->first_key
6469 0 : pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
6470 0 : while ((KEY *) ((char *) pheader + pkey_tmp->next_key) != pkey) {
6471 : // FIXME: validate pkey_tmp->next_key
6472 0 : pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
6473 : }
6474 0 : 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 0 : pkey_tmp = (KEY *) ((char *) pheader + pkeylist->first_key);
6480 0 : if (idx < 0 || idx >= pkeylist->num_keys - 1) {
6481 : /* add at bottom */
6482 :
6483 : /* find last key */
6484 0 : for (i = 0; i < pkeylist->num_keys - 2; i++) {
6485 : // FIXME: validate pkey_tmp->next_key
6486 0 : pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
6487 : }
6488 :
6489 0 : pkey_tmp->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
6490 0 : pkey->next_key = 0;
6491 : } else {
6492 0 : if (idx == 0) {
6493 : /* add at top */
6494 0 : pkey->next_key = pkeylist->first_key;
6495 0 : pkeylist->first_key = (POINTER_T) pkey - (POINTER_T) pheader;
6496 : } else {
6497 : /* add at position index */
6498 0 : for (i = 0; i < idx - 1; i++) {
6499 : // FIXME: validate pkey_tmp->next_key
6500 0 : pkey_tmp = (KEY *) ((char *) pheader + pkey_tmp->next_key);
6501 : }
6502 :
6503 0 : pkey->next_key = pkey_tmp->next_key;
6504 0 : pkey_tmp->next_key = (POINTER_T) pkey - (POINTER_T) pheader;
6505 : }
6506 : }
6507 :
6508 0 : db_unlock_database(hDB);
6509 :
6510 : }
6511 : #endif /* LOCAL_ROUTINES */
6512 :
6513 0 : return DB_SUCCESS;
6514 : }
6515 :
6516 : /**dox***************************************************************/
6517 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
6518 :
6519 :
6520 : /********************************************************************/
6521 : /**
6522 : Get key data from a handle
6523 :
6524 : The function returns single values or whole arrays which are contained
6525 : in an ODB key. Since the data buffer is of type void, no type checking can be
6526 : performed by the compiler. Therefore the type has to be explicitly supplied,
6527 : which is checked against the type stored in the ODB.
6528 : \code
6529 : HNLDE hkey;
6530 : INT run_number, size;
6531 : // get key handle for run number
6532 : db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
6533 : // return run number
6534 : size = sizeof(run_number);
6535 : db_get_data(hDB, hkey, &run_number, &size,TID_INT32);
6536 : \endcode
6537 : @param hDB ODB handle obtained via cm_get_experiment_database().
6538 : @param hKey Handle for key where search starts, zero for root.
6539 : @param data Pointer to the return data.
6540 : @param buf_size Size of data buffer.
6541 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
6542 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED, DB_TYPE_MISMATCH
6543 : */
6544 40 : INT db_get_data(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, DWORD type)
6545 : {
6546 40 : if (rpc_is_remote())
6547 0 : return rpc_call(RPC_DB_GET_DATA, hDB, hKey, data, buf_size, type);
6548 :
6549 : #ifdef LOCAL_ROUTINES
6550 : {
6551 : int status;
6552 :
6553 40 : if (hDB > _database_entries || hDB <= 0) {
6554 0 : cm_msg(MERROR, "db_get_data", "Invalid database handle");
6555 0 : return DB_INVALID_HANDLE;
6556 : }
6557 :
6558 40 : if (!_database[hDB - 1].attached) {
6559 0 : cm_msg(MERROR, "db_get_data", "invalid database handle");
6560 0 : return DB_INVALID_HANDLE;
6561 : }
6562 :
6563 40 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
6564 0 : cm_msg(MERROR, "db_get_data", "invalid key handle");
6565 0 : return DB_INVALID_HANDLE;
6566 : }
6567 :
6568 40 : db_lock_database(hDB);
6569 :
6570 40 : DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
6571 40 : db_err_msg* msg = NULL;
6572 :
6573 40 : const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_get_data", &msg);
6574 :
6575 40 : if (!pkey) {
6576 0 : db_unlock_database(hDB);
6577 0 : if (msg)
6578 0 : db_flush_msg(&msg);
6579 0 : return status;
6580 : }
6581 :
6582 : /* check for read access */
6583 40 : if (!(pkey->access_mode & MODE_READ)) {
6584 0 : db_unlock_database(hDB);
6585 0 : if (msg)
6586 0 : db_flush_msg(&msg);
6587 0 : return DB_NO_ACCESS;
6588 : }
6589 :
6590 : /* follow links to array index */
6591 40 : if (pkey->type == TID_LINK) {
6592 0 : std::string link_name = (char *) pheader + pkey->data;
6593 0 : if (link_name.length() > 0 && link_name.back() == ']') {
6594 0 : size_t pos = link_name.rfind("[");
6595 0 : if (pos == std::string::npos) {
6596 0 : 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());
6597 0 : db_unlock_database(hDB);
6598 0 : if (msg)
6599 0 : db_flush_msg(&msg);
6600 0 : return DB_INVALID_LINK;
6601 : }
6602 0 : int idx = atoi(link_name.c_str()+pos+1);
6603 0 : 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 0 : if (link_name[0] != '/') {
6608 0 : 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());
6609 0 : db_unlock_database(hDB);
6610 0 : if (msg)
6611 0 : db_flush_msg(&msg);
6612 0 : return DB_INVALID_LINK;
6613 : }
6614 :
6615 0 : const KEY* pkey = db_find_pkey_locked(pheader, NULL, link_name.c_str(), &status, &msg);
6616 :
6617 0 : if (!pkey) {
6618 0 : db_unlock_database(hDB);
6619 0 : if (msg)
6620 0 : db_flush_msg(&msg);
6621 0 : 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 0 : status = db_get_data_locked(pheader, pkey, idx, data, buf_size, type, &msg);
6627 :
6628 0 : db_unlock_database(hDB);
6629 0 : if (msg)
6630 0 : db_flush_msg(&msg);
6631 0 : return status;
6632 : }
6633 0 : }
6634 :
6635 : //printf("db_get_data [%s] type [%s]\n", db_get_path_locked(pheader, pkey).c_str(), rpc_tid_name(type));
6636 :
6637 40 : status = db_get_data_locked(pheader, pkey, -1, data, buf_size, type, &msg);
6638 :
6639 40 : db_unlock_database(hDB);
6640 40 : if (msg)
6641 0 : db_flush_msg(&msg);
6642 :
6643 40 : return status;
6644 : }
6645 : #endif /* LOCAL_ROUTINES */
6646 :
6647 : return DB_SUCCESS;
6648 : }
6649 :
6650 : /********************************************************************/
6651 : /**
6652 : Same as db_get_data, but do not follow a link to an array index
6653 :
6654 : @param hDB ODB handle obtained via cm_get_experiment_database().
6655 : @param hKey Handle for key where search starts, zero for root.
6656 : @param data Pointer to the return data.
6657 : @param buf_size Size of data buffer.
6658 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
6659 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED, DB_TYPE_MISMATCH
6660 : */
6661 20 : INT db_get_link_data(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, DWORD type)
6662 : {
6663 20 : if (rpc_is_remote())
6664 0 : 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 20 : if (hDB > _database_entries || hDB <= 0) {
6672 0 : cm_msg(MERROR, "db_get_data", "Invalid database handle");
6673 0 : return DB_INVALID_HANDLE;
6674 : }
6675 :
6676 20 : if (!_database[hDB - 1].attached) {
6677 0 : cm_msg(MERROR, "db_get_data", "invalid database handle");
6678 0 : return DB_INVALID_HANDLE;
6679 : }
6680 :
6681 20 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
6682 0 : cm_msg(MERROR, "db_get_data", "invalid key handle");
6683 0 : return DB_INVALID_HANDLE;
6684 : }
6685 :
6686 20 : db_lock_database(hDB);
6687 :
6688 20 : pheader = _database[hDB - 1].database_header;
6689 :
6690 : /* check if hKey argument is correct */
6691 20 : if (!db_validate_hkey(pheader, hKey)) {
6692 0 : db_unlock_database(hDB);
6693 0 : return DB_INVALID_HANDLE;
6694 : }
6695 :
6696 20 : pkey = (KEY *) ((char *) pheader + hKey);
6697 :
6698 : /* check for read access */
6699 20 : if (!(pkey->access_mode & MODE_READ)) {
6700 0 : db_unlock_database(hDB);
6701 0 : return DB_NO_ACCESS;
6702 : }
6703 :
6704 20 : if (!pkey->type) {
6705 0 : int pkey_type = pkey->type;
6706 0 : db_unlock_database(hDB);
6707 0 : cm_msg(MERROR, "db_get_data", "hkey %d invalid key type %d", hKey, pkey_type);
6708 0 : return DB_INVALID_HANDLE;
6709 : }
6710 :
6711 20 : if (pkey->type != type) {
6712 0 : int pkey_type = pkey->type;
6713 : char pkey_name[NAME_LENGTH];
6714 0 : mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
6715 0 : db_unlock_database(hDB);
6716 0 : 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 0 : return DB_TYPE_MISMATCH;
6718 : }
6719 :
6720 : /* keys cannot contain data */
6721 20 : if (pkey->type == TID_KEY) {
6722 0 : db_unlock_database(hDB);
6723 0 : cm_msg(MERROR, "db_get_data", "Key cannot contain data");
6724 0 : return DB_TYPE_MISMATCH;
6725 : }
6726 :
6727 : /* check if key has data */
6728 20 : if (pkey->data == 0) {
6729 0 : memset(data, 0, *buf_size);
6730 0 : *buf_size = 0;
6731 0 : db_unlock_database(hDB);
6732 0 : return DB_SUCCESS;
6733 : }
6734 :
6735 : /* check if buffer is too small */
6736 20 : if (pkey->num_values * pkey->item_size > *buf_size) {
6737 0 : int pkey_size = pkey->num_values * pkey->item_size;
6738 0 : memcpy(data, (char *) pheader + pkey->data, *buf_size);
6739 0 : db_unlock_database(hDB);
6740 0 : std::string path = db_get_path(hDB, hKey);
6741 0 : cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
6742 0 : return DB_TRUNCATED;
6743 0 : }
6744 :
6745 : /* copy key data */
6746 20 : memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
6747 20 : *buf_size = pkey->num_values * pkey->item_size;
6748 :
6749 20 : db_unlock_database(hDB);
6750 :
6751 : }
6752 : #endif /* LOCAL_ROUTINES */
6753 :
6754 20 : return DB_SUCCESS;
6755 : }
6756 :
6757 : /**dox***************************************************************/
6758 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
6759 :
6760 : /*------------------------------------------------------------------*/
6761 0 : INT 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 0 : if (rpc_is_remote())
6788 0 : 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 0 : if (hDB > _database_entries || hDB <= 0) {
6796 0 : cm_msg(MERROR, "db_get_data", "Invalid database handle");
6797 0 : return DB_INVALID_HANDLE;
6798 : }
6799 :
6800 0 : if (!_database[hDB - 1].attached) {
6801 0 : cm_msg(MERROR, "db_get_data", "invalid database handle");
6802 0 : return DB_INVALID_HANDLE;
6803 : }
6804 :
6805 0 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
6806 0 : cm_msg(MERROR, "db_get_data", "invalid key handle");
6807 0 : return DB_INVALID_HANDLE;
6808 : }
6809 :
6810 0 : db_lock_database(hDB);
6811 :
6812 0 : pheader = _database[hDB - 1].database_header;
6813 :
6814 : /* check if hKey argument is correct */
6815 0 : if (!db_validate_hkey(pheader, hKey)) {
6816 0 : db_unlock_database(hDB);
6817 0 : return DB_INVALID_HANDLE;
6818 : }
6819 :
6820 0 : pkey = (KEY *) ((char *) pheader + hKey);
6821 :
6822 : /* check for read access */
6823 0 : if (!(pkey->access_mode & MODE_READ)) {
6824 0 : db_unlock_database(hDB);
6825 0 : return DB_NO_ACCESS;
6826 : }
6827 :
6828 0 : if (!pkey->type) {
6829 0 : int pkey_type = pkey->type;
6830 0 : db_unlock_database(hDB);
6831 0 : cm_msg(MERROR, "db_get_data", "hkey %d invalid key type %d", hKey, pkey_type);
6832 0 : return DB_INVALID_HANDLE;
6833 : }
6834 :
6835 0 : if (pkey->type != type) {
6836 0 : int pkey_type = pkey->type;
6837 : char pkey_name[NAME_LENGTH];
6838 0 : mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
6839 0 : db_unlock_database(hDB);
6840 0 : 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 0 : return DB_TYPE_MISMATCH;
6842 : }
6843 :
6844 : /* keys cannot contain data */
6845 0 : if (pkey->type == TID_KEY) {
6846 0 : db_unlock_database(hDB);
6847 0 : cm_msg(MERROR, "db_get_data", "Key cannot contain data");
6848 0 : return DB_TYPE_MISMATCH;
6849 : }
6850 :
6851 : /* check if key has data */
6852 0 : if (pkey->data == 0) {
6853 0 : memset(data, 0, *buf_size);
6854 0 : *buf_size = 0;
6855 0 : db_unlock_database(hDB);
6856 0 : return DB_SUCCESS;
6857 : }
6858 :
6859 : /* check if buffer is too small */
6860 0 : if (pkey->num_values * pkey->item_size > *buf_size) {
6861 0 : int pkey_size = pkey->num_values * pkey->item_size;
6862 0 : memcpy(data, (char *) pheader + pkey->data, *buf_size);
6863 0 : db_unlock_database(hDB);
6864 0 : std::string path = db_get_path(hDB, hKey);
6865 0 : cm_msg(MERROR, "db_get_data", "data for key \"%s\" truncated from %d to %d bytes", path.c_str(), pkey_size, *buf_size);
6866 0 : return DB_TRUNCATED;
6867 0 : }
6868 :
6869 : /* copy key data */
6870 0 : memcpy(data, (char *) pheader + pkey->data, pkey->num_values * pkey->item_size);
6871 0 : *buf_size = pkey->num_values * pkey->item_size;
6872 0 : *num_values = pkey->num_values;
6873 :
6874 0 : db_unlock_database(hDB);
6875 :
6876 : }
6877 : #endif /* LOCAL_ROUTINES */
6878 :
6879 0 : return DB_SUCCESS;
6880 : }
6881 :
6882 : /**dox***************************************************************/
6883 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
6884 :
6885 : /********************************************************************/
6886 : /**
6887 : returns a single value of keys containing arrays of values.
6888 :
6889 : The function returns a single value of keys containing arrays of values.
6890 : @param hDB ODB handle obtained via cm_get_experiment_database().
6891 : @param hKey Handle for key where search starts, zero for root.
6892 : @param data Size of data buffer.
6893 : @param buf_size Return size of the record.
6894 : @param idx Index of array [0..n-1].
6895 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
6896 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED, DB_OUT_OF_RANGE
6897 : */
6898 58 : INT db_get_data_index(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT idx, DWORD type)
6899 : {
6900 58 : if (rpc_is_remote())
6901 0 : 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 58 : if (hDB > _database_entries || hDB <= 0) {
6909 0 : cm_msg(MERROR, "db_get_data", "Invalid database handle");
6910 0 : return DB_INVALID_HANDLE;
6911 : }
6912 :
6913 58 : if (!_database[hDB - 1].attached) {
6914 0 : cm_msg(MERROR, "db_get_data", "invalid database handle");
6915 0 : return DB_INVALID_HANDLE;
6916 : }
6917 :
6918 58 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
6919 0 : cm_msg(MERROR, "db_get_data", "invalid key handle");
6920 0 : return DB_INVALID_HANDLE;
6921 : }
6922 :
6923 58 : db_lock_database(hDB);
6924 :
6925 58 : pheader = _database[hDB - 1].database_header;
6926 :
6927 : /* check if hKey argument is correct */
6928 58 : if (!db_validate_hkey(pheader, hKey)) {
6929 0 : db_unlock_database(hDB);
6930 0 : return DB_INVALID_HANDLE;
6931 : }
6932 :
6933 58 : pkey = (KEY *) ((char *) pheader + hKey);
6934 :
6935 : /* check for read access */
6936 58 : if (!(pkey->access_mode & MODE_READ)) {
6937 0 : db_unlock_database(hDB);
6938 0 : return DB_NO_ACCESS;
6939 : }
6940 :
6941 58 : if (!pkey->type) {
6942 0 : int pkey_type = pkey->type;
6943 0 : db_unlock_database(hDB);
6944 0 : cm_msg(MERROR, "db_get_data_index", "hkey %d invalid key type %d", hKey, pkey_type);
6945 0 : return DB_INVALID_HANDLE;
6946 : }
6947 :
6948 58 : if (pkey->type != type) {
6949 0 : int pkey_type = pkey->type;
6950 : char pkey_name[NAME_LENGTH];
6951 0 : mstrlcpy(pkey_name, pkey->name, sizeof(pkey_name));
6952 0 : db_unlock_database(hDB);
6953 0 : 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 0 : return DB_TYPE_MISMATCH;
6955 : }
6956 :
6957 : /* keys cannot contain data */
6958 58 : if (pkey->type == TID_KEY) {
6959 0 : db_unlock_database(hDB);
6960 0 : cm_msg(MERROR, "db_get_data_index", "Key cannot contain data");
6961 0 : return DB_TYPE_MISMATCH;
6962 : }
6963 :
6964 : /* check if key has data */
6965 58 : if (pkey->data == 0) {
6966 0 : memset(data, 0, *buf_size);
6967 0 : *buf_size = 0;
6968 0 : db_unlock_database(hDB);
6969 0 : return DB_SUCCESS;
6970 : }
6971 :
6972 : /* check if index in range */
6973 58 : if (idx < 0 || idx >= pkey->num_values) {
6974 0 : int pkey_num_values = pkey->num_values;
6975 0 : memset(data, 0, *buf_size);
6976 0 : db_unlock_database(hDB);
6977 :
6978 0 : std::string path = db_get_path(hDB, hKey);
6979 0 : cm_msg(MERROR, "db_get_data_index", "index (%d) exceeds array length (%d) for key \"%s\"", idx, pkey_num_values, path.c_str());
6980 0 : return DB_OUT_OF_RANGE;
6981 0 : }
6982 :
6983 : /* check if buffer is too small */
6984 58 : if (pkey->item_size > *buf_size) {
6985 0 : int pkey_size = pkey->item_size;
6986 : /* copy data */
6987 0 : memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, *buf_size);
6988 0 : db_unlock_database(hDB);
6989 0 : std::string path = db_get_path(hDB, hKey);
6990 0 : 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 0 : return DB_TRUNCATED;
6992 0 : }
6993 :
6994 : /* copy key data */
6995 58 : memcpy(data, (char *) pheader + pkey->data + idx * pkey->item_size, pkey->item_size);
6996 58 : *buf_size = pkey->item_size;
6997 :
6998 58 : db_unlock_database(hDB);
6999 :
7000 : }
7001 : #endif /* LOCAL_ROUTINES */
7002 :
7003 58 : return DB_SUCCESS;
7004 : }
7005 :
7006 : #ifdef LOCAL_ROUTINES
7007 : /********************************************************************/
7008 : /**
7009 : Set key data, adjust number of values if previous data has different size.
7010 : @param pkey Key to change
7011 : @param idx Data index to change, "-1" means the whole array of data
7012 : @param data Buffer from which data gets copied to.
7013 : @param data_size Size of data buffer.
7014 : @param num_values Number of data values (for arrays).
7015 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
7016 : @return DB_SUCCESS, DB_FULL
7017 : */
7018 :
7019 163 : 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)
7020 : {
7021 : /* if no buf_size given (Java!), calculate it */
7022 163 : if (data_size == 0)
7023 0 : data_size = pkey->item_size * num_values;
7024 :
7025 : /* resize data size if necessary */
7026 163 : if (pkey->total_size != data_size) {
7027 : // FIXME: validate pkey->data!
7028 22 : pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, data_size, caller);
7029 :
7030 22 : if (pkey->data == 0) {
7031 0 : pkey->total_size = 0;
7032 0 : 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 0 : return DB_FULL;
7034 : }
7035 :
7036 22 : pkey->data -= (POINTER_T) pheader;
7037 22 : pkey->total_size = data_size;
7038 : }
7039 :
7040 : /* set number of values */
7041 163 : pkey->num_values = num_values;
7042 :
7043 163 : if (type == TID_STRING || type == TID_LINK)
7044 24 : pkey->item_size = data_size / num_values;
7045 : else
7046 139 : pkey->item_size = rpc_tid_size(type);
7047 :
7048 163 : if ((type == TID_STRING || type == TID_LINK) && pkey->num_values == 1) {
7049 : /* copy string up to NUL termination */
7050 24 : mstrlcpy((char *) pheader + pkey->data, (const char*)data, data_size);
7051 : } else {
7052 : /* copy data */
7053 139 : memcpy((char *) pheader + pkey->data, data, data_size);
7054 : }
7055 :
7056 : /* update time */
7057 163 : pkey->last_written = ss_time();
7058 :
7059 163 : return DB_SUCCESS;
7060 : }
7061 :
7062 4 : 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)
7063 : {
7064 : /* increase key size if necessary */
7065 4 : if (idx >= pkey->num_values || pkey->item_size == 0) {
7066 : // FIXME: validate pkey->data
7067 4 : pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, data_size * (idx + 1), caller);
7068 :
7069 4 : if (pkey->data == 0) {
7070 0 : pkey->total_size = 0;
7071 0 : pkey->num_values = 0;
7072 0 : 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 0 : return DB_FULL;
7074 : }
7075 :
7076 4 : pkey->data -= (POINTER_T) pheader;
7077 4 : if (!pkey->item_size)
7078 4 : pkey->item_size = data_size;
7079 4 : pkey->total_size = data_size * (idx + 1);
7080 4 : 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 4 : if ((type == TID_STRING || type == TID_LINK)) {
7093 : /* cut strings which are too long */
7094 4 : mstrlcpy((char *) pheader + pkey->data + idx * pkey->item_size, (char*)data, pkey->item_size);
7095 : } else {
7096 : /* copy data */
7097 0 : memcpy((char *) pheader + pkey->data + idx * pkey->item_size, data, pkey->item_size);
7098 : }
7099 :
7100 : /* update time */
7101 4 : pkey->last_written = ss_time();
7102 :
7103 4 : return DB_SUCCESS;
7104 : }
7105 :
7106 163 : 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)
7107 : {
7108 : /* check for write access */
7109 163 : if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7110 0 : return DB_NO_ACCESS;
7111 : }
7112 :
7113 163 : if (pkey->type != type) {
7114 0 : 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 0 : return DB_TYPE_MISMATCH;
7116 : }
7117 :
7118 : /* keys cannot contain data */
7119 163 : if (pkey->type == TID_KEY) {
7120 0 : db_msg(msg, MERROR, caller, "\"%s\" of type TID_KEY cannot contain data", db_get_path_locked(pheader, pkey).c_str());
7121 0 : return DB_TYPE_MISMATCH;
7122 : }
7123 :
7124 163 : if (type == TID_STRING || type == TID_LINK) {
7125 24 : if (num_values > 1) {
7126 0 : int item_size = pkey->item_size;
7127 0 : if (data_size > 0 && num_values > 0)
7128 0 : 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 0 : for (int i=0; i<num_values; i++) {
7131 0 : 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 0 : if (!is_utf8(value)) {
7134 0 : 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 24 : 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 24 : if (!is_utf8(value)) {
7143 0 : 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 163 : return DB_SUCCESS;
7151 : }
7152 :
7153 4 : 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)
7154 : {
7155 : /* check for write access */
7156 4 : if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7157 0 : return DB_NO_ACCESS;
7158 : }
7159 :
7160 4 : if (pkey->type != type) {
7161 0 : 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 0 : return DB_TYPE_MISMATCH;
7163 : }
7164 :
7165 : /* keys cannot contain data */
7166 4 : if (pkey->type == TID_KEY) {
7167 0 : 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 0 : return DB_TYPE_MISMATCH;
7169 : }
7170 :
7171 : /* check utf-8 encoding */
7172 4 : 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 4 : const char* value = (const char*)data;
7175 4 : if (!is_utf8(value)) {
7176 0 : 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 4 : if (idx < 0) {
7184 0 : db_msg(msg, MERROR, caller, "\%s\" given invalid index %d", db_get_path_locked(pheader, pkey).c_str(), idx);
7185 0 : 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 4 : if (pkey->item_size != 0 && data_size != pkey->item_size) {
7191 0 : 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 0 : return DB_TYPE_MISMATCH;
7193 : }
7194 :
7195 4 : return DB_SUCCESS;
7196 : }
7197 :
7198 : #endif // LOCAL_ROUTINES
7199 :
7200 : /********************************************************************/
7201 : /**
7202 : Set key data from a handle. Adjust number of values if
7203 : previous data has different size.
7204 : \code
7205 : HNLDE hkey;
7206 : INT run_number;
7207 : // get key handle for run number
7208 : db_find_key(hDB, 0, "/Runinfo/Run number", &hkey);
7209 : // set run number
7210 : db_set_data(hDB, hkey, &run_number, sizeof(run_number),TID_INT32);
7211 : \endcode
7212 : @param hDB ODB handle obtained via cm_get_experiment_database().
7213 : @param hKey Handle for key where search starts, zero for root.
7214 : @param data Buffer from which data gets copied to.
7215 : @param buf_size Size of data buffer.
7216 : @param num_values Number of data values (for arrays).
7217 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
7218 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED
7219 : */
7220 6 : INT db_set_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7221 : {
7222 6 : if (rpc_is_remote())
7223 0 : 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;
7229 : HNDLE hkeylink;
7230 : int link_idx;
7231 : char link_name[256];
7232 : int status;
7233 :
7234 6 : if (hDB > _database_entries || hDB <= 0) {
7235 0 : cm_msg(MERROR, "db_set_data", "invalid database handle");
7236 0 : return DB_INVALID_HANDLE;
7237 : }
7238 :
7239 6 : if (!_database[hDB - 1].attached) {
7240 0 : cm_msg(MERROR, "db_set_data", "invalid database handle");
7241 0 : return DB_INVALID_HANDLE;
7242 : }
7243 :
7244 6 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
7245 0 : cm_msg(MERROR, "db_set_data", "invalid key handle");
7246 0 : return DB_INVALID_HANDLE;
7247 : }
7248 :
7249 6 : if (num_values == 0)
7250 0 : return DB_INVALID_PARAM;
7251 :
7252 6 : db_lock_database(hDB);
7253 6 : db_err_msg* msg = NULL;
7254 :
7255 6 : pheader = _database[hDB - 1].database_header;
7256 :
7257 : /* check if hKey argument is correct */
7258 6 : if (!db_validate_hkey(pheader, hKey)) {
7259 0 : db_unlock_database(hDB);
7260 0 : return DB_INVALID_HANDLE;
7261 : }
7262 :
7263 6 : pkey = (KEY *) ((char *) pheader + hKey);
7264 :
7265 : /* check for write access */
7266 6 : if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7267 0 : db_unlock_database(hDB);
7268 0 : return DB_NO_ACCESS;
7269 : }
7270 :
7271 : /* check for link to array index */
7272 6 : if (pkey->type == TID_LINK) {
7273 0 : mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7274 0 : if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7275 0 : db_unlock_database(hDB);
7276 0 : if (strchr(link_name, '[') == NULL)
7277 0 : return DB_INVALID_LINK;
7278 0 : link_idx = atoi(strchr(link_name, '[') + 1);
7279 0 : *strchr(link_name, '[') = 0;
7280 0 : if (db_find_key(hDB, 0, link_name, &hkeylink) != DB_SUCCESS)
7281 0 : return DB_INVALID_LINK;
7282 0 : return db_set_data_index(hDB, hkeylink, data, buf_size, link_idx, type);
7283 : }
7284 : }
7285 :
7286 6 : status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_data", &msg);
7287 :
7288 6 : if (status != DB_SUCCESS) {
7289 0 : db_unlock_database(hDB);
7290 0 : if (msg)
7291 0 : db_flush_msg(&msg);
7292 0 : return status;
7293 : }
7294 :
7295 6 : db_allow_write_locked(&_database[hDB-1], "db_set_data");
7296 :
7297 6 : status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_data", &msg);
7298 :
7299 6 : if (status != DB_SUCCESS) {
7300 0 : db_unlock_database(hDB);
7301 0 : if (msg)
7302 0 : db_flush_msg(&msg);
7303 0 : return status;
7304 : }
7305 :
7306 6 : db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
7307 6 : db_unlock_database(hDB);
7308 6 : if (msg)
7309 0 : db_flush_msg(&msg);
7310 :
7311 :
7312 : }
7313 : #endif /* LOCAL_ROUTINES */
7314 :
7315 6 : return DB_SUCCESS;
7316 : }
7317 :
7318 0 : INT 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 0 : if (rpc_is_remote())
7326 0 : 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;
7332 : HNDLE hkeylink;
7333 : int link_idx;
7334 : char link_name[256];
7335 : int status;
7336 :
7337 0 : if (hDB > _database_entries || hDB <= 0) {
7338 0 : cm_msg(MERROR, "db_set_data1", "invalid database handle");
7339 0 : return DB_INVALID_HANDLE;
7340 : }
7341 :
7342 0 : if (!_database[hDB - 1].attached) {
7343 0 : cm_msg(MERROR, "db_set_data1", "invalid database handle");
7344 0 : return DB_INVALID_HANDLE;
7345 : }
7346 :
7347 0 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
7348 0 : cm_msg(MERROR, "db_set_data1", "invalid key handle");
7349 0 : return DB_INVALID_HANDLE;
7350 : }
7351 :
7352 0 : if (num_values == 0)
7353 0 : return DB_INVALID_PARAM;
7354 :
7355 0 : db_lock_database(hDB);
7356 0 : db_err_msg* msg = NULL;
7357 :
7358 0 : pheader = _database[hDB - 1].database_header;
7359 :
7360 : /* check if hKey argument is correct */
7361 0 : if (!db_validate_hkey(pheader, hKey)) {
7362 0 : db_unlock_database(hDB);
7363 0 : return DB_INVALID_HANDLE;
7364 : }
7365 :
7366 0 : pkey = (KEY *) ((char *) pheader + hKey);
7367 :
7368 : /* check for write access */
7369 0 : if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7370 0 : db_unlock_database(hDB);
7371 0 : return DB_NO_ACCESS;
7372 : }
7373 :
7374 : /* check for link to array index */
7375 0 : if (pkey->type == TID_LINK) {
7376 0 : mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7377 0 : if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7378 0 : db_unlock_database(hDB);
7379 0 : if (strchr(link_name, '[') == NULL)
7380 0 : return DB_INVALID_LINK;
7381 0 : link_idx = atoi(strchr(link_name, '[') + 1);
7382 0 : *strchr(link_name, '[') = 0;
7383 0 : if (db_find_key(hDB, 0, link_name, &hkeylink) != DB_SUCCESS)
7384 0 : return DB_INVALID_LINK;
7385 0 : return db_set_data_index1(hDB, hkeylink, data, buf_size, link_idx, type, FALSE);
7386 : }
7387 : }
7388 :
7389 0 : status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_data1", &msg);
7390 :
7391 0 : if (status != DB_SUCCESS) {
7392 0 : db_unlock_database(hDB);
7393 0 : if (msg)
7394 0 : db_flush_msg(&msg);
7395 0 : return status;
7396 : }
7397 :
7398 0 : db_allow_write_locked(&_database[hDB - 1], "db_set_data1");
7399 :
7400 0 : status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_data1", &msg);
7401 :
7402 0 : if (status != DB_SUCCESS) {
7403 0 : db_unlock_database(hDB);
7404 0 : if (msg)
7405 0 : db_flush_msg(&msg);
7406 0 : return status;
7407 : }
7408 :
7409 0 : db_unlock_database(hDB);
7410 0 : if (msg)
7411 0 : db_flush_msg(&msg);
7412 :
7413 : }
7414 : #endif /* LOCAL_ROUTINES */
7415 :
7416 0 : return DB_SUCCESS;
7417 : }
7418 :
7419 : /********************************************************************/
7420 : /**
7421 : Same as db_set_data, but it does not follow a link to an array index
7422 : @param hDB ODB handle obtained via cm_get_experiment_database().
7423 : @param hKey Handle for key where search starts, zero for root.
7424 : @param data Buffer from which data gets copied to.
7425 : @param buf_size Size of data buffer.
7426 : @param num_values Number of data values (for arrays).
7427 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
7428 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TRUNCATED
7429 : */
7430 38 : INT db_set_link_data(HNDLE hDB, HNDLE hKey, const void *data, INT buf_size, INT num_values, DWORD type)
7431 : {
7432 38 : if (rpc_is_remote())
7433 0 : 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 38 : if (hDB > _database_entries || hDB <= 0) {
7442 0 : cm_msg(MERROR, "db_set_data", "invalid database handle");
7443 0 : return DB_INVALID_HANDLE;
7444 : }
7445 :
7446 38 : if (!_database[hDB - 1].attached) {
7447 0 : cm_msg(MERROR, "db_set_data", "invalid database handle");
7448 0 : return DB_INVALID_HANDLE;
7449 : }
7450 :
7451 38 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
7452 0 : cm_msg(MERROR, "db_set_data", "invalid key handle");
7453 0 : return DB_INVALID_HANDLE;
7454 : }
7455 :
7456 38 : if (num_values == 0)
7457 0 : return DB_INVALID_PARAM;
7458 :
7459 38 : db_lock_database(hDB);
7460 38 : db_err_msg* msg = NULL;
7461 :
7462 38 : pheader = _database[hDB - 1].database_header;
7463 :
7464 : /* check if hKey argument is correct */
7465 38 : if (!db_validate_hkey(pheader, hKey)) {
7466 0 : db_unlock_database(hDB);
7467 0 : return DB_INVALID_HANDLE;
7468 : }
7469 :
7470 38 : pkey = (KEY *) ((char *) pheader + hKey);
7471 :
7472 38 : status = db_check_set_data_locked(pheader, pkey, data, buf_size, num_values, type, "db_set_link_data", &msg);
7473 :
7474 38 : if (status != DB_SUCCESS) {
7475 0 : db_unlock_database(hDB);
7476 0 : if (msg)
7477 0 : db_flush_msg(&msg);
7478 0 : return status;
7479 : }
7480 :
7481 38 : db_allow_write_locked(&_database[hDB - 1], "db_set_link_data");
7482 :
7483 38 : status = db_set_data_wlocked(pheader, pkey, data, buf_size, num_values, type, "db_set_link_data", &msg);
7484 :
7485 38 : if (status != DB_SUCCESS) {
7486 0 : db_unlock_database(hDB);
7487 0 : if (msg)
7488 0 : db_flush_msg(&msg);
7489 0 : return status;
7490 : }
7491 :
7492 38 : db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
7493 38 : db_unlock_database(hDB);
7494 38 : if (msg)
7495 0 : db_flush_msg(&msg);
7496 :
7497 : }
7498 : #endif /* LOCAL_ROUTINES */
7499 :
7500 38 : return DB_SUCCESS;
7501 : }
7502 :
7503 : /**dox***************************************************************/
7504 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
7505 :
7506 : /*------------------------------------------------------------------*/
7507 1 : INT db_set_num_values(HNDLE hDB, HNDLE hKey, INT num_values)
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 1 : if (rpc_is_remote())
7529 0 : 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 1 : if (hDB > _database_entries || hDB <= 0) {
7538 0 : cm_msg(MERROR, "db_set_num_values", "invalid database handle");
7539 0 : return DB_INVALID_HANDLE;
7540 : }
7541 :
7542 1 : if (!_database[hDB - 1].attached) {
7543 0 : cm_msg(MERROR, "db_set_num_values", "invalid database handle");
7544 0 : return DB_INVALID_HANDLE;
7545 : }
7546 :
7547 1 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
7548 0 : cm_msg(MERROR, "db_set_num_values", "invalid key handle");
7549 0 : return DB_INVALID_HANDLE;
7550 : }
7551 :
7552 1 : if (num_values <= 0) {
7553 0 : cm_msg(MERROR, "db_set_num_values", "invalid num_values %d", num_values);
7554 0 : return DB_INVALID_PARAM;
7555 : }
7556 :
7557 1 : if (num_values == 0)
7558 0 : return DB_INVALID_PARAM;
7559 :
7560 1 : db_lock_database(hDB);
7561 1 : db_err_msg* msg = NULL;
7562 :
7563 1 : pheader = _database[hDB - 1].database_header;
7564 :
7565 : /* check if hKey argument is correct */
7566 1 : if (!db_validate_hkey(pheader, hKey)) {
7567 0 : db_unlock_database(hDB);
7568 0 : return DB_INVALID_HANDLE;
7569 : }
7570 :
7571 1 : pkey = (KEY *) ((char *) pheader + hKey);
7572 :
7573 : /* check for write access */
7574 1 : if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7575 0 : db_unlock_database(hDB);
7576 0 : return DB_NO_ACCESS;
7577 : }
7578 :
7579 : /* keys cannot contain data */
7580 1 : if (pkey->type == TID_KEY) {
7581 0 : db_unlock_database(hDB);
7582 0 : cm_msg(MERROR, "db_set_num_values", "Key cannot contain data");
7583 0 : return DB_TYPE_MISMATCH;
7584 : }
7585 :
7586 1 : if (pkey->total_size != pkey->item_size * pkey->num_values) {
7587 0 : db_unlock_database(hDB);
7588 0 : cm_msg(MERROR, "db_set_num_values", "Corrupted key");
7589 0 : return DB_CORRUPTED;
7590 : }
7591 :
7592 1 : if (pkey->item_size == 0) {
7593 0 : db_unlock_database(hDB);
7594 0 : cm_msg(MERROR, "db_set_num_values", "Cannot resize array with item_size equal to zero");
7595 0 : return DB_INVALID_PARAM;
7596 : }
7597 :
7598 1 : db_allow_write_locked(&_database[hDB - 1], "db_set_num_values");
7599 :
7600 : /* resize data size if necessary */
7601 1 : if (pkey->num_values != num_values) {
7602 1 : new_size = pkey->item_size * num_values;
7603 :
7604 1 : pkey->data = (POINTER_T) realloc_data(pheader, (char *) pheader + pkey->data, pkey->total_size, new_size, "db_set_num_values");
7605 :
7606 1 : if (pkey->data == 0) {
7607 0 : pkey->total_size = 0;
7608 0 : pkey->num_values = 0;
7609 0 : 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);
7610 0 : db_unlock_database(hDB);
7611 0 : if (msg)
7612 0 : db_flush_msg(&msg);
7613 0 : return DB_FULL;
7614 : }
7615 :
7616 1 : pkey->data -= (POINTER_T) pheader;
7617 1 : pkey->total_size = new_size;
7618 1 : pkey->num_values = num_values;
7619 : }
7620 :
7621 : /* update time */
7622 1 : pkey->last_written = ss_time();
7623 :
7624 1 : db_notify_clients_locked(pheader, hDB, hKey, -1, TRUE, &msg);
7625 1 : db_unlock_database(hDB);
7626 1 : if (msg)
7627 0 : db_flush_msg(&msg);
7628 :
7629 : }
7630 : #endif /* LOCAL_ROUTINES */
7631 :
7632 1 : return DB_SUCCESS;
7633 : }
7634 :
7635 : /**dox***************************************************************/
7636 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
7637 :
7638 : /********************************************************************/
7639 : /**
7640 : Set key data for a key which contains an array of values.
7641 :
7642 : This function sets individual values of a key containing an array.
7643 : If the index is larger than the array size, the array is extended and the intermediate
7644 : values are set to zero.
7645 : @param hDB ODB handle obtained via cm_get_experiment_database().
7646 : @param hKey Handle for key where search starts, zero for root.
7647 : @param data Pointer to single value of data.
7648 : @param data_size
7649 : @param idx Size of single data element.
7650 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
7651 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH
7652 : */
7653 4 : INT db_set_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
7654 : {
7655 4 : if (rpc_is_remote())
7656 0 : 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;
7664 : HNDLE hkeylink;
7665 : int status;
7666 :
7667 4 : if (hDB > _database_entries || hDB <= 0) {
7668 0 : cm_msg(MERROR, "db_set_data_index", "invalid database handle");
7669 0 : return DB_INVALID_HANDLE;
7670 : }
7671 :
7672 4 : if (!_database[hDB - 1].attached) {
7673 0 : cm_msg(MERROR, "db_set_data_index", "invalid database handle");
7674 0 : return DB_INVALID_HANDLE;
7675 : }
7676 :
7677 4 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
7678 0 : cm_msg(MERROR, "db_set_data_index", "invalid key handle");
7679 0 : return DB_INVALID_HANDLE;
7680 : }
7681 :
7682 4 : db_lock_database(hDB);
7683 4 : db_err_msg* msg = NULL;
7684 :
7685 4 : pheader = _database[hDB - 1].database_header;
7686 :
7687 : /* check if hKey argument is correct */
7688 4 : if (!db_validate_hkey(pheader, hKey)) {
7689 0 : db_unlock_database(hDB);
7690 0 : return DB_INVALID_HANDLE;
7691 : }
7692 :
7693 4 : pkey = (KEY *) ((char *) pheader + hKey);
7694 :
7695 : /* check for write access */
7696 4 : if (!(pkey->access_mode & MODE_WRITE) || (pkey->access_mode & MODE_EXCLUSIVE)) {
7697 0 : db_unlock_database(hDB);
7698 0 : return DB_NO_ACCESS;
7699 : }
7700 :
7701 : /* check for link to array index */
7702 4 : if (pkey->type == TID_LINK) {
7703 0 : mstrlcpy(link_name, (char *) pheader + pkey->data, sizeof(link_name));
7704 0 : if (strlen(link_name) > 0 && link_name[strlen(link_name) - 1] == ']') {
7705 0 : db_unlock_database(hDB);
7706 0 : if (strchr(link_name, '[') == NULL)
7707 0 : return DB_INVALID_LINK;
7708 0 : link_idx = atoi(strchr(link_name, '[') + 1);
7709 0 : *strchr(link_name, '[') = 0;
7710 0 : if (db_find_key(hDB, 0, link_name, &hkeylink) != DB_SUCCESS)
7711 0 : return DB_INVALID_LINK;
7712 0 : return db_set_data_index(hDB, hkeylink, data, data_size, link_idx, type);
7713 : }
7714 : }
7715 :
7716 4 : status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_data_index", &msg);
7717 :
7718 4 : if (status != DB_SUCCESS) {
7719 0 : db_unlock_database(hDB);
7720 0 : if (msg)
7721 0 : db_flush_msg(&msg);
7722 0 : return status;
7723 : }
7724 :
7725 4 : db_allow_write_locked(&_database[hDB-1], "db_set_data_index");
7726 :
7727 4 : status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_data_index", &msg);
7728 :
7729 4 : if (status != DB_SUCCESS) {
7730 0 : db_unlock_database(hDB);
7731 0 : if (msg)
7732 0 : db_flush_msg(&msg);
7733 0 : return status;
7734 : }
7735 :
7736 4 : db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
7737 4 : db_unlock_database(hDB);
7738 4 : if (msg)
7739 0 : db_flush_msg(&msg);
7740 :
7741 : }
7742 : #endif /* LOCAL_ROUTINES */
7743 :
7744 4 : return DB_SUCCESS;
7745 : }
7746 :
7747 : /********************************************************************/
7748 : /**
7749 : Same as db_set_data_index, but does not follow links.
7750 :
7751 : @param hDB ODB handle obtained via cm_get_experiment_database().
7752 : @param hKey Handle for key where search starts, zero for root.
7753 : @param data Pointer to single value of data.
7754 : @param data_size
7755 : @param idx Size of single data element.
7756 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
7757 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_ACCESS, DB_TYPE_MISMATCH
7758 : */
7759 0 : INT db_set_link_data_index(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type)
7760 : {
7761 0 : if (rpc_is_remote())
7762 0 : 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 0 : if (hDB > _database_entries || hDB <= 0) {
7771 0 : cm_msg(MERROR, "db_set_link_data_index", "invalid database handle");
7772 0 : return DB_INVALID_HANDLE;
7773 : }
7774 :
7775 0 : if (!_database[hDB - 1].attached) {
7776 0 : cm_msg(MERROR, "db_set_link_data_index", "invalid database handle");
7777 0 : return DB_INVALID_HANDLE;
7778 : }
7779 :
7780 0 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
7781 0 : cm_msg(MERROR, "db_set_link_data_index", "invalid key handle");
7782 0 : return DB_INVALID_HANDLE;
7783 : }
7784 :
7785 0 : db_lock_database(hDB);
7786 0 : db_err_msg* msg = NULL;
7787 :
7788 0 : pheader = _database[hDB - 1].database_header;
7789 :
7790 : /* check if hKey argument is correct */
7791 0 : if (!db_validate_hkey(pheader, hKey)) {
7792 0 : db_unlock_database(hDB);
7793 0 : return DB_INVALID_HANDLE;
7794 : }
7795 :
7796 0 : pkey = (KEY *) ((char *) pheader + hKey);
7797 :
7798 0 : status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_link_data_index", &msg);
7799 :
7800 0 : if (status != DB_SUCCESS) {
7801 0 : db_unlock_database(hDB);
7802 0 : if (msg)
7803 0 : db_flush_msg(&msg);
7804 0 : return status;
7805 : }
7806 :
7807 0 : db_allow_write_locked(&_database[hDB - 1], "db_set_link_data_index");
7808 :
7809 0 : status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_link_data_index", &msg);
7810 :
7811 0 : if (status != DB_SUCCESS) {
7812 0 : db_unlock_database(hDB);
7813 0 : if (msg)
7814 0 : db_flush_msg(&msg);
7815 0 : return status;
7816 : }
7817 :
7818 0 : db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
7819 0 : db_unlock_database(hDB);
7820 0 : if (msg)
7821 0 : db_flush_msg(&msg);
7822 :
7823 : }
7824 : #endif /* LOCAL_ROUTINES */
7825 :
7826 0 : return DB_SUCCESS;
7827 : }
7828 :
7829 : /**dox***************************************************************/
7830 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
7831 :
7832 : /*------------------------------------------------------------------*/
7833 0 : INT db_set_data_index1(HNDLE hDB, HNDLE hKey, const void *data, INT data_size, INT idx, DWORD type, BOOL bNotify)
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 0 : if (rpc_is_remote())
7862 0 : 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 0 : if (hDB > _database_entries || hDB <= 0) {
7871 0 : cm_msg(MERROR, "db_set_data_index1", "invalid database handle");
7872 0 : return DB_INVALID_HANDLE;
7873 : }
7874 :
7875 0 : if (!_database[hDB - 1].attached) {
7876 0 : cm_msg(MERROR, "db_set_data_index1", "invalid database handle");
7877 0 : return DB_INVALID_HANDLE;
7878 : }
7879 :
7880 0 : if (hKey < (int) sizeof(DATABASE_HEADER)) {
7881 0 : cm_msg(MERROR, "db_set_data_index1", "invalid key handle");
7882 0 : return DB_INVALID_HANDLE;
7883 : }
7884 :
7885 0 : db_lock_database(hDB);
7886 0 : db_err_msg* msg = NULL;
7887 :
7888 0 : pheader = _database[hDB - 1].database_header;
7889 :
7890 : /* check if hKey argument is correct */
7891 0 : if (!db_validate_hkey(pheader, hKey)) {
7892 0 : db_unlock_database(hDB);
7893 0 : return DB_INVALID_HANDLE;
7894 : }
7895 :
7896 0 : pkey = (KEY *) ((char *) pheader + hKey);
7897 :
7898 0 : status = db_check_set_data_index_locked(pheader, pkey, idx, data, data_size, type, "db_set_data_index1", &msg);
7899 :
7900 0 : if (status != DB_SUCCESS) {
7901 0 : db_unlock_database(hDB);
7902 0 : if (msg)
7903 0 : db_flush_msg(&msg);
7904 0 : return status;
7905 : }
7906 :
7907 0 : db_allow_write_locked(&_database[hDB - 1], "db_set_data_index1");
7908 :
7909 0 : status = db_set_data_index_wlocked(pheader, pkey, idx, data, data_size, type, "db_set_data_index1", &msg);
7910 :
7911 0 : if (status != DB_SUCCESS) {
7912 0 : db_unlock_database(hDB);
7913 0 : if (msg)
7914 0 : db_flush_msg(&msg);
7915 0 : return status;
7916 : }
7917 :
7918 0 : if (bNotify)
7919 0 : db_notify_clients_locked(pheader, hDB, hKey, idx, TRUE, &msg);
7920 :
7921 0 : db_unlock_database(hDB);
7922 0 : if (msg)
7923 0 : db_flush_msg(&msg);
7924 :
7925 : }
7926 : #endif /* LOCAL_ROUTINES */
7927 :
7928 0 : return DB_SUCCESS;
7929 : }
7930 :
7931 : /*----------------------------------------------------------------------------*/
7932 :
7933 0 : INT 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;
7960 : INT status, old_size;
7961 :
7962 0 : if (num_values == 0)
7963 0 : return DB_INVALID_PARAM;
7964 :
7965 0 : status = db_find_key(hDB, hKeyRoot, name, &hKey);
7966 0 : if (status != DB_SUCCESS) {
7967 0 : db_create_key(hDB, hKeyRoot, name, type);
7968 0 : status = db_find_key(hDB, hKeyRoot, name, &hKey);
7969 0 : if (status != DB_SUCCESS)
7970 0 : return status;
7971 0 : status = db_set_data(hDB, hKey, data, data_size, num_values, type);
7972 : } else {
7973 0 : old_size = data_size;
7974 0 : db_get_data(hDB, hKey, data, &old_size, type);
7975 0 : status = db_set_data(hDB, hKey, data, data_size, num_values, type);
7976 : }
7977 :
7978 0 : return status;
7979 : }
7980 :
7981 : #ifdef LOCAL_ROUTINES
7982 :
7983 : /*------------------------------------------------------------------*/
7984 164 : static int db_set_mode_wlocked(DATABASE_HEADER *pheader, KEY *pkey, WORD mode, int recurse, db_err_msg** msg)
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 164 : if (pkey->type == TID_LINK) {
8007 : int status;
8008 0 : pkey = (KEY*)db_resolve_link_locked(pheader, pkey, &status, msg);
8009 0 : if (!pkey) {
8010 0 : return status;
8011 : }
8012 : }
8013 :
8014 164 : if (pkey->type == TID_KEY && recurse) {
8015 : // drop "const" from KEY* we are permitted to write to ODB!
8016 32 : KEY* psubkey = (KEY*)db_enum_first_locked(pheader, pkey, msg);
8017 174 : while (psubkey) {
8018 142 : db_set_mode_wlocked(pheader, psubkey, mode, recurse+1, msg);
8019 142 : psubkey = (KEY*)db_enum_next_locked(pheader, pkey, psubkey, msg);
8020 : }
8021 : }
8022 :
8023 : /* now set mode */
8024 164 : pkey->access_mode = mode;
8025 :
8026 164 : return DB_SUCCESS;
8027 : }
8028 :
8029 : #endif
8030 :
8031 : /*------------------------------------------------------------------*/
8032 20 : INT db_set_mode(HNDLE hDB, HNDLE hKey, WORD mode, BOOL recurse)
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 20 : if (rpc_is_remote())
8059 0 : return rpc_call(RPC_DB_SET_MODE, hDB, hKey, mode, recurse);
8060 :
8061 : #ifdef LOCAL_ROUTINES
8062 : {
8063 : DATABASE_HEADER *pheader;
8064 20 : BOOL locked = FALSE;
8065 :
8066 20 : if (hDB > _database_entries || hDB <= 0) {
8067 0 : cm_msg(MERROR, "db_set_mode", "invalid database handle");
8068 0 : return DB_INVALID_HANDLE;
8069 : }
8070 :
8071 20 : if (!_database[hDB - 1].attached) {
8072 0 : cm_msg(MERROR, "db_set_mode", "invalid database handle");
8073 0 : return DB_INVALID_HANDLE;
8074 : }
8075 :
8076 20 : if (recurse < 2) {
8077 20 : db_lock_database(hDB);
8078 20 : locked = TRUE;
8079 : }
8080 :
8081 20 : pheader = _database[hDB - 1].database_header;
8082 :
8083 20 : db_err_msg* msg = NULL;
8084 20 : int status = 0;
8085 :
8086 20 : KEY *pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_set_mode", &msg);
8087 :
8088 20 : if (!pkey) {
8089 0 : if (locked) {
8090 0 : db_unlock_database(hDB);
8091 0 : if (msg)
8092 0 : db_flush_msg(&msg);
8093 0 : return status;
8094 : }
8095 : }
8096 :
8097 20 : db_allow_write_locked(&_database[hDB-1], "db_set_mode");
8098 :
8099 20 : status = db_set_mode_wlocked(pheader, pkey, mode, recurse, &msg);
8100 :
8101 20 : if (locked) {
8102 20 : db_unlock_database(hDB);
8103 20 : if (msg)
8104 0 : db_flush_msg(&msg);
8105 : }
8106 :
8107 20 : return status;
8108 : }
8109 : #endif /* LOCAL_ROUTINES */
8110 :
8111 : return DB_SUCCESS;
8112 : }
8113 :
8114 : /**dox***************************************************************/
8115 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
8116 :
8117 : /********************************************************************/
8118 : /**
8119 : Load a branch of a database from an .ODB file.
8120 :
8121 : This function is used by the ODBEdit command load. For a
8122 : description of the ASCII format, see db_copy(). Data can be loaded relative to
8123 : the root of the ODB (hkey equal zero) or relative to a certain key.
8124 : @param hDB ODB handle obtained via cm_get_experiment_database().
8125 : @param hKeyRoot Handle for key where search starts, zero for root.
8126 : @param filename Filename of .ODB file.
8127 : @param bRemote If TRUE, the file is loaded by the server process on the
8128 : back-end, if FALSE, it is loaded from the current process
8129 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FILE_ERROR
8130 : */
8131 0 : INT 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 0 : if (rpc_is_remote() && bRemote)
8138 0 : return rpc_call(RPC_DB_LOAD, hDB, hKeyRoot, filename);
8139 :
8140 : /* open file */
8141 0 : hfile = open(filename, O_RDONLY | O_TEXT, 0644);
8142 0 : if (hfile == -1) {
8143 0 : cm_msg(MERROR, "db_load", "file \"%s\" not found", filename);
8144 0 : return DB_FILE_ERROR;
8145 : }
8146 :
8147 : /* allocate buffer with file size */
8148 0 : fstat(hfile, &stat_buf);
8149 0 : size = stat_buf.st_size;
8150 0 : buffer = (char *) malloc(size + 1);
8151 :
8152 0 : if (buffer == NULL) {
8153 0 : cm_msg(MERROR, "db_load", "cannot allocate ODB load buffer");
8154 0 : close(hfile);
8155 0 : return DB_NO_MEMORY;
8156 : }
8157 :
8158 0 : n = 0;
8159 :
8160 : do {
8161 0 : i = read(hfile, buffer + n, size - n);
8162 0 : if (i <= 0)
8163 0 : break;
8164 0 : n += i;
8165 : } while (TRUE);
8166 :
8167 0 : buffer[n] = 0;
8168 :
8169 0 : if (strncmp(buffer, "<?xml version=\"1.0\"", 19) == 0) {
8170 0 : status = db_paste_xml(hDB, hKeyRoot, buffer);
8171 0 : if (status != DB_SUCCESS)
8172 0 : printf("Error in file \"%s\"\n", filename);
8173 0 : } else if( buffer[0] == '{'){
8174 0 : if(strrchr(buffer, '}')){
8175 0 : status = db_paste_json(hDB, hKeyRoot, buffer);
8176 : } else {
8177 0 : status = DB_FILE_ERROR;
8178 : }
8179 : } else
8180 0 : status = db_paste(hDB, hKeyRoot, buffer);
8181 :
8182 0 : close(hfile);
8183 0 : free(buffer);
8184 :
8185 0 : return status;
8186 : }
8187 :
8188 : /********************************************************************/
8189 : /**
8190 : Copy an ODB subtree in ASCII format to a buffer
8191 :
8192 : This function converts the binary ODB contents to an ASCII.
8193 : The function db_paste() can be used to convert the ASCII representation back
8194 : to binary ODB contents. The functions db_load() and db_save() internally
8195 : use db_copy() and db_paste(). This function converts the binary ODB
8196 : contents to an ASCII representation of the form:
8197 : - For single value:
8198 : \code
8199 : [ODB path]
8200 : key name = type : value
8201 : \endcode
8202 : - For strings:
8203 : \code
8204 : key name = STRING : [size] string contents
8205 : \endcode
8206 : - For arrayes (type can be BYTE, SBYTE, CHAR, WORD, SHORT, DWORD,
8207 : INT, BOOL, FLOAT, DOUBLE, STRING or LINK):
8208 : \code
8209 : key name = type[size] :
8210 : [0] value0
8211 : [1] value1
8212 : [2] value2
8213 : ...
8214 : \endcode
8215 : @param hDB ODB handle obtained via cm_get_experiment_database().
8216 : @param hKey Handle for key where search starts, zero for root.
8217 : @param buffer ASCII buffer which receives ODB contents.
8218 : @param buffer_size Size of buffer, returns remaining space in buffer.
8219 : @param path Internal use only, must be empty ("").
8220 : @return DB_SUCCESS, DB_TRUNCATED, DB_NO_MEMORY
8221 : */
8222 4 : INT 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 4 : mstrlcpy(full_path, path, sizeof(full_path));
8233 :
8234 4 : bWritten = FALSE;
8235 :
8236 : /* first enumerate this level */
8237 4 : for (i = 0;; i++) {
8238 24 : db_enum_link(hDB, hKey, i, &hSubkey);
8239 :
8240 24 : if (i == 0 && !hSubkey) {
8241 : /* If key has no subkeys, just write this key */
8242 0 : status = db_get_link(hDB, hKey, &key);
8243 0 : if (status != DB_SUCCESS)
8244 0 : continue;
8245 0 : size = key.total_size;
8246 0 : data = (char *) malloc(size);
8247 0 : if (data == NULL) {
8248 0 : cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
8249 0 : return DB_NO_MEMORY;
8250 : }
8251 0 : line[0] = 0;
8252 :
8253 0 : if (key.type != TID_KEY) {
8254 0 : status = db_get_link_data(hDB, hKey, data, &size, key.type);
8255 0 : if (status != DB_SUCCESS)
8256 0 : continue;
8257 0 : if (key.num_values == 1) {
8258 0 : sprintf(line, "%s = %s : ", key.name, rpc_tid_name(key.type));
8259 :
8260 0 : if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
8261 : /* multiline string */
8262 0 : sprintf(line + strlen(line), "[====#$@$#====]\n");
8263 :
8264 : /* copy line to buffer */
8265 0 : if ((INT) (strlen(line) + 1) > *buffer_size) {
8266 0 : free(data);
8267 0 : return DB_TRUNCATED;
8268 : }
8269 :
8270 0 : strcpy(buffer, line);
8271 0 : buffer += strlen(line);
8272 0 : *buffer_size -= strlen(line);
8273 :
8274 : /* copy multiple lines to buffer */
8275 0 : if (key.item_size > *buffer_size) {
8276 0 : free(data);
8277 0 : return DB_TRUNCATED;
8278 : }
8279 :
8280 0 : strcpy(buffer, data);
8281 0 : buffer += strlen(data);
8282 0 : *buffer_size -= strlen(data);
8283 :
8284 0 : strcpy(line, "\n====#$@$#====\n");
8285 : } else {
8286 0 : std::string str = db_sprintf(data, key.item_size, 0, key.type);
8287 :
8288 0 : if (key.type == TID_STRING || key.type == TID_LINK)
8289 0 : sprintf(line + strlen(line), "[%d] ", key.item_size);
8290 :
8291 0 : sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8292 0 : }
8293 : } else {
8294 0 : sprintf(line, "%s = %s[%d] :\n", key.name, rpc_tid_name(key.type), key.num_values);
8295 :
8296 0 : for (j = 0; j < key.num_values; j++) {
8297 0 : if (key.type == TID_STRING || key.type == TID_LINK)
8298 0 : sprintf(line + strlen(line), "[%d] ", key.item_size);
8299 : else
8300 0 : sprintf(line + strlen(line), "[%d] ", j);
8301 :
8302 0 : std::string str = db_sprintf(data, key.item_size, j, key.type);
8303 0 : sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8304 :
8305 : /* copy line to buffer */
8306 0 : if ((INT) (strlen(line) + 1) > *buffer_size) {
8307 0 : free(data);
8308 0 : return DB_TRUNCATED;
8309 : }
8310 :
8311 0 : strcpy(buffer, line);
8312 0 : buffer += strlen(line);
8313 0 : *buffer_size -= strlen(line);
8314 0 : line[0] = 0;
8315 0 : }
8316 : }
8317 : }
8318 :
8319 : /* copy line to buffer */
8320 0 : if ((INT) (strlen(line) + 1) > *buffer_size) {
8321 0 : free(data);
8322 0 : return DB_TRUNCATED;
8323 : }
8324 :
8325 0 : strcpy(buffer, line);
8326 0 : buffer += strlen(line);
8327 0 : *buffer_size -= strlen(line);
8328 :
8329 0 : free(data);
8330 0 : data = NULL;
8331 : }
8332 :
8333 24 : if (!hSubkey)
8334 4 : break;
8335 :
8336 20 : status = db_get_link(hDB, hSubkey, &key);
8337 20 : if (status != DB_SUCCESS)
8338 0 : continue;
8339 :
8340 20 : if (strcmp(key.name, "arr2") == 0)
8341 0 : printf("\narr2\n");
8342 20 : size = key.total_size;
8343 20 : data = (char *) malloc(size);
8344 20 : if (data == NULL) {
8345 0 : cm_msg(MERROR, "db_copy", "cannot allocate data buffer");
8346 0 : return DB_NO_MEMORY;
8347 : }
8348 :
8349 20 : line[0] = 0;
8350 :
8351 20 : if (key.type == TID_KEY) {
8352 : char str[MAX_ODB_PATH];
8353 :
8354 : /* new line */
8355 0 : if (bWritten) {
8356 0 : if (*buffer_size < 2) {
8357 0 : free(data);
8358 0 : return DB_TRUNCATED;
8359 : }
8360 :
8361 0 : strcpy(buffer, "\n");
8362 0 : buffer += 1;
8363 0 : *buffer_size -= 1;
8364 : }
8365 :
8366 0 : strcpy(str, full_path);
8367 0 : if (str[0] && str[strlen(str) - 1] != '/')
8368 0 : strcat(str, "/");
8369 0 : strcat(str, key.name);
8370 :
8371 : /* recurse */
8372 0 : status = db_copy(hDB, hSubkey, buffer, buffer_size, str);
8373 0 : if (status != DB_SUCCESS) {
8374 0 : free(data);
8375 0 : return status;
8376 : }
8377 :
8378 0 : buffer += strlen(buffer);
8379 0 : bWritten = FALSE;
8380 : } else {
8381 20 : status = db_get_link_data(hDB, hSubkey, data, &size, key.type);
8382 20 : if (status != DB_SUCCESS)
8383 0 : continue;
8384 :
8385 20 : if (!bWritten) {
8386 4 : if (path[0] == 0)
8387 4 : sprintf(line, "[.]\n");
8388 : else
8389 0 : sprintf(line, "[%s]\n", path);
8390 4 : bWritten = TRUE;
8391 : }
8392 :
8393 20 : if (key.num_values == 1) {
8394 20 : sprintf(line + strlen(line), "%s = %s : ", key.name, rpc_tid_name(key.type));
8395 :
8396 20 : if (key.type == TID_STRING && strchr(data, '\n') != NULL) {
8397 : /* multiline string */
8398 0 : sprintf(line + strlen(line), "[====#$@$#====]\n");
8399 :
8400 : /* ensure string limiter */
8401 0 : data[size - 1] = 0;
8402 :
8403 : /* copy line to buffer */
8404 0 : if ((INT) (strlen(line) + 1) > *buffer_size) {
8405 0 : free(data);
8406 0 : return DB_TRUNCATED;
8407 : }
8408 :
8409 0 : strcpy(buffer, line);
8410 0 : buffer += strlen(line);
8411 0 : *buffer_size -= strlen(line);
8412 :
8413 : /* copy multiple lines to buffer */
8414 0 : if (key.item_size > *buffer_size) {
8415 0 : free(data);
8416 0 : return DB_TRUNCATED;
8417 : }
8418 :
8419 0 : strcpy(buffer, data);
8420 0 : buffer += strlen(data);
8421 0 : *buffer_size -= strlen(data);
8422 :
8423 0 : strcpy(line, "\n====#$@$#====\n");
8424 : } else {
8425 20 : std::string str = db_sprintf(data, key.item_size, 0, key.type);
8426 :
8427 20 : if (key.type == TID_STRING || key.type == TID_LINK)
8428 4 : sprintf(line + strlen(line), "[%d] ", key.item_size);
8429 :
8430 20 : sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8431 20 : }
8432 : } else {
8433 0 : sprintf(line + strlen(line), "%s = %s[%d] :\n", key.name, rpc_tid_name(key.type), key.num_values);
8434 :
8435 0 : for (j = 0; j < key.num_values; j++) {
8436 0 : if (key.type == TID_STRING || key.type == TID_LINK)
8437 0 : sprintf(line + strlen(line), "[%d] ", key.item_size);
8438 : else
8439 0 : sprintf(line + strlen(line), "[%d] ", j);
8440 :
8441 0 : std::string str = db_sprintf(data, key.item_size, j, key.type);
8442 0 : sprintf(line + strlen(line), "%s\n", str.c_str()); // FIXME: buffer overflow. K.O. Aug2024
8443 :
8444 : /* copy line to buffer */
8445 0 : if ((INT) (strlen(line) + 1) > *buffer_size) {
8446 0 : free(data);
8447 0 : return DB_TRUNCATED;
8448 : }
8449 :
8450 0 : strcpy(buffer, line);
8451 0 : buffer += strlen(line);
8452 0 : *buffer_size -= strlen(line);
8453 0 : line[0] = 0;
8454 0 : }
8455 : }
8456 :
8457 : /* copy line to buffer */
8458 20 : if ((INT) (strlen(line) + 1) > *buffer_size) {
8459 0 : free(data);
8460 0 : return DB_TRUNCATED;
8461 : }
8462 :
8463 20 : strcpy(buffer, line);
8464 20 : buffer += strlen(line);
8465 20 : *buffer_size -= strlen(line);
8466 : }
8467 :
8468 20 : free(data);
8469 20 : data = NULL;
8470 20 : }
8471 :
8472 4 : if (bWritten) {
8473 4 : if (*buffer_size < 2)
8474 0 : return DB_TRUNCATED;
8475 :
8476 4 : strcpy(buffer, "\n");
8477 4 : buffer += 1;
8478 4 : *buffer_size -= 1;
8479 : }
8480 :
8481 4 : return DB_SUCCESS;
8482 : }
8483 :
8484 : /********************************************************************/
8485 : /**
8486 : Copy an ODB subtree in ASCII format from a buffer
8487 : @param hDB ODB handle obtained via cm_get_experiment_database().
8488 : @param hKeyRoot Handle for key where search starts, zero for root.
8489 : @param buffer NULL-terminated buffer
8490 : @return DB_SUCCESS, DB_TRUNCATED, DB_NO_MEMORY
8491 : */
8492 6 : INT 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 6 : title[0] = 0;
8503 :
8504 6 : if (hKeyRoot == 0)
8505 0 : db_find_key(hDB, hKeyRoot, "", &hKeyRoot);
8506 :
8507 6 : db_get_key(hDB, hKeyRoot, &root_key);
8508 :
8509 : /* initial data size */
8510 6 : data_size = 1000;
8511 6 : data = (char *) malloc(data_size);
8512 6 : if (data == NULL) {
8513 0 : cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8514 0 : return DB_NO_MEMORY;
8515 : }
8516 :
8517 : do {
8518 : char line[10*MAX_STRING_LENGTH];
8519 :
8520 56 : if (*buffer == 0)
8521 6 : break;
8522 :
8523 1048 : for (i = 0; *buffer != '\n' && *buffer && i < 10*MAX_STRING_LENGTH; i++)
8524 998 : line[i] = *buffer++;
8525 :
8526 50 : if (i == 10*MAX_STRING_LENGTH) {
8527 0 : line[10*MAX_STRING_LENGTH-1] = 0;
8528 0 : cm_msg(MERROR, "db_paste", "line too long: %s...", line);
8529 0 : free(data);
8530 0 : return DB_TRUNCATED;
8531 : }
8532 :
8533 50 : line[i] = 0;
8534 50 : if (*buffer == '\n')
8535 50 : buffer++;
8536 :
8537 : /* check if it is a section title */
8538 50 : if (line[0] == '[') {
8539 : /* extract title and append '/' */
8540 6 : mstrlcpy(title, line + 1, sizeof(title));
8541 6 : if (strchr(title, ']'))
8542 6 : *strchr(title, ']') = 0;
8543 6 : if (title[0] && title[strlen(title) - 1] != '/')
8544 6 : mstrlcat(title, "/", sizeof(title));
8545 : } else {
8546 : /* valid data line if it includes '=' and no ';' */
8547 44 : 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 38 : char* pline = strrchr(line, '=') + 1;
8554 38 : while (strstr(line, ": [") != NULL && strstr(line, ": [") < pline) {
8555 0 : pline -= 2;
8556 0 : while (*pline != '=' && pline > line)
8557 0 : pline--;
8558 0 : pline++;
8559 : }
8560 76 : while (*pline == ' ')
8561 38 : pline++;
8562 38 : mstrlcpy(data_str, pline, sizeof(data_str));
8563 :
8564 : /* extract key name */
8565 38 : *strrchr(line, '=') = 0;
8566 38 : while (strstr(line, ": [") && strchr(line, '='))
8567 0 : *strrchr(line, '=') = 0;
8568 :
8569 38 : pline = &line[strlen(line) - 1];
8570 76 : while (*pline == ' ')
8571 38 : *pline-- = 0;
8572 :
8573 38 : key_name[0] = 0;
8574 38 : if (title[0] != '.')
8575 0 : mstrlcpy(key_name, title, sizeof(key_name));
8576 :
8577 38 : mstrlcat(key_name, line, sizeof(key_name));
8578 :
8579 : /* evaluate type info */
8580 38 : mstrlcpy(line, data_str, sizeof(line));
8581 38 : if (strchr(line, ' '))
8582 38 : *strchr(line, ' ') = 0;
8583 :
8584 38 : n_data = 1;
8585 38 : if (strchr(line, '[')) {
8586 0 : n_data = atol(strchr(line, '[') + 1);
8587 0 : *strchr(line, '[') = 0;
8588 : }
8589 :
8590 428 : for (tid = 0; tid < TID_LAST; tid++)
8591 422 : if (strcmp(rpc_tid_name(tid), line) == 0)
8592 32 : break;
8593 38 : if (tid == TID_LAST) {
8594 44 : for (tid = 0; tid < TID_LAST; tid++)
8595 44 : if (strcmp(rpc_tid_name_old(tid), line) == 0)
8596 6 : break;
8597 : }
8598 :
8599 38 : string_length = 0;
8600 :
8601 38 : if (tid == TID_LAST)
8602 0 : cm_msg(MERROR, "db_paste", "found unknown data type \"%s\" in ODB file", line);
8603 : else {
8604 : /* skip type info */
8605 38 : char* pc = data_str;
8606 220 : while (*pc != ' ' && *pc)
8607 182 : pc++;
8608 152 : while ((*pc == ' ' || *pc == ':') && *pc)
8609 114 : pc++;
8610 :
8611 : //mstrlcpy(data_str, pc, sizeof(data_str)); // MacOS 10.9 does not permit mstrlcpy() of overlapping strings
8612 38 : assert(strlen(pc) < sizeof(data_str)); // "pc" points at a substring inside "data_str"
8613 38 : memmove(data_str, pc, strlen(pc)+1);
8614 :
8615 38 : if (n_data > 1) {
8616 0 : data_str[0] = 0;
8617 0 : if (!*buffer)
8618 0 : break;
8619 :
8620 0 : for (j = 0; *buffer != '\n' && *buffer; j++)
8621 0 : data_str[j] = *buffer++;
8622 0 : data_str[j] = 0;
8623 0 : if (*buffer == '\n')
8624 0 : buffer++;
8625 : }
8626 :
8627 76 : for (i = 0; i < n_data; i++) {
8628 : /* strip trailing \n */
8629 38 : char* pc = &data_str[strlen(data_str) - 1];
8630 38 : while (*pc == '\n' || *pc == '\r')
8631 0 : *pc-- = 0;
8632 :
8633 38 : if (tid == TID_STRING || tid == TID_LINK) {
8634 8 : if (!string_length) {
8635 8 : if (data_str[1] == '=')
8636 0 : string_length = -1;
8637 : else
8638 8 : string_length = atoi(data_str + 1);
8639 8 : if (string_length > MAX_STRING_LENGTH) {
8640 0 : string_length = MAX_STRING_LENGTH;
8641 0 : cm_msg(MERROR, "db_paste", "found string exceeding MAX_STRING_LENGTH, odb path \"%s\"", key_name);
8642 : }
8643 8 : if (string_length == 0) {
8644 0 : string_length = 32;
8645 0 : cm_msg(MERROR, "db_paste", "found string length of zero, set to 32, odb path \"%s\"", key_name);
8646 : }
8647 : }
8648 :
8649 8 : if (string_length == -1) {
8650 : /* multi-line string */
8651 0 : if (strstr(buffer, "\n====#$@$#====\n") != NULL) {
8652 0 : string_length = (POINTER_T) strstr(buffer, "\n====#$@$#====\n") - (POINTER_T) buffer + 1;
8653 :
8654 0 : if (string_length >= data_size) {
8655 0 : data_size += string_length + 100;
8656 0 : data = (char *) realloc(data, data_size);
8657 0 : if (data == NULL) {
8658 0 : cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8659 0 : return DB_NO_MEMORY;
8660 : }
8661 : }
8662 :
8663 0 : memset(data, 0, data_size);
8664 0 : strncpy(data, buffer, string_length);
8665 0 : data[string_length - 1] = 0;
8666 0 : buffer = strstr(buffer, "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
8667 : } else
8668 0 : cm_msg(MERROR, "db_paste", "found multi-line string without termination sequence");
8669 : } else {
8670 8 : char* pc = data_str + 2;
8671 28 : while (*pc && *pc != ' ')
8672 20 : pc++;
8673 :
8674 : // skip one space (needed for strings starting with spaces)
8675 8 : if (*pc)
8676 8 : pc++;
8677 :
8678 : /* limit string size */
8679 8 : *(pc + string_length - 1) = 0;
8680 :
8681 : /* increase data buffer if necessary */
8682 8 : if (string_length * (i + 1) >= data_size) {
8683 0 : data_size += 1000;
8684 0 : data = (char *) realloc(data, data_size);
8685 0 : if (data == NULL) {
8686 0 : cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8687 0 : return DB_NO_MEMORY;
8688 : }
8689 : }
8690 :
8691 8 : mstrlcpy(data + string_length * i, pc, string_length);
8692 : }
8693 8 : } else {
8694 30 : char* pc = data_str;
8695 :
8696 30 : if (n_data > 1 && data_str[0] == '[') {
8697 0 : index = atoi(data_str+1);
8698 0 : pc = strchr(data_str, ']') + 1;
8699 0 : while (*pc && *pc == ' ')
8700 0 : pc++;
8701 : } else
8702 30 : index = 0;
8703 :
8704 : /* increase data buffer if necessary */
8705 30 : if (rpc_tid_size(tid) * (index + 1) >= data_size) {
8706 0 : data_size += 1000;
8707 0 : data = (char *) realloc(data, data_size);
8708 0 : if (data == NULL) {
8709 0 : cm_msg(MERROR, "db_paste", "cannot allocate data buffer");
8710 0 : return DB_NO_MEMORY;
8711 : }
8712 : }
8713 :
8714 30 : db_sscanf(pc, data, &size, index, tid);
8715 : }
8716 :
8717 38 : if (i < n_data - 1) {
8718 0 : data_str[0] = 0;
8719 0 : if (!*buffer)
8720 0 : break;
8721 :
8722 0 : pold = buffer;
8723 :
8724 0 : for (j = 0; *buffer != '\n' && *buffer; j++)
8725 0 : data_str[j] = *buffer++;
8726 0 : data_str[j] = 0;
8727 0 : if (*buffer == '\n')
8728 0 : buffer++;
8729 :
8730 : /* test if valid data */
8731 0 : if (tid != TID_STRING && tid != TID_LINK) {
8732 0 : if (data_str[0] == 0 || (strchr(data_str, '=')
8733 0 : && strchr(data_str, ':')))
8734 0 : buffer = pold;
8735 : }
8736 : }
8737 : }
8738 :
8739 : /* skip system client entries */
8740 38 : mstrlcpy(test_str, key_name, sizeof(test_str));
8741 38 : test_str[15] = 0;
8742 :
8743 38 : if (!equal_ustring(test_str, "/System/Clients")) {
8744 38 : if (root_key.type != TID_KEY) {
8745 : /* root key is destination key */
8746 0 : hKey = hKeyRoot;
8747 : } else {
8748 : /* create key and set value */
8749 38 : if (key_name[0] == '/') {
8750 0 : status = db_find_link(hDB, 0, key_name, &hKey);
8751 0 : if (status == DB_NO_KEY) {
8752 0 : db_create_key(hDB, 0, key_name, tid);
8753 0 : status = db_find_link(hDB, 0, key_name, &hKey);
8754 : }
8755 : } else {
8756 38 : status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
8757 38 : if (status == DB_NO_KEY) {
8758 38 : db_create_key(hDB, hKeyRoot, key_name, tid);
8759 38 : status = db_find_link(hDB, hKeyRoot, key_name, &hKey);
8760 : }
8761 : }
8762 : }
8763 :
8764 : /* set key data if created successfully */
8765 38 : if (hKey) {
8766 38 : if (tid == TID_STRING || tid == TID_LINK)
8767 8 : db_set_link_data(hDB, hKey, data, string_length * n_data, n_data, tid);
8768 : else
8769 30 : db_set_link_data(hDB, hKey, data, rpc_tid_size(tid) * n_data, n_data, tid);
8770 : }
8771 : }
8772 : }
8773 : }
8774 : }
8775 50 : } while (TRUE);
8776 :
8777 6 : free(data);
8778 6 : return DB_SUCCESS;
8779 : }
8780 :
8781 : /********************************************************************/
8782 : /*
8783 : Only internally used by db_paste_xml
8784 : */
8785 0 : static int db_paste_node(HNDLE hDB, HNDLE hKeyRoot, PMXML_NODE node)
8786 : {
8787 : int status;
8788 :
8789 0 : if (strcmp(mxml_get_name(node), "odb") == 0) {
8790 0 : for (int i = 0; i < mxml_get_number_of_children(node); i++) {
8791 0 : status = db_paste_node(hDB, hKeyRoot, mxml_subnode(node, i));
8792 0 : if (status != DB_SUCCESS)
8793 0 : return status;
8794 : }
8795 0 : } else if (strcmp(mxml_get_name(node), "dir") == 0) {
8796 0 : const char* name = mxml_get_attribute(node, "name");
8797 :
8798 0 : if (name == NULL) {
8799 0 : cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no name in XML data", mxml_get_name(node));
8800 0 : return DB_TYPE_MISMATCH;
8801 : }
8802 :
8803 : HNDLE hKey;
8804 0 : status = db_find_link(hDB, hKeyRoot, name, &hKey);
8805 :
8806 0 : if (status == DB_NO_KEY) {
8807 0 : status = db_create_key(hDB, hKeyRoot, name, TID_KEY);
8808 0 : if (status == DB_NO_ACCESS) {
8809 0 : cm_msg(MINFO, "db_paste_node", "cannot load key \"%s\": write protected", name);
8810 0 : return DB_SUCCESS; /* key or tree is locked, just skip it */
8811 : }
8812 :
8813 0 : if (status != DB_SUCCESS && status != DB_KEY_EXIST) {
8814 0 : cm_msg(MERROR, "db_paste_node", "cannot create key \"%s\" in ODB, status = %d", name, status);
8815 0 : return status;
8816 : }
8817 0 : status = db_find_link(hDB, hKeyRoot, name, &hKey);
8818 0 : if (status != DB_SUCCESS) {
8819 0 : cm_msg(MERROR, "db_paste_node", "cannot find key \"%s\" in ODB", name);
8820 0 : return status;
8821 : }
8822 : }
8823 :
8824 0 : std::string path = db_get_path(hDB, hKey);
8825 0 : if (!equal_ustring(path.c_str(), "/System/Clients")) {
8826 0 : for (int i = 0; i < mxml_get_number_of_children(node); i++) {
8827 0 : status = db_paste_node(hDB, hKey, mxml_subnode(node, i));
8828 0 : if (status != DB_SUCCESS)
8829 0 : return status;
8830 : }
8831 : }
8832 0 : } else if (strcmp(mxml_get_name(node), "key") == 0 || strcmp(mxml_get_name(node), "keyarray") == 0) {
8833 :
8834 0 : const char* name = mxml_get_attribute(node, "name");
8835 :
8836 0 : if (name == NULL) {
8837 0 : cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no name in XML data", mxml_get_name(node));
8838 0 : return DB_TYPE_MISMATCH;
8839 : }
8840 :
8841 : int num_values;
8842 0 : if (strcmp(mxml_get_name(node), "keyarray") == 0)
8843 0 : num_values = atoi(mxml_get_attribute(node, "num_values"));
8844 : else
8845 0 : num_values = 0;
8846 :
8847 0 : const char* type = mxml_get_attribute(node, "type");
8848 :
8849 0 : if (type == NULL) {
8850 0 : cm_msg(MERROR, "db_paste_node", "found key \"%s\" with no type in XML data", mxml_get_name(node));
8851 0 : return DB_TYPE_MISMATCH;
8852 : }
8853 :
8854 0 : int tid = rpc_name_tid(type);
8855 0 : if (tid == 0) {
8856 0 : cm_msg(MERROR, "db_paste_node", "found unknown data type \"%s\" in XML data", type);
8857 0 : return DB_TYPE_MISMATCH;
8858 : }
8859 :
8860 : HNDLE hKey;
8861 0 : status = db_find_link(hDB, hKeyRoot, name, &hKey);
8862 0 : if (status == DB_NO_KEY) {
8863 0 : status = db_create_key(hDB, hKeyRoot, name, tid);
8864 0 : if (status == DB_NO_ACCESS) {
8865 0 : cm_msg(MINFO, "db_paste_node", "cannot load key \"%s\": write protected", name);
8866 0 : return DB_SUCCESS; /* key or tree is locked, just skip it */
8867 : }
8868 :
8869 0 : if (status != DB_SUCCESS) {
8870 0 : cm_msg(MERROR, "db_paste_node", "cannot create key \"%s\" in ODB, status = %d", name, status);
8871 0 : return status;
8872 : }
8873 0 : status = db_find_link(hDB, hKeyRoot, name, &hKey);
8874 0 : if (status != DB_SUCCESS) {
8875 0 : cm_msg(MERROR, "db_paste_node", "cannot find key \"%s\" in ODB, status = %d", name, status);
8876 0 : return status;
8877 : }
8878 : } else {
8879 : KEY key;
8880 0 : status = db_get_key(hDB, hKey, &key);
8881 0 : if (status != DB_SUCCESS) {
8882 0 : cm_msg(MERROR, "db_paste_node", "cannot get key \"%s\" in ODB, status = %d", name, status);
8883 0 : return status;
8884 : }
8885 :
8886 0 : if (num_values > 0 && num_values != key.num_values && key.item_size > 0) {
8887 0 : status = db_set_num_values(hDB, hKey, num_values);
8888 0 : if (status != DB_SUCCESS) {
8889 0 : cm_msg(MERROR, "db_paste_node", "cannot resize key \"%s\" in ODB, status = %d", name, status);
8890 0 : return status;
8891 : }
8892 : }
8893 : }
8894 :
8895 0 : int size = 0;
8896 0 : char *buf = NULL;
8897 :
8898 0 : if (tid == TID_STRING || tid == TID_LINK) {
8899 0 : size = atoi(mxml_get_attribute(node, "size"));
8900 0 : buf = (char *)malloc(size);
8901 0 : assert(buf);
8902 0 : buf[0] = 0;
8903 : }
8904 :
8905 0 : if (num_values) {
8906 : /* evaluate array */
8907 0 : for (int i = 0; i < mxml_get_number_of_children(node); i++) {
8908 0 : PMXML_NODE child = mxml_subnode(node, i);
8909 : int idx;
8910 0 : if (mxml_get_attribute(child, "index"))
8911 0 : idx = atoi(mxml_get_attribute(child, "index"));
8912 : else
8913 0 : idx = i;
8914 0 : if (tid == TID_STRING || tid == TID_LINK) {
8915 0 : if (mxml_get_value(child) == NULL) {
8916 0 : status = db_set_data_index(hDB, hKey, "", size, i, tid);
8917 0 : if (status == DB_NO_ACCESS) {
8918 0 : cm_msg(MINFO, "db_paste_node", "cannot load string or link \"%s\": write protected", mxml_get_attribute(node, "name"));
8919 0 : return DB_SUCCESS; /* key or tree is locked, just skip it */
8920 0 : } else if (status != DB_SUCCESS) {
8921 0 : 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 0 : return status;
8923 : }
8924 : } else {
8925 0 : mstrlcpy(buf, mxml_get_value(child), size);
8926 0 : status = db_set_data_index(hDB, hKey, buf, size, idx, tid);
8927 0 : if (status == DB_NO_ACCESS) {
8928 0 : cm_msg(MINFO, "db_paste_node", "cannot load array element \"%s\": write protected", mxml_get_attribute(node, "name"));
8929 0 : return DB_SUCCESS; /* key or tree is locked, just skip it */
8930 0 : } else if (status != DB_SUCCESS) {
8931 0 : cm_msg(MERROR, "db_paste_node", "cannot load array element \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
8932 0 : return status;
8933 : }
8934 : }
8935 : } else {
8936 : char data[256];
8937 0 : db_sscanf(mxml_get_value(child), data, &size, 0, tid);
8938 0 : status = db_set_data_index(hDB, hKey, data, rpc_tid_size(tid), idx, tid);
8939 0 : if (status == DB_NO_ACCESS) {
8940 0 : cm_msg(MINFO, "db_paste_node", "cannot load array element \"%s\": write protected", mxml_get_attribute(node, "name"));
8941 0 : return DB_SUCCESS; /* key or tree is locked, just skip it */
8942 0 : } else if (status != DB_SUCCESS) {
8943 0 : cm_msg(MERROR, "db_paste_node", "cannot load array element \"%s\": db_set_data_index() status %d", mxml_get_attribute(node, "name"), status);
8944 0 : return status;
8945 : }
8946 : }
8947 : }
8948 :
8949 : } else { /* single value */
8950 0 : if (tid == TID_STRING || tid == TID_LINK) {
8951 0 : size = atoi(mxml_get_attribute(node, "size"));
8952 0 : if (mxml_get_value(node) == NULL) {
8953 0 : status = db_set_data(hDB, hKey, "", size, 1, tid);
8954 0 : if (status == DB_NO_ACCESS) {
8955 0 : cm_msg(MINFO, "db_paste_node", "cannot load string or link \"%s\": write protected", mxml_get_attribute(node, "name"));
8956 0 : return DB_SUCCESS; /* key or tree is locked, just skip it */
8957 0 : } else if (status != DB_SUCCESS) {
8958 0 : cm_msg(MERROR, "db_paste_node", "cannot load string or link \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
8959 0 : return status;
8960 : }
8961 : } else {
8962 0 : mstrlcpy(buf, mxml_get_value(node), size);
8963 0 : status = db_set_data(hDB, hKey, buf, size, 1, tid);
8964 0 : if (status == DB_NO_ACCESS) {
8965 0 : cm_msg(MINFO, "db_paste_node", "cannot load value \"%s\": write protected", mxml_get_attribute(node, "name"));
8966 0 : return DB_SUCCESS; /* key or tree is locked, just skip it */
8967 0 : } else if (status != DB_SUCCESS) {
8968 0 : cm_msg(MERROR, "db_paste_node", "cannot load value \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
8969 0 : return status;
8970 : }
8971 : }
8972 : } else {
8973 : char data[256];
8974 0 : db_sscanf(mxml_get_value(node), data, &size, 0, tid);
8975 0 : status = db_set_data(hDB, hKey, data, rpc_tid_size(tid), 1, tid);
8976 0 : if (status == DB_NO_ACCESS) {
8977 0 : cm_msg(MINFO, "db_paste_node", "cannot load value \"%s\": write protected", mxml_get_attribute(node, "name"));
8978 0 : return DB_SUCCESS; /* key or tree is locked, just skip it */
8979 0 : } else if (status != DB_SUCCESS) {
8980 0 : cm_msg(MERROR, "db_paste_node", "cannot load value \"%s\": db_set_data() status %d", mxml_get_attribute(node, "name"), status);
8981 0 : return status;
8982 : }
8983 : }
8984 : }
8985 :
8986 0 : if (buf) {
8987 0 : free(buf);
8988 0 : buf = NULL;
8989 : }
8990 : }
8991 :
8992 0 : return DB_SUCCESS;
8993 : }
8994 :
8995 : /********************************************************************/
8996 : /**
8997 : Paste an ODB subtree in XML format from a buffer
8998 : @param hDB ODB handle obtained via cm_get_experiment_database().
8999 : @param hKeyRoot Handle for key where search starts, zero for root.
9000 : @param buffer NULL-terminated buffer
9001 : @return DB_SUCCESS, DB_INVALID_PARAM, DB_NO_MEMORY, DB_TYPE_MISMATCH
9002 : */
9003 0 : INT db_paste_xml(HNDLE hDB, HNDLE hKeyRoot, const char *buffer)
9004 : {
9005 : char error[256];
9006 : INT status;
9007 : PMXML_NODE tree, node;
9008 :
9009 0 : if (hKeyRoot == 0)
9010 0 : db_find_key(hDB, hKeyRoot, "", &hKeyRoot);
9011 :
9012 : /* parse XML buffer */
9013 0 : tree = mxml_parse_buffer(buffer, error, sizeof(error), NULL);
9014 0 : if (tree == NULL) {
9015 0 : puts(error);
9016 0 : return DB_TYPE_MISMATCH;
9017 : }
9018 :
9019 0 : node = mxml_find_node(tree, "odb");
9020 0 : if (node == NULL) {
9021 0 : puts("Cannot find element \"odb\" in XML data");
9022 0 : return DB_TYPE_MISMATCH;
9023 : }
9024 :
9025 0 : status = db_paste_node(hDB, hKeyRoot, node);
9026 :
9027 0 : mxml_free_tree(tree);
9028 :
9029 0 : return status;
9030 : }
9031 :
9032 : /********************************************************************/
9033 : /**
9034 : Copy an ODB subtree in XML format to a buffer
9035 :
9036 : @param hDB ODB handle obtained via cm_get_experiment_database().
9037 : @param hKey Handle for key where search starts, zero for root.
9038 : @param buffer ASCII buffer which receives ODB contents.
9039 : @param buffer_size Size of buffer, returns remaining space in buffer.
9040 : @return DB_SUCCESS, DB_TRUNCATED, DB_NO_MEMORY
9041 : */
9042 0 : INT db_copy_xml(HNDLE hDB, HNDLE hKey, char *buffer, int *buffer_size, bool header)
9043 : {
9044 :
9045 0 : if (rpc_is_remote())
9046 0 : 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 0 : writer = mxml_open_buffer();
9056 0 : if (writer == NULL) {
9057 0 : cm_msg(MERROR, "db_copy_xml", "Cannot allocate buffer");
9058 0 : return DB_NO_MEMORY;
9059 : }
9060 :
9061 0 : if (header) {
9062 0 : std::string path = db_get_path(hDB, hKey);
9063 :
9064 : /* write XML header */
9065 0 : mxml_start_element(writer, "odb");
9066 0 : mxml_write_attribute(writer, "root", path.c_str());
9067 0 : mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
9068 0 : mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", "http://midas.psi.ch/odb.xsd");
9069 :
9070 0 : db_save_xml_key(hDB, hKey, 0, writer);
9071 0 : mxml_end_element(writer); // "odb"
9072 0 : } else {
9073 : KEY key;
9074 0 : int status = db_get_key(hDB, hKey, &key);
9075 0 : if (status != DB_SUCCESS)
9076 0 : return status;
9077 :
9078 0 : db_save_xml_key(hDB, hKey, 1, writer);
9079 : }
9080 :
9081 0 : p = mxml_close_buffer(writer);
9082 0 : len = strlen(p) + 1;
9083 0 : if (len > *buffer_size) {
9084 0 : free(p);
9085 0 : *buffer_size = 0;
9086 0 : return DB_TRUNCATED;
9087 : }
9088 :
9089 0 : mstrlcpy(buffer, p, len);
9090 0 : free(p);
9091 0 : *buffer_size = len;
9092 : }
9093 : #endif /* LOCAL_ROUTINES */
9094 :
9095 0 : return DB_SUCCESS;
9096 : }
9097 :
9098 : /**dox***************************************************************/
9099 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
9100 :
9101 : /*------------------------------------------------------------------*/
9102 0 : void name2c(char *str)
9103 : /********************************************************************\
9104 :
9105 : Routine: name2c
9106 :
9107 : Purpose: Convert key name to C name. Internal use only.
9108 :
9109 : \********************************************************************/
9110 : {
9111 0 : if (*str >= '0' && *str <= '9')
9112 0 : *str = '_';
9113 :
9114 0 : while (*str) {
9115 0 : if (!(*str >= 'a' && *str <= 'z') && !(*str >= 'A' && *str <= 'Z') && !(*str >= '0' && *str <= '9'))
9116 0 : *str = '_';
9117 0 : *str = (char) tolower(*str);
9118 0 : str++;
9119 : }
9120 0 : }
9121 :
9122 : /*------------------------------------------------------------------*/
9123 0 : static void db_save_tree_struct(HNDLE hDB, HNDLE hKey, int hfile, INT level)
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 0 : for (idx = 0;; idx++) {
9140 : char name[MAX_ODB_PATH];
9141 :
9142 0 : db_enum_link(hDB, hKey, idx, &hSubkey);
9143 0 : if (!hSubkey)
9144 0 : break;
9145 :
9146 : /* first get the name of the link, than the type of the link target */
9147 0 : db_get_key(hDB, hSubkey, &key);
9148 0 : mstrlcpy(name, key.name, sizeof(name));
9149 0 : db_enum_key(hDB, hKey, idx, &hSubkey);
9150 :
9151 0 : db_get_key(hDB, hSubkey, &key);
9152 :
9153 0 : if (key.type != TID_KEY) {
9154 : char line[MAX_ODB_PATH];
9155 : char str[MAX_ODB_PATH];
9156 :
9157 0 : for (i = 0; i <= level; i++) {
9158 0 : wr = write(hfile, " ", 2);
9159 0 : assert(wr == 2);
9160 : }
9161 :
9162 0 : switch (key.type) {
9163 0 : case TID_INT8:
9164 : case TID_CHAR:
9165 0 : strcpy(line, "char");
9166 0 : break;
9167 0 : case TID_INT16:
9168 0 : strcpy(line, "short");
9169 0 : break;
9170 0 : case TID_FLOAT:
9171 0 : strcpy(line, "float");
9172 0 : break;
9173 0 : case TID_DOUBLE:
9174 0 : strcpy(line, "double");
9175 0 : break;
9176 0 : case TID_BITFIELD:
9177 0 : strcpy(line, "unsigned char");
9178 0 : break;
9179 0 : case TID_STRING:
9180 0 : strcpy(line, "char");
9181 0 : break;
9182 0 : case TID_LINK:
9183 0 : strcpy(line, "char");
9184 0 : break;
9185 0 : default:
9186 0 : strcpy(line, rpc_tid_name(key.type));
9187 0 : break;
9188 : }
9189 :
9190 0 : mstrlcat(line, " ", sizeof(line));
9191 0 : mstrlcpy(str, name, sizeof(str));
9192 0 : name2c(str);
9193 :
9194 0 : if (key.num_values > 1)
9195 0 : sprintf(str + strlen(str), "[%d]", key.num_values);
9196 0 : if (key.type == TID_STRING || key.type == TID_LINK)
9197 0 : sprintf(str + strlen(str), "[%d]", key.item_size);
9198 :
9199 0 : mstrlcpy(line + 10, str, sizeof(line) - 10);
9200 0 : mstrlcat(line, ";\n", sizeof(line));
9201 :
9202 0 : wr = write(hfile, line, strlen(line));
9203 0 : assert(wr > 0);
9204 : } else {
9205 : char line[10+MAX_ODB_PATH];
9206 : char str[MAX_ODB_PATH];
9207 :
9208 : /* recurse subtree */
9209 0 : for (i = 0; i <= level; i++) {
9210 0 : wr = write(hfile, " ", 2);
9211 0 : assert(wr == 2);
9212 : }
9213 :
9214 0 : sprintf(line, "struct {\n");
9215 0 : wr = write(hfile, line, strlen(line));
9216 0 : assert(wr > 0);
9217 0 : db_save_tree_struct(hDB, hSubkey, hfile, level + 1);
9218 :
9219 0 : for (i = 0; i <= level; i++) {
9220 0 : wr = write(hfile, " ", 2);
9221 0 : assert(wr == 2);
9222 : }
9223 :
9224 0 : strcpy(str, name);
9225 0 : name2c(str);
9226 :
9227 0 : sprintf(line, "} %s;\n", str);
9228 0 : wr = write(hfile, line, strlen(line));
9229 0 : assert(wr > 0);
9230 : }
9231 0 : }
9232 0 : }
9233 :
9234 : /**dox***************************************************************/
9235 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
9236 :
9237 : /********************************************************************/
9238 : /**
9239 : Save a branch of a database to an .ODB file
9240 :
9241 : This function is used by the ODBEdit command save. For a
9242 : description of the ASCII format, see db_copy(). Data of the whole ODB can
9243 : be saved (hkey equal zero) or only a sub-tree.
9244 : @param hDB ODB handle obtained via cm_get_experiment_database().
9245 : @param hKey Handle for key where search starts, zero for root.
9246 : @param filename Filename of .ODB file.
9247 : @param bRemote Flag for saving database on remote server.
9248 : @return DB_SUCCESS, DB_FILE_ERROR
9249 : */
9250 0 : INT db_save(HNDLE hDB, HNDLE hKey, const char *filename, BOOL bRemote)
9251 : {
9252 0 : if (rpc_is_remote() && bRemote)
9253 0 : 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 0 : hfile = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0644);
9262 0 : if (hfile == -1) {
9263 0 : cm_msg(MERROR, "db_save", "Cannot open file \"%s\"", filename);
9264 0 : return DB_FILE_ERROR;
9265 : }
9266 :
9267 0 : std::string path = db_get_path(hDB, hKey);
9268 :
9269 0 : buffer_size = 10000;
9270 : do {
9271 0 : buffer = (char *) malloc(buffer_size);
9272 0 : if (buffer == NULL) {
9273 0 : cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
9274 0 : break;
9275 : }
9276 :
9277 0 : size = buffer_size;
9278 0 : status = db_copy(hDB, hKey, buffer, &size, path.c_str());
9279 0 : if (status != DB_TRUNCATED) {
9280 0 : n = write(hfile, buffer, buffer_size - size);
9281 0 : free(buffer);
9282 0 : buffer = NULL;
9283 :
9284 0 : if (n != buffer_size - size) {
9285 0 : cm_msg(MERROR, "db_save", "cannot save .ODB file");
9286 0 : close(hfile);
9287 0 : return DB_FILE_ERROR;
9288 : }
9289 0 : break;
9290 : }
9291 :
9292 : /* increase buffer size if truncated */
9293 0 : free(buffer);
9294 0 : buffer = NULL;
9295 0 : buffer_size *= 2;
9296 : } while (1);
9297 :
9298 0 : close(hfile);
9299 :
9300 0 : }
9301 : #endif /* LOCAL_ROUTINES */
9302 :
9303 0 : return DB_SUCCESS;
9304 : }
9305 :
9306 : /*------------------------------------------------------------------*/
9307 :
9308 0 : void xml_encode(char *src, int size)
9309 : {
9310 : int i;
9311 : char *dst, *p;
9312 :
9313 0 : dst = (char *) malloc(size);
9314 0 : if (dst == NULL)
9315 0 : return;
9316 :
9317 0 : *dst = 0;
9318 0 : for (i = 0; i < (int) strlen(src); i++) {
9319 0 : switch (src[i]) {
9320 0 : case '<':
9321 0 : mstrlcat(dst, "<", size);
9322 0 : break;
9323 0 : case '>':
9324 0 : mstrlcat(dst, ">", size);
9325 0 : break;
9326 0 : case '&':
9327 0 : mstrlcat(dst, "&", size);
9328 0 : break;
9329 0 : case '\"':
9330 0 : mstrlcat(dst, """, size);
9331 0 : break;
9332 0 : case '\'':
9333 0 : mstrlcat(dst, "'", size);
9334 0 : break;
9335 0 : default:
9336 0 : if ((int) strlen(dst) >= size) {
9337 0 : free(dst);
9338 0 : return;
9339 : }
9340 0 : p = dst + strlen(dst);
9341 0 : *p = src[i];
9342 0 : *(p + 1) = 0;
9343 : }
9344 : }
9345 :
9346 0 : mstrlcpy(src, dst, size);
9347 : }
9348 :
9349 : /*------------------------------------------------------------------*/
9350 :
9351 0 : INT db_save_xml_key(HNDLE hDB, HNDLE hKey, INT level, MXML_WRITER * writer)
9352 : {
9353 : INT i, idx, size, status;
9354 : char *data;
9355 : HNDLE hSubkey;
9356 : KEY key;
9357 :
9358 0 : status = db_get_link(hDB, hKey, &key);
9359 0 : if (status != DB_SUCCESS)
9360 0 : return status;
9361 :
9362 0 : if (key.type == TID_KEY) {
9363 :
9364 : /* save opening tag for subtree */
9365 :
9366 0 : if (level > 0) {
9367 0 : mxml_start_element(writer, "dir");
9368 0 : mxml_write_attribute(writer, "name", key.name);
9369 0 : mxml_write_attribute(writer, "handle", std::to_string(hKey).c_str());
9370 : }
9371 :
9372 0 : for (idx = 0;; idx++) {
9373 0 : db_enum_link(hDB, hKey, idx, &hSubkey);
9374 :
9375 0 : if (!hSubkey)
9376 0 : break;
9377 :
9378 : /* save subtree */
9379 0 : status = db_save_xml_key(hDB, hSubkey, level + 1, writer);
9380 0 : if (status != DB_SUCCESS)
9381 0 : return status;
9382 : }
9383 :
9384 : /* save closing tag for subtree */
9385 0 : if (level > 0)
9386 0 : mxml_end_element(writer);
9387 :
9388 : } else {
9389 : /* save key value */
9390 :
9391 0 : if (key.num_values > 1)
9392 0 : mxml_start_element(writer, "keyarray");
9393 : else
9394 0 : mxml_start_element(writer, "key");
9395 0 : mxml_write_attribute(writer, "name", key.name);
9396 0 : mxml_write_attribute(writer, "type", rpc_tid_name(key.type));
9397 0 : mxml_write_attribute(writer, "handle", std::to_string(hKey).c_str());
9398 :
9399 0 : if (key.type == TID_STRING || key.type == TID_LINK) {
9400 : char str[256];
9401 0 : sprintf(str, "%d", key.item_size);
9402 0 : mxml_write_attribute(writer, "size", str);
9403 : }
9404 :
9405 0 : if (key.num_values > 1) {
9406 : char str[256];
9407 0 : sprintf(str, "%d", key.num_values);
9408 0 : mxml_write_attribute(writer, "num_values", str);
9409 : }
9410 :
9411 0 : size = key.total_size;
9412 0 : data = (char *) malloc(size+1); // an extra byte to zero-terminate strings
9413 0 : if (data == NULL) {
9414 0 : cm_msg(MERROR, "db_save_xml_key", "cannot allocate data buffer");
9415 0 : return DB_NO_MEMORY;
9416 : }
9417 :
9418 0 : db_get_link_data(hDB, hKey, data, &size, key.type);
9419 :
9420 0 : if (key.num_values == 1) {
9421 0 : if (key.type == TID_STRING) {
9422 0 : data[size] = 0; // make sure strings are NUL-terminated
9423 0 : mxml_write_value(writer, data);
9424 : } else {
9425 0 : std::string str = db_sprintf(data, key.item_size, 0, key.type);
9426 0 : if (key.type == TID_STRING && strlen(data) >= MAX_STRING_LENGTH) {
9427 0 : std::string path = db_get_path(hDB, hKey);
9428 0 : 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 0 : }
9430 0 : mxml_write_value(writer, str.c_str());
9431 0 : }
9432 0 : mxml_end_element(writer);
9433 :
9434 : } else { /* array of values */
9435 :
9436 0 : for (i = 0; i < key.num_values; i++) {
9437 :
9438 0 : mxml_start_element(writer, "value");
9439 :
9440 : {
9441 : char str[256];
9442 0 : sprintf(str, "%d", i);
9443 0 : mxml_write_attribute(writer, "index", str);
9444 : }
9445 :
9446 0 : if (key.type == TID_STRING) {
9447 0 : char* p = data + i * key.item_size;
9448 0 : 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 0 : mxml_write_value(writer, p);
9451 : } else {
9452 0 : std::string str = db_sprintf(data, key.item_size, i, key.type);
9453 0 : if ((key.type == TID_STRING) && (str.length() >= MAX_STRING_LENGTH-1)) {
9454 0 : std::string path = db_get_path(hDB, hKey);
9455 0 : cm_msg(MERROR, "db_save_xml_key", "Long odb string array probably truncated, odb path \"%s\"[%d]", path.c_str(), i);
9456 0 : }
9457 0 : mxml_write_value(writer, str.c_str());
9458 0 : }
9459 :
9460 0 : mxml_end_element(writer);
9461 : }
9462 :
9463 0 : mxml_end_element(writer); /* keyarray */
9464 : }
9465 :
9466 0 : free(data);
9467 0 : data = NULL;
9468 : }
9469 :
9470 0 : return DB_SUCCESS;
9471 : }
9472 :
9473 : /********************************************************************/
9474 : /**
9475 : Save a branch of a database to an .xml file
9476 :
9477 : This function is used by the ODBEdit command save to write the contents
9478 : of the ODB into a XML file. Data of the whole ODB can
9479 : be saved (hkey equal zero) or only a sub-tree.
9480 : @param hDB ODB handle obtained via cm_get_experiment_database().
9481 : @param hKey Handle for key where search starts, zero for root.
9482 : @param filename Filename of .XML file.
9483 : @return DB_SUCCESS, DB_FILE_ERROR
9484 : */
9485 0 : INT 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 0 : writer = mxml_open_file(filename);
9494 0 : if (writer == NULL) {
9495 0 : cm_msg(MERROR, "db_save_xml", "Cannot open file \"%s\"", filename);
9496 0 : return DB_FILE_ERROR;
9497 : }
9498 :
9499 0 : std::string path = db_get_path(hDB, hKey);
9500 :
9501 : /* write XML header */
9502 0 : mxml_start_element(writer, "odb");
9503 0 : mxml_write_attribute(writer, "root", path.c_str());
9504 0 : mxml_write_attribute(writer, "filename", filename);
9505 0 : mxml_write_attribute(writer, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
9506 :
9507 0 : std::string xsd_path;
9508 :
9509 0 : if (getenv("MIDASSYS"))
9510 0 : xsd_path = getenv("MIDASSYS");
9511 : else
9512 0 : xsd_path = ".";
9513 :
9514 0 : xsd_path += DIR_SEPARATOR_STR;
9515 0 : xsd_path += "odb.xsd";
9516 0 : mxml_write_attribute(writer, "xsi:noNamespaceSchemaLocation", xsd_path.c_str());
9517 :
9518 0 : status = db_save_xml_key(hDB, hKey, 0, writer);
9519 :
9520 0 : mxml_end_element(writer); // "odb"
9521 0 : mxml_close_file(writer);
9522 :
9523 0 : return status;
9524 0 : }
9525 : #endif /* LOCAL_ROUTINES */
9526 :
9527 : return DB_SUCCESS;
9528 : }
9529 :
9530 : /*------------------------------------------------------------------*/
9531 :
9532 0 : void 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 0 : len = strlen(s);
9537 0 : remain = *buffer_size - *buffer_end;
9538 0 : assert(remain >= 0);
9539 :
9540 0 : xlevel = 2*level;
9541 :
9542 0 : while (10 + xlevel + 3*len > remain) {
9543 : // reallocate the buffer
9544 0 : int new_buffer_size = 2*(*buffer_size);
9545 0 : if (new_buffer_size < 4*1024)
9546 0 : 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 0 : assert(new_buffer_size > *buffer_size);
9549 0 : *buffer = (char *)realloc(*buffer, new_buffer_size);
9550 0 : assert(*buffer);
9551 0 : *buffer_size = new_buffer_size;
9552 0 : remain = *buffer_size - *buffer_end;
9553 0 : assert(remain >= 0);
9554 : }
9555 :
9556 0 : if (xlevel) {
9557 : int i;
9558 0 : for (i=0; i<xlevel; i++)
9559 0 : (*buffer)[(*buffer_end)++] = ' ';
9560 : }
9561 :
9562 0 : if (!quoted) {
9563 0 : memcpy(*buffer + *buffer_end, s, len);
9564 0 : *buffer_end += len;
9565 0 : (*buffer)[*buffer_end] = 0; // NUL-terminate the buffer
9566 0 : return;
9567 : }
9568 :
9569 0 : char *bufptr = *buffer;
9570 0 : int bufend = *buffer_end;
9571 :
9572 0 : bufptr[bufend++] = '"';
9573 :
9574 0 : while (*s) {
9575 0 : switch (*s) {
9576 0 : case '\"':
9577 0 : bufptr[bufend++] = '\\';
9578 0 : bufptr[bufend++] = '\"';
9579 0 : s++;
9580 0 : break;
9581 0 : case '\\':
9582 0 : bufptr[bufend++] = '\\';
9583 0 : bufptr[bufend++] = '\\';
9584 0 : s++;
9585 0 : break;
9586 : #if 0
9587 : case '/':
9588 : bufptr[bufend++] = '\\';
9589 : bufptr[bufend++] = '/';
9590 : s++;
9591 : break;
9592 : #endif
9593 0 : case '\b':
9594 0 : bufptr[bufend++] = '\\';
9595 0 : bufptr[bufend++] = 'b';
9596 0 : s++;
9597 0 : break;
9598 0 : case '\f':
9599 0 : bufptr[bufend++] = '\\';
9600 0 : bufptr[bufend++] = 'f';
9601 0 : s++;
9602 0 : break;
9603 0 : case '\n':
9604 0 : bufptr[bufend++] = '\\';
9605 0 : bufptr[bufend++] = 'n';
9606 0 : s++;
9607 0 : break;
9608 0 : case '\r':
9609 0 : bufptr[bufend++] = '\\';
9610 0 : bufptr[bufend++] = 'r';
9611 0 : s++;
9612 0 : break;
9613 0 : case '\t':
9614 0 : bufptr[bufend++] = '\\';
9615 0 : bufptr[bufend++] = 't';
9616 0 : s++;
9617 0 : break;
9618 0 : default:
9619 0 : bufptr[bufend++] = *s++;
9620 : }
9621 : }
9622 :
9623 0 : bufptr[bufend++] = '"';
9624 0 : bufptr[bufend] = 0; // NUL-terminate the buffer
9625 :
9626 0 : *buffer_end = bufend;
9627 :
9628 0 : remain = *buffer_size - *buffer_end;
9629 0 : assert(remain > 0);
9630 : }
9631 :
9632 0 : static void json_ensure_decimal_dot(char *str)
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 0 : char* comma = strchr(str, ',');
9641 :
9642 0 : if (comma != nullptr) {
9643 0 : *comma = '.';
9644 : }
9645 0 : }
9646 :
9647 0 : static 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 0 : switch (key->type) {
9651 0 : case TID_UINT8:
9652 0 : sprintf(str, "%u", *(unsigned char*)p);
9653 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9654 0 : break;
9655 0 : case TID_INT8:
9656 0 : sprintf(str, "%d", *(char*)p);
9657 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9658 0 : break;
9659 0 : case TID_CHAR:
9660 0 : sprintf(str, "%c", *(char*)p);
9661 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 1);
9662 0 : break;
9663 0 : case TID_UINT16:
9664 0 : sprintf(str, "\"0x%04x\"", *(WORD*)p);
9665 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9666 0 : break;
9667 0 : case TID_INT16:
9668 0 : sprintf(str, "%d", *(short*)p);
9669 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9670 0 : break;
9671 0 : case TID_UINT32:
9672 0 : sprintf(str, "\"0x%08x\"", *(DWORD*)p);
9673 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9674 0 : break;
9675 0 : case TID_INT32:
9676 0 : sprintf(str, "%d", *(int*)p);
9677 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9678 0 : break;
9679 0 : case TID_UINT64:
9680 : // encode as hex string
9681 0 : sprintf(str, "\"0x%016llx\"", *(UINT64*)p);
9682 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9683 0 : break;
9684 0 : case TID_INT64:
9685 : // encode as decimal string
9686 0 : sprintf(str, "\"%lld\"", *(INT64*)p);
9687 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9688 0 : break;
9689 0 : case TID_BOOL:
9690 0 : if (*(int*)p)
9691 0 : json_write(buffer, buffer_size, buffer_end, 0, "true", 0);
9692 : else
9693 0 : json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
9694 0 : break;
9695 0 : case TID_FLOAT: {
9696 0 : float flt = (*(float*)p);
9697 0 : if (isnan(flt))
9698 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
9699 0 : else if (isinf(flt)) {
9700 0 : if (flt > 0)
9701 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
9702 : else
9703 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
9704 0 : } else if (flt == 0)
9705 0 : json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
9706 0 : else if (flt == (int)flt) {
9707 0 : sprintf(str, "%.0f", flt);
9708 0 : json_ensure_decimal_dot(str);
9709 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9710 : } else {
9711 0 : sprintf(str, "%.7e", flt);
9712 0 : json_ensure_decimal_dot(str);
9713 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9714 : }
9715 0 : break;
9716 : }
9717 0 : case TID_DOUBLE: {
9718 0 : double dbl = (*(double*)p);
9719 0 : if (isnan(dbl))
9720 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
9721 0 : else if (isinf(dbl)) {
9722 0 : if (dbl > 0)
9723 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
9724 : else
9725 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
9726 0 : } else if (dbl == 0)
9727 0 : json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
9728 0 : else if (dbl == (int)dbl) {
9729 0 : sprintf(str, "%.0f", dbl);
9730 0 : json_ensure_decimal_dot(str);
9731 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9732 : } else {
9733 0 : sprintf(str, "%.16e", dbl);
9734 0 : json_ensure_decimal_dot(str);
9735 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9736 : }
9737 0 : break;
9738 : }
9739 0 : case TID_BITFIELD:
9740 0 : json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
9741 0 : break;
9742 0 : case TID_STRING:
9743 : // data is already NUL terminated // p[key.item_size-1] = 0; // make sure string is NUL terminated!
9744 0 : json_write(buffer, buffer_size, buffer_end, 0, p, 1);
9745 0 : break;
9746 0 : case TID_ARRAY:
9747 0 : json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
9748 0 : break;
9749 0 : case TID_STRUCT:
9750 0 : json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
9751 0 : break;
9752 0 : case TID_KEY:
9753 0 : json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
9754 0 : break;
9755 0 : case TID_LINK:
9756 : // data is already NUL terminated // p[key.item_size-1] = 0; // make sure string is NUL terminated!
9757 0 : json_write(buffer, buffer_size, buffer_end, 0, p, 1);
9758 0 : break;
9759 0 : default:
9760 0 : json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
9761 : }
9762 0 : }
9763 :
9764 0 : static 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 0 : json_write(buffer, buffer_size, buffer_end, 0, "{ ", 0);
9769 :
9770 0 : sprintf(str, "\"type\" : %d", key->type);
9771 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9772 :
9773 0 : if (link_path) {
9774 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9775 0 : json_write(buffer, buffer_size, buffer_end, 0, "link", 1);
9776 0 : json_write(buffer, buffer_size, buffer_end, 0, ": ", 0);
9777 0 : json_write(buffer, buffer_size, buffer_end, 0, link_path, 1);
9778 : }
9779 :
9780 0 : if (key->num_values > 1) {
9781 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9782 :
9783 0 : sprintf(str, "\"num_values\" : %d", key->num_values);
9784 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9785 : }
9786 :
9787 0 : if (key->type == TID_STRING) {
9788 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9789 :
9790 0 : sprintf(str, "\"item_size\" : %d", key->item_size);
9791 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9792 : }
9793 :
9794 0 : if (key->notify_count > 0) {
9795 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9796 :
9797 0 : sprintf(str, "\"notify_count\" : %d", key->notify_count);
9798 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9799 : }
9800 :
9801 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9802 :
9803 0 : sprintf(str, "\"access_mode\" : %d", key->access_mode);
9804 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9805 :
9806 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9807 :
9808 0 : sprintf(str, "\"last_written\" : %d", key->last_written);
9809 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9810 :
9811 0 : json_write(buffer, buffer_size, buffer_end, 0, " ", 0);
9812 :
9813 0 : json_write(buffer, buffer_size, buffer_end, 0, "}", 0);
9814 0 : }
9815 :
9816 0 : 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)
9817 : {
9818 : INT i, size, status;
9819 : char *data;
9820 : KEY key;
9821 : KEY link_key;
9822 : char link_path[MAX_ODB_PATH];
9823 0 : 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 0 : if (level < 0) {
9828 0 : level = 0;
9829 0 : omit_top_level_braces = 1;
9830 : }
9831 :
9832 0 : status = db_get_link(hDB, hKey, &key);
9833 :
9834 0 : if (status != DB_SUCCESS)
9835 0 : return status;
9836 :
9837 0 : link_key = key;
9838 :
9839 0 : if (key.type == TID_LINK) {
9840 0 : size = sizeof(link_path);
9841 0 : status = db_get_data(hDB, hKey, link_path, &size, TID_LINK);
9842 :
9843 0 : if (status != DB_SUCCESS)
9844 0 : return status;
9845 :
9846 0 : if (follow_links) {
9847 0 : status = db_find_key(hDB, 0, link_path, &hKey);
9848 :
9849 0 : if (status != DB_SUCCESS)
9850 0 : return status;
9851 :
9852 0 : status = db_get_key(hDB, hKey, &key);
9853 :
9854 0 : if (status != DB_SUCCESS)
9855 0 : 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 0 : if (key.type == TID_KEY && (recurse || level<=0)) {
9862 0 : int idx = 0;
9863 0 : int do_close_curly_bracket = 0;
9864 :
9865 0 : if (level == 0 && !omit_top_level_braces) {
9866 0 : json_write(buffer, buffer_size, buffer_end, 0, "{\n", 0);
9867 0 : do_close_curly_bracket = 1;
9868 : }
9869 0 : else if (level > 0) {
9870 0 : json_write(buffer, buffer_size, buffer_end, level, link_key.name, 1);
9871 0 : json_write(buffer, buffer_size, buffer_end, 0, " : {\n", 0);
9872 0 : do_close_curly_bracket = 1;
9873 : }
9874 :
9875 0 : if (level > 100) {
9876 0 : std::string path = db_get_path(hDB, hKey);
9877 :
9878 0 : json_write(buffer, buffer_size, buffer_end, 0, "/error", 1);
9879 0 : json_write(buffer, buffer_size, buffer_end, 0, " : ", 0);
9880 0 : json_write(buffer, buffer_size, buffer_end, 0, "max nesting level exceed", 1);
9881 :
9882 0 : 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 0 : } else {
9885 : HNDLE hSubkey;
9886 :
9887 0 : for (;; idx++) {
9888 0 : db_enum_link(hDB, hKey, idx, &hSubkey);
9889 :
9890 0 : if (!hSubkey)
9891 0 : break;
9892 :
9893 0 : if (idx != 0) {
9894 0 : json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
9895 : }
9896 :
9897 : /* save subtree */
9898 0 : status = db_save_json_key_obsolete(hDB, hSubkey, level + 1, buffer, buffer_size, buffer_end, save_keys, follow_links, recurse);
9899 0 : if (status != DB_SUCCESS)
9900 0 : return status;
9901 : }
9902 : }
9903 :
9904 0 : if (do_close_curly_bracket) {
9905 0 : if (idx > 0)
9906 0 : json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
9907 0 : json_write(buffer, buffer_size, buffer_end, level, "}", 0);
9908 : }
9909 :
9910 0 : } else {
9911 :
9912 0 : if (save_keys && level == 0) {
9913 0 : json_write(buffer, buffer_size, buffer_end, 0, "{\n", 0);
9914 : }
9915 :
9916 : /* save key value */
9917 :
9918 0 : if (save_keys == 1) {
9919 : char str[NAME_LENGTH+15];
9920 0 : sprintf(str, "%s/key", link_key.name);
9921 :
9922 0 : json_write(buffer, buffer_size, buffer_end, level, str, 1);
9923 0 : json_write(buffer, buffer_size, buffer_end, 0, " : { ", 0);
9924 :
9925 0 : sprintf(str, "\"type\" : %d", key.type);
9926 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9927 :
9928 0 : if (link_key.type == TID_LINK && follow_links) {
9929 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9930 0 : json_write(buffer, buffer_size, buffer_end, 0, "link", 1);
9931 0 : json_write(buffer, buffer_size, buffer_end, 0, ": ", 0);
9932 0 : json_write(buffer, buffer_size, buffer_end, 0, link_path, 1);
9933 : }
9934 :
9935 0 : if (key.num_values > 1) {
9936 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9937 :
9938 0 : sprintf(str, "\"num_values\" : %d", key.num_values);
9939 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9940 : }
9941 :
9942 0 : if (key.type == TID_STRING || key.type == TID_LINK) {
9943 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9944 :
9945 0 : sprintf(str, "\"item_size\" : %d", key.item_size);
9946 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9947 : }
9948 :
9949 0 : if (key.notify_count > 0) {
9950 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9951 :
9952 0 : sprintf(str, "\"notify_count\" : %d", key.notify_count);
9953 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9954 : }
9955 :
9956 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9957 :
9958 0 : sprintf(str, "\"access_mode\" : %d", key.access_mode);
9959 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9960 :
9961 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
9962 :
9963 0 : sprintf(str, "\"last_written\" : %d", key.last_written);
9964 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9965 :
9966 0 : json_write(buffer, buffer_size, buffer_end, 0, " ", 0);
9967 :
9968 0 : json_write(buffer, buffer_size, buffer_end, 0, "}", 0);
9969 :
9970 0 : json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
9971 : }
9972 :
9973 0 : if (save_keys == 2) {
9974 : char str[NAME_LENGTH+15];
9975 0 : sprintf(str, "%s/last_written", link_key.name);
9976 :
9977 0 : json_write(buffer, buffer_size, buffer_end, level, str, 1);
9978 :
9979 0 : sprintf(str, " : %d", key.last_written);
9980 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
9981 :
9982 0 : json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
9983 : }
9984 :
9985 0 : if (save_keys) {
9986 0 : json_write(buffer, buffer_size, buffer_end, level, link_key.name, 1);
9987 0 : json_write(buffer, buffer_size, buffer_end, 0, " : ", 0);
9988 : }
9989 :
9990 0 : if (key.num_values > 1) {
9991 0 : json_write(buffer, buffer_size, buffer_end, 0, "[ ", 0);
9992 : }
9993 :
9994 0 : size = key.total_size;
9995 0 : data = (char *) malloc(size);
9996 0 : if (data == NULL) {
9997 0 : cm_msg(MERROR, "db_save_json_key", "cannot allocate data buffer for %d bytes", size);
9998 0 : return DB_NO_MEMORY;
9999 : }
10000 :
10001 0 : if (key.type != TID_KEY) {
10002 0 : if (follow_links)
10003 0 : status = db_get_data(hDB, hKey, data, &size, key.type);
10004 : else
10005 0 : status = db_get_link_data(hDB, hKey, data, &size, key.type);
10006 :
10007 0 : if (status != DB_SUCCESS)
10008 0 : return status;
10009 : }
10010 :
10011 0 : for (i = 0; i < key.num_values; i++) {
10012 : char str[256];
10013 0 : char *p = data + key.item_size*i;
10014 :
10015 0 : if (i != 0)
10016 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10017 :
10018 0 : switch (key.type) {
10019 0 : case TID_UINT8:
10020 0 : sprintf(str, "%u", *(unsigned char*)p);
10021 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10022 0 : break;
10023 0 : case TID_INT8:
10024 0 : sprintf(str, "%d", *(char*)p);
10025 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10026 0 : break;
10027 0 : case TID_CHAR:
10028 0 : sprintf(str, "%c", *(char*)p);
10029 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 1);
10030 0 : break;
10031 0 : case TID_UINT16:
10032 0 : sprintf(str, "\"0x%04x\"", *(WORD*)p);
10033 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10034 0 : break;
10035 0 : case TID_INT16:
10036 0 : sprintf(str, "%d", *(short*)p);
10037 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10038 0 : break;
10039 0 : case TID_UINT32:
10040 0 : sprintf(str, "\"0x%08x\"", *(DWORD*)p);
10041 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10042 0 : break;
10043 0 : case TID_INT32:
10044 0 : sprintf(str, "%d", *(int*)p);
10045 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10046 0 : break;
10047 0 : case TID_UINT64:
10048 0 : sprintf(str, "\"0x%08llx\"", *(UINT64*)p);
10049 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10050 0 : break;
10051 0 : case TID_INT64:
10052 0 : sprintf(str, "%lld", *(INT64*)p);
10053 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10054 0 : break;
10055 0 : case TID_BOOL:
10056 0 : if (*(int*)p)
10057 0 : json_write(buffer, buffer_size, buffer_end, 0, "true", 0);
10058 : else
10059 0 : json_write(buffer, buffer_size, buffer_end, 0, "false", 0);
10060 0 : break;
10061 0 : case TID_FLOAT: {
10062 0 : float flt = (*(float*)p);
10063 0 : if (isnan(flt))
10064 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
10065 0 : else if (isinf(flt)) {
10066 0 : if (flt > 0)
10067 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
10068 : else
10069 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
10070 0 : } else if (flt == 0)
10071 0 : json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10072 0 : else if (flt == (int)flt) {
10073 0 : sprintf(str, "%.0f", flt);
10074 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10075 : } else {
10076 0 : sprintf(str, "%.7e", flt);
10077 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10078 : }
10079 0 : break;
10080 : }
10081 0 : case TID_DOUBLE: {
10082 0 : double dbl = (*(double*)p);
10083 0 : if (isnan(dbl))
10084 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"NaN\"", 0);
10085 0 : else if (isinf(dbl)) {
10086 0 : if (dbl > 0)
10087 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"Infinity\"", 0);
10088 : else
10089 0 : json_write(buffer, buffer_size, buffer_end, 0, "\"-Infinity\"", 0);
10090 0 : } else if (dbl == 0)
10091 0 : json_write(buffer, buffer_size, buffer_end, 0, "0", 0);
10092 0 : else if (dbl == (int)dbl) {
10093 0 : sprintf(str, "%.0f", dbl);
10094 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10095 : } else {
10096 0 : sprintf(str, "%.16e", dbl);
10097 0 : json_write(buffer, buffer_size, buffer_end, 0, str, 0);
10098 : }
10099 0 : break;
10100 : }
10101 0 : case TID_BITFIELD:
10102 0 : json_write(buffer, buffer_size, buffer_end, 0, "(TID_BITFIELD value)", 1);
10103 0 : break;
10104 0 : case TID_STRING:
10105 0 : p[key.item_size-1] = 0; // make sure string is NUL terminated!
10106 0 : json_write(buffer, buffer_size, buffer_end, 0, p, 1);
10107 0 : break;
10108 0 : case TID_ARRAY:
10109 0 : json_write(buffer, buffer_size, buffer_end, 0, "(TID_ARRAY value)", 1);
10110 0 : break;
10111 0 : case TID_STRUCT:
10112 0 : json_write(buffer, buffer_size, buffer_end, 0, "(TID_STRUCT value)", 1);
10113 0 : break;
10114 0 : case TID_KEY:
10115 0 : json_write(buffer, buffer_size, buffer_end, 0, "{ }", 0);
10116 0 : break;
10117 0 : case TID_LINK:
10118 0 : p[key.item_size-1] = 0; // make sure string is NUL terminated!
10119 0 : json_write(buffer, buffer_size, buffer_end, 0, p, 1);
10120 0 : break;
10121 0 : default:
10122 0 : json_write(buffer, buffer_size, buffer_end, 0, "(TID_UNKNOWN value)", 1);
10123 : }
10124 :
10125 : }
10126 :
10127 0 : if (key.num_values > 1) {
10128 0 : json_write(buffer, buffer_size, buffer_end, 0, " ]", 0);
10129 : } else {
10130 0 : json_write(buffer, buffer_size, buffer_end, 0, "", 0);
10131 : }
10132 :
10133 0 : free(data);
10134 0 : data = NULL;
10135 :
10136 0 : if (save_keys && level == 0) {
10137 0 : json_write(buffer, buffer_size, buffer_end, 0, "\n}", 0);
10138 : }
10139 : }
10140 :
10141 0 : return DB_SUCCESS;
10142 : }
10143 :
10144 : /********************************************************************/
10145 : /**
10146 : Copy an ODB array in JSON format to a buffer
10147 :
10148 : @param hDB ODB handle obtained via cm_get_experiment_database().
10149 : @param hKey Handle for key
10150 : @param buffer returns pointer to ASCII buffer with ODB contents
10151 : @param buffer_size returns size of ASCII buffer
10152 : @param buffer_end returns number of bytes contained in buffer
10153 : @return DB_SUCCESS, DB_NO_MEMORY
10154 : */
10155 0 : INT 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 :
10163 0 : status = db_get_key(hDB, hKey, &key);
10164 0 : if (status != DB_SUCCESS)
10165 0 : return status;
10166 :
10167 0 : assert(key.type != TID_KEY);
10168 :
10169 0 : if (key.num_values > 1) {
10170 0 : json_write(buffer, buffer_size, buffer_end, 0, "[ ", 0);
10171 : }
10172 :
10173 0 : size = key.total_size;
10174 :
10175 0 : asize = size;
10176 0 : if (asize < 1024)
10177 0 : asize = 1024;
10178 :
10179 0 : data = (char *) malloc(asize);
10180 0 : if (data == NULL) {
10181 0 : cm_msg(MERROR, "db_save_json_key_data", "cannot allocate data buffer for %d bytes", asize);
10182 0 : return DB_NO_MEMORY;
10183 : }
10184 :
10185 0 : data[0] = 0; // protect against TID_STRING that has key.total_size == 0.
10186 :
10187 0 : status = db_get_data(hDB, hKey, data, &size, key.type);
10188 0 : if (status != DB_SUCCESS) {
10189 0 : free(data);
10190 0 : return status;
10191 : }
10192 :
10193 0 : for (i = 0; i < key.num_values; i++) {
10194 0 : char *p = data + key.item_size*i;
10195 :
10196 0 : if (i != 0)
10197 0 : json_write(buffer, buffer_size, buffer_end, 0, ", ", 0);
10198 :
10199 0 : json_write_data(buffer, buffer_size, buffer_end, 0, &key, p);
10200 : }
10201 :
10202 0 : if (key.num_values > 1) {
10203 0 : json_write(buffer, buffer_size, buffer_end, 0, " ]", 0);
10204 : }
10205 :
10206 0 : free(data);
10207 0 : data = NULL;
10208 :
10209 0 : return DB_SUCCESS;
10210 : }
10211 :
10212 : /********************************************************************/
10213 : /**
10214 : Copy an ODB array element in JSON format to a buffer
10215 :
10216 : @param hDB ODB handle obtained via cm_get_experiment_database().
10217 : @param hKey Handle for key
10218 : @param index Array index
10219 : @param buffer returns pointer to ASCII buffer with ODB contents
10220 : @param buffer_size returns size of ASCII buffer
10221 : @param buffer_end returns number of bytes contained in buffer
10222 : @return DB_SUCCESS, DB_NO_MEMORY
10223 : */
10224 0 : INT 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 :
10229 0 : status = db_get_key(hDB, hKey, &key);
10230 :
10231 0 : if (status != DB_SUCCESS)
10232 0 : return status;
10233 :
10234 0 : int size = key.item_size;
10235 0 : char* data = (char*)malloc(size + 1); // extra byte for string NUL termination
10236 0 : assert(data != NULL);
10237 :
10238 0 : status = db_get_data_index(hDB, hKey, data, &size, index, key.type);
10239 :
10240 0 : if (status != DB_SUCCESS) {
10241 0 : free(data);
10242 0 : return status;
10243 : }
10244 :
10245 0 : assert(size <= key.item_size);
10246 0 : data[key.item_size] = 0; // make sure data is NUL terminated, in case of strings.
10247 :
10248 0 : json_write_data(buffer, buffer_size, buffer_end, 0, &key, data);
10249 :
10250 0 : free(data);
10251 :
10252 0 : return DB_SUCCESS;
10253 : }
10254 :
10255 0 : 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)
10256 : {
10257 : int status;
10258 : char link_buf[MAX_ODB_PATH]; // link_path points to link_buf
10259 0 : char* link_path = NULL;
10260 :
10261 0 : HNDLE hLinkTarget = hLink;
10262 :
10263 0 : if (link.type == TID_LINK) {
10264 0 : int size = sizeof(link_buf);
10265 0 : status = db_get_link_data(hDB, hLink, link_buf, &size, TID_LINK);
10266 0 : if (status != DB_SUCCESS)
10267 0 : return status;
10268 :
10269 : //printf("link size %d, len %d, data [%s]\n", size, (int)strlen(link_buf), link_buf);
10270 :
10271 0 : if ((size > 0) && (strlen(link_buf) > 0)) {
10272 0 : link_path = link_buf;
10273 :
10274 : // resolve the link, unless it is a link to an array element
10275 0 : if ((flags & JSFLAG_FOLLOW_LINKS) && strchr(link_path, '[') == NULL) {
10276 0 : status = db_find_key(hDB, 0, link_path, &hLinkTarget);
10277 0 : if (status != DB_SUCCESS) {
10278 : // dangling link to nowhere
10279 0 : hLinkTarget = hLink;
10280 : }
10281 : }
10282 : }
10283 : }
10284 :
10285 : KEY link_target;
10286 0 : status = db_get_key(hDB, hLinkTarget, &link_target);
10287 0 : if (status != DB_SUCCESS)
10288 0 : return status;
10289 :
10290 0 : if (flags & JSFLAG_OMIT_OLD) {
10291 0 : if (link_target.last_written) {
10292 0 : if (link_target.last_written < timestamp) {
10293 : // omit old data
10294 0 : return DB_SUCCESS;
10295 : }
10296 : }
10297 : }
10298 :
10299 0 : if (need_comma) {
10300 0 : json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10301 : } else {
10302 0 : json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10303 : }
10304 :
10305 : char link_name[MAX_ODB_PATH];
10306 :
10307 0 : mstrlcpy(link_name, link.name, sizeof(link_name));
10308 :
10309 0 : if (flags & JSFLAG_LOWERCASE) {
10310 0 : for (int i=0; (i<(int)sizeof(link.name)) && link.name[i]; i++)
10311 0 : link_name[i] = tolower(link.name[i]);
10312 : }
10313 :
10314 0 : if ((flags & JSFLAG_LOWERCASE) && !(flags & JSFLAG_OMIT_NAMES)) {
10315 : char buf[MAX_ODB_PATH];
10316 0 : mstrlcpy(buf, link_name, sizeof(buf));
10317 0 : mstrlcat(buf, "/name", sizeof(buf));
10318 0 : json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10319 0 : json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10320 0 : json_write(buffer, buffer_size, buffer_end, 0, link.name, 1);
10321 0 : json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10322 : }
10323 :
10324 0 : if (link.type != TID_KEY && (flags & JSFLAG_SAVE_KEYS)) {
10325 : char buf[MAX_ODB_PATH];
10326 0 : mstrlcpy(buf, link_name, sizeof(buf));
10327 0 : mstrlcat(buf, "/key", sizeof(buf));
10328 0 : json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10329 0 : json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10330 0 : json_write_key(hDB, hLink, &link_target, link_path, buffer, buffer_size, buffer_end);
10331 0 : json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10332 0 : } else if ((link_target.type != TID_KEY) && !(flags & JSFLAG_OMIT_LAST_WRITTEN)) {
10333 : char buf[MAX_ODB_PATH];
10334 0 : mstrlcpy(buf, link_name, sizeof(buf));
10335 0 : mstrlcat(buf, "/last_written", sizeof(buf));
10336 0 : json_write(buffer, buffer_size, buffer_end, level, buf, 1);
10337 0 : json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10338 0 : sprintf(buf, "%d", link_target.last_written);
10339 0 : json_write(buffer, buffer_size, buffer_end, 0, buf, 0);
10340 0 : json_write(buffer, buffer_size, buffer_end, 0, ",\n", 0);
10341 : }
10342 :
10343 0 : json_write(buffer, buffer_size, buffer_end, level, link_name, 1);
10344 0 : json_write(buffer, buffer_size, buffer_end, 0, " : " , 0);
10345 :
10346 0 : if (link_target.type == TID_KEY && !(flags & JSFLAG_RECURSE)) {
10347 0 : json_write(buffer, buffer_size, buffer_end, 0, "{ }" , 0);
10348 : } else {
10349 0 : status = json_write_anything(hDB, hLinkTarget, buffer, buffer_size, buffer_end, level, 0, flags, timestamp);
10350 0 : if (status != DB_SUCCESS)
10351 0 : return status;
10352 : }
10353 :
10354 0 : return DB_SUCCESS;
10355 : }
10356 :
10357 0 : int 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 0 : 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 0 : cm_msg(MERROR, "json_write_bare_subdir", "Max ODB subdirectory nesting level exceeded %d", level);
10363 0 : return DB_TRUNCATED;
10364 : }
10365 :
10366 0 : for (int i=0; ; i++) {
10367 : int status;
10368 :
10369 : HNDLE hLink;
10370 0 : status = db_enum_link(hDB, hKey, i, &hLink);
10371 0 : if (status != DB_SUCCESS && !hLink)
10372 0 : break;
10373 :
10374 : KEY link;
10375 0 : status = db_get_link(hDB, hLink, &link);
10376 0 : if (status != DB_SUCCESS)
10377 0 : return status;
10378 :
10379 0 : bool need_comma = (i!=0);
10380 :
10381 0 : status = json_write_bare_key(hDB, hLink, link, buffer, buffer_size, buffer_end, level, flags, timestamp, need_comma);
10382 0 : if (status != DB_SUCCESS)
10383 0 : return status;
10384 0 : }
10385 :
10386 0 : return DB_SUCCESS;
10387 : }
10388 :
10389 0 : 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)
10390 : {
10391 : int status;
10392 : KEY key;
10393 :
10394 0 : status = db_get_key(hDB, hKey, &key);
10395 0 : if (status != DB_SUCCESS)
10396 0 : return status;
10397 :
10398 0 : if (key.type == TID_KEY) {
10399 :
10400 0 : json_write(buffer, buffer_size, buffer_end, 0, "{", 0);
10401 :
10402 0 : status = json_write_bare_subdir(hDB, hKey, buffer, buffer_size, buffer_end, level+1, flags, timestamp);
10403 0 : if (status != DB_SUCCESS)
10404 0 : return status;
10405 :
10406 0 : json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10407 0 : json_write(buffer, buffer_size, buffer_end, level, "}", 0);
10408 :
10409 0 : } else if (must_be_subdir) {
10410 0 : json_write(buffer, buffer_size, buffer_end, 0, "{", 0);
10411 0 : status = json_write_bare_key(hDB, hKey, key, buffer, buffer_size, buffer_end, level+1, flags, timestamp, false);
10412 0 : if (status != DB_SUCCESS)
10413 0 : return status;
10414 0 : json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10415 0 : json_write(buffer, buffer_size, buffer_end, level, "}", 0);
10416 : } else {
10417 0 : status = db_copy_json_array(hDB, hKey, buffer, buffer_size, buffer_end);
10418 :
10419 0 : if (status != DB_SUCCESS)
10420 0 : return status;
10421 : }
10422 :
10423 0 : return DB_SUCCESS;
10424 : }
10425 :
10426 0 : INT db_copy_json_ls(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
10427 : {
10428 : int status;
10429 0 : bool unlock = false;
10430 :
10431 0 : if (!rpc_is_remote()) {
10432 0 : status = db_lock_database(hDB);
10433 0 : if (status != DB_SUCCESS) {
10434 0 : return status;
10435 : }
10436 0 : unlock = true;
10437 : }
10438 :
10439 0 : status = json_write_anything(hDB, hKey, buffer, buffer_size, buffer_end, JS_LEVEL_0, JS_MUST_BE_SUBDIR, JSFLAG_SAVE_KEYS|JSFLAG_FOLLOW_LINKS, 0);
10440 :
10441 0 : if (unlock) {
10442 0 : db_unlock_database(hDB);
10443 : }
10444 :
10445 0 : return status;
10446 : }
10447 :
10448 0 : 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)
10449 : {
10450 : int status;
10451 0 : int flags = JSFLAG_FOLLOW_LINKS|JSFLAG_RECURSE;
10452 0 : if (omit_names)
10453 0 : flags |= JSFLAG_OMIT_NAMES;
10454 0 : if (omit_last_written)
10455 0 : flags |= JSFLAG_OMIT_LAST_WRITTEN;
10456 0 : if (omit_old_timestamp)
10457 0 : flags |= JSFLAG_OMIT_OLD;
10458 0 : if (!preserve_case)
10459 0 : flags |= JSFLAG_LOWERCASE;
10460 :
10461 0 : bool unlock = false;
10462 :
10463 0 : if (!rpc_is_remote()) {
10464 0 : status = db_lock_database(hDB);
10465 0 : if (status != DB_SUCCESS) {
10466 0 : return status;
10467 : }
10468 0 : unlock = true;
10469 : }
10470 :
10471 0 : status = json_write_anything(hDB, hKey, buffer, buffer_size, buffer_end, JS_LEVEL_0, 0, flags, omit_old_timestamp);
10472 :
10473 0 : if (unlock) {
10474 0 : db_unlock_database(hDB);
10475 : }
10476 :
10477 0 : return status;
10478 : }
10479 :
10480 0 : INT db_copy_json_save(HNDLE hDB, HNDLE hKey, char **buffer, int* buffer_size, int* buffer_end)
10481 : {
10482 : int status;
10483 0 : bool unlock = false;
10484 :
10485 0 : if (!rpc_is_remote()) {
10486 0 : status = db_lock_database(hDB);
10487 0 : if (status != DB_SUCCESS) {
10488 0 : return status;
10489 : }
10490 0 : unlock = true;
10491 : }
10492 :
10493 0 : status = json_write_anything(hDB, hKey, buffer, buffer_size, buffer_end, JS_LEVEL_0, JS_MUST_BE_SUBDIR, JSFLAG_SAVE_KEYS|JSFLAG_RECURSE, 0);
10494 :
10495 0 : if (unlock) {
10496 0 : db_unlock_database(hDB);
10497 : }
10498 :
10499 0 : return status;
10500 : }
10501 :
10502 : /********************************************************************/
10503 : /**
10504 : Copy an ODB subtree in JSON format to a buffer
10505 :
10506 : @param hDB ODB handle obtained via cm_get_experiment_database().
10507 : @param hKey Handle for key where search starts, zero for root.
10508 : @param buffer returns pointer to ASCII buffer with ODB contents
10509 : @param buffer_size returns size of ASCII buffer
10510 : @param buffer_end returns number of bytes contained in buffer
10511 : @return DB_SUCCESS, DB_NO_MEMORY
10512 : */
10513 0 : 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)
10514 : {
10515 0 : db_save_json_key_obsolete(hDB, hKey, 0, buffer, buffer_size, buffer_end, save_keys, follow_links, recurse);
10516 0 : json_write(buffer, buffer_size, buffer_end, 0, "\n", 0);
10517 0 : return DB_SUCCESS;
10518 : }
10519 :
10520 : /********************************************************************/
10521 : /**
10522 : Save a branch of a database to an .json file
10523 :
10524 : This function is used by the ODBEdit command save to write the contents
10525 : of the ODB into a JSON file. Data of the whole ODB can
10526 : be saved (hkey equal zero) or only a sub-tree.
10527 : @param hDB ODB handle obtained via cm_get_experiment_database().
10528 : @param hKey Handle for key where search starts, zero for root.
10529 : @param filename Filename of .json file.
10530 : @return DB_SUCCESS, DB_FILE_ERROR
10531 : */
10532 0 : INT db_save_json(HNDLE hDB, HNDLE hKey, const char *filename, int flags)
10533 : {
10534 : INT status;
10535 :
10536 : /* open file */
10537 0 : FILE *fp = fopen(filename, "w");
10538 0 : if (fp == NULL) {
10539 0 : cm_msg(MERROR, "db_save_json", "Cannot open file \"%s\", fopen() errno %d (%s)", filename, errno, strerror(errno));
10540 0 : return DB_FILE_ERROR;
10541 : }
10542 :
10543 0 : bool unlock = false;
10544 :
10545 0 : if (!rpc_is_remote()) {
10546 0 : status = db_lock_database(hDB);
10547 0 : if (status != DB_SUCCESS) {
10548 0 : fclose(fp);
10549 0 : return status;
10550 : }
10551 0 : unlock = true;
10552 : }
10553 :
10554 0 : std::string path = db_get_path(hDB, hKey);
10555 :
10556 0 : bool emptySubdir = false;
10557 : HNDLE hSubKey;
10558 0 : status = db_enum_link(hDB, hKey, 0, &hSubKey);
10559 0 : if (status == DB_NO_MORE_SUBKEYS)
10560 0 : emptySubdir = true;
10561 :
10562 0 : char* buffer = NULL;
10563 0 : int buffer_size = 0;
10564 0 : int buffer_end = 0;
10565 :
10566 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, "{\n", 0);
10567 :
10568 0 : json_write(&buffer, &buffer_size, &buffer_end, 1, "/MIDAS version", 1);
10569 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10570 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, MIDAS_VERSION, 1);
10571 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10572 :
10573 0 : json_write(&buffer, &buffer_size, &buffer_end, 1, "/MIDAS git revision", 1);
10574 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10575 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, GIT_REVISION, 1);
10576 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10577 :
10578 0 : json_write(&buffer, &buffer_size, &buffer_end, 1, "/filename", 1);
10579 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10580 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, filename, 1);
10581 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, ",\n", 0);
10582 :
10583 0 : json_write(&buffer, &buffer_size, &buffer_end, 1, "/ODB path", 1);
10584 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, " : ", 0);
10585 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, path.c_str(), 1);
10586 :
10587 0 : if (emptySubdir)
10588 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, "", 0);
10589 : else
10590 0 : 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 0 : status = json_write_bare_subdir(hDB, hKey, &buffer, &buffer_size, &buffer_end, JS_LEVEL_1, flags, 0);
10594 :
10595 0 : json_write(&buffer, &buffer_size, &buffer_end, 0, "\n}\n", 0);
10596 :
10597 0 : if (unlock) {
10598 0 : db_unlock_database(hDB);
10599 : }
10600 :
10601 0 : if (status == DB_SUCCESS) {
10602 0 : if (buffer) {
10603 0 : size_t wr = fwrite(buffer, 1, buffer_end, fp);
10604 0 : if (wr != (size_t)buffer_end) {
10605 0 : cm_msg(MERROR, "db_save_json", "Cannot write to file \"%s\", fwrite() errno %d (%s)", filename, errno, strerror(errno));
10606 0 : free(buffer);
10607 0 : fclose(fp);
10608 0 : return DB_FILE_ERROR;
10609 : }
10610 : }
10611 : }
10612 :
10613 0 : if (buffer)
10614 0 : free(buffer);
10615 :
10616 0 : fclose(fp);
10617 :
10618 0 : return DB_SUCCESS;
10619 0 : }
10620 :
10621 : /********************************************************************/
10622 : /**
10623 : Save a branch of a database to a C structure .H file
10624 : @param hDB ODB handle obtained via cm_get_experiment_database().
10625 : @param hKey Handle for key where search starts, zero for root.
10626 : @param file_name Filename of .ODB file.
10627 : @param struct_name Name of structure. If struct_name == NULL,
10628 : the name of the key is used.
10629 : @param append If TRUE, append to end of existing file
10630 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FILE_ERROR
10631 : */
10632 0 : INT db_save_struct(HNDLE hDB, HNDLE hKey, const char *file_name, const char *struct_name, BOOL append)
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 0 : fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
10641 :
10642 0 : if (fh == -1) {
10643 0 : cm_msg(MERROR, "db_save_struct", "Cannot open file\"%s\"", file_name);
10644 0 : return DB_FILE_ERROR;
10645 : }
10646 :
10647 0 : status = db_get_key(hDB, hKey, &key);
10648 0 : if (status != DB_SUCCESS) {
10649 0 : cm_msg(MERROR, "db_save_struct", "cannot find key");
10650 0 : return DB_INVALID_HANDLE;
10651 : }
10652 :
10653 0 : sprintf(line, "typedef struct {\n");
10654 :
10655 0 : size = strlen(line);
10656 0 : wr = write(fh, line, size);
10657 0 : if (wr != size) {
10658 0 : 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 0 : close(fh);
10660 0 : return DB_FILE_ERROR;
10661 : }
10662 :
10663 0 : db_save_tree_struct(hDB, hKey, fh, 0);
10664 :
10665 0 : if (struct_name && struct_name[0])
10666 0 : mstrlcpy(str, struct_name, sizeof(str));
10667 : else
10668 0 : mstrlcpy(str, key.name, sizeof(str));
10669 :
10670 0 : name2c(str);
10671 0 : for (i = 0; i < (int) strlen(str); i++)
10672 0 : str[i] = (char) toupper(str[i]);
10673 :
10674 0 : sprintf(line, "} %s;\n\n", str);
10675 :
10676 0 : size = strlen(line);
10677 0 : wr = write(fh, line, size);
10678 0 : if (wr != size) {
10679 0 : 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 0 : close(fh);
10681 0 : return DB_FILE_ERROR;
10682 : }
10683 :
10684 0 : close(fh);
10685 :
10686 0 : return DB_SUCCESS;
10687 : }
10688 :
10689 : /**dox***************************************************************/
10690 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
10691 :
10692 : /*------------------------------------------------------------------*/
10693 0 : INT db_save_string(HNDLE hDB, HNDLE hKey, const char *file_name, const char *string_name, BOOL append)
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 0 : char *buffer = NULL, *pc;
10721 : int wr;
10722 :
10723 :
10724 : /* open file */
10725 0 : fh = open(file_name, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0644);
10726 :
10727 0 : if (fh == -1) {
10728 0 : cm_msg(MERROR, "db_save_string", "Cannot open file\"%s\"", file_name);
10729 0 : return DB_FILE_ERROR;
10730 : }
10731 :
10732 0 : status = db_get_key(hDB, hKey, &key);
10733 0 : if (status != DB_SUCCESS) {
10734 0 : cm_msg(MERROR, "db_save_string", "cannot find key");
10735 0 : return DB_INVALID_HANDLE;
10736 : }
10737 :
10738 0 : if (string_name && string_name[0])
10739 0 : strcpy(str, string_name);
10740 : else
10741 0 : strcpy(str, key.name);
10742 :
10743 0 : name2c(str);
10744 0 : for (i = 0; i < (int) strlen(str); i++)
10745 0 : str[i] = (char) toupper(str[i]);
10746 :
10747 0 : sprintf(line, "#define %s(_name) const char *_name[] = {\\\n", str);
10748 0 : size = strlen(line);
10749 0 : wr = write(fh, line, size);
10750 0 : if (wr != size) {
10751 0 : cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10752 0 : close(fh);
10753 0 : if (buffer)
10754 0 : free(buffer);
10755 0 : return DB_FILE_ERROR;
10756 : }
10757 :
10758 0 : buffer_size = 10000;
10759 : do {
10760 0 : buffer = (char *) malloc(buffer_size);
10761 0 : if (buffer == NULL) {
10762 0 : cm_msg(MERROR, "db_save", "cannot allocate ODB dump buffer");
10763 0 : break;
10764 : }
10765 :
10766 0 : size = buffer_size;
10767 0 : status = db_copy(hDB, hKey, buffer, &size, "");
10768 0 : if (status != DB_TRUNCATED)
10769 0 : break;
10770 :
10771 : /* increase buffer size if truncated */
10772 0 : free(buffer);
10773 0 : buffer = NULL;
10774 0 : buffer_size *= 2;
10775 : } while (1);
10776 :
10777 :
10778 0 : pc = buffer;
10779 :
10780 : do {
10781 0 : i = 0;
10782 0 : line[i++] = '"';
10783 0 : while (*pc != '\n' && *pc != 0) {
10784 0 : if (*pc == '\"' || *pc == '\'')
10785 0 : line[i++] = '\\';
10786 0 : line[i++] = *pc++;
10787 : }
10788 0 : strcpy(&line[i], "\",\\\n");
10789 0 : if (i > 0) {
10790 0 : size = strlen(line);
10791 0 : wr = write(fh, line, size);
10792 0 : if (wr != size) {
10793 0 : cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10794 0 : close(fh);
10795 0 : if (buffer)
10796 0 : free(buffer);
10797 0 : return DB_FILE_ERROR;
10798 : }
10799 : }
10800 :
10801 0 : if (*pc == '\n')
10802 0 : pc++;
10803 :
10804 0 : } while (*pc);
10805 :
10806 0 : sprintf(line, "NULL }\n\n");
10807 0 : size = strlen(line);
10808 0 : wr = write(fh, line, size);
10809 0 : if (wr != size) {
10810 0 : cm_msg(MERROR, "db_save", "file \"%s\" write error: write(%d) returned %d, errno %d (%s)", file_name, size, wr, errno, strerror(errno));
10811 0 : close(fh);
10812 0 : if (buffer)
10813 0 : free(buffer);
10814 0 : return DB_FILE_ERROR;
10815 : }
10816 :
10817 0 : close(fh);
10818 0 : free(buffer);
10819 :
10820 0 : return DB_SUCCESS;
10821 : }
10822 :
10823 : /**dox***************************************************************/
10824 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
10825 :
10826 : /********************************************************************/
10827 : /**
10828 : Convert a database value to a string according to its type.
10829 :
10830 : This function is a convenient way to convert a binary ODB value into a
10831 : string depending on its type if is not known at compile time. If it is known, the
10832 : normal sprintf() function can be used.
10833 : \code
10834 : ...
10835 : for (j=0 ; j<key.num_values ; j++)
10836 : {
10837 : db_sprintf(pbuf, pdata, key.item_size, j, key.type);
10838 : strcat(pbuf, "\n");
10839 : }
10840 : ...
10841 : \endcode
10842 : @param string output ASCII string of data. must be at least MAX_STRING_LENGTH bytes long.
10843 : @param data Value data.
10844 : @param data_size Size of single data element.
10845 : @param idx Index for array data.
10846 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
10847 : @return DB_SUCCESS
10848 : */
10849 0 : INT db_sprintf(char *string, const void *data, INT data_size, INT idx, DWORD type)
10850 : {
10851 0 : if (data_size == 0)
10852 0 : sprintf(string, "<NULL>");
10853 : else
10854 0 : switch (type) {
10855 0 : case TID_UINT8:
10856 0 : sprintf(string, "%d", *(((BYTE *) data) + idx));
10857 0 : break;
10858 0 : case TID_INT8:
10859 0 : sprintf(string, "%d", *(((char *) data) + idx));
10860 0 : break;
10861 0 : case TID_CHAR:
10862 0 : sprintf(string, "%c", *(((char *) data) + idx));
10863 0 : break;
10864 0 : case TID_UINT16:
10865 0 : sprintf(string, "%u", *(((WORD *) data) + idx));
10866 0 : break;
10867 0 : case TID_INT16:
10868 0 : sprintf(string, "%d", *(((short *) data) + idx));
10869 0 : break;
10870 0 : case TID_UINT32:
10871 0 : sprintf(string, "%u", *(((DWORD *) data) + idx));
10872 0 : break;
10873 0 : case TID_INT32:
10874 0 : sprintf(string, "%d", *(((INT *) data) + idx));
10875 0 : break;
10876 0 : case TID_UINT64:
10877 0 : sprintf(string, "%llu", *(((UINT64 *) data) + idx));
10878 0 : break;
10879 0 : case TID_INT64:
10880 0 : sprintf(string, "%lld", *(((INT64 *) data) + idx));
10881 0 : break;
10882 0 : case TID_BOOL:
10883 0 : sprintf(string, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
10884 0 : break;
10885 0 : case TID_FLOAT:
10886 0 : if (ss_isnan(*(((float *) data) + idx)))
10887 0 : sprintf(string, "NAN");
10888 : else
10889 0 : sprintf(string, "%.7g", *(((float *) data) + idx));
10890 0 : break;
10891 0 : case TID_DOUBLE:
10892 0 : if (ss_isnan(*(((double *) data) + idx)))
10893 0 : sprintf(string, "NAN");
10894 : else
10895 0 : sprintf(string, "%.16lg", *(((double *) data) + idx));
10896 0 : break;
10897 0 : case TID_BITFIELD:
10898 0 : sprintf(string, "%u", *(((DWORD *) data) + idx));
10899 0 : break;
10900 0 : case TID_STRING:
10901 : case TID_LINK:
10902 0 : mstrlcpy(string, ((char *) data) + data_size * idx, MAX_STRING_LENGTH);
10903 0 : break;
10904 0 : default:
10905 0 : sprintf(string, "<unknown>");
10906 0 : break;
10907 : }
10908 :
10909 0 : return DB_SUCCESS;
10910 : }
10911 :
10912 : /********************************************************************/
10913 : /**
10914 : Same as db_sprintf, but with additional format parameter
10915 :
10916 : @param string output ASCII string of data.
10917 : @param format Format specifier passed to sprintf()
10918 : @param data Value data.
10919 : @param data_size Size of single data element.
10920 : @param idx Index for array data.
10921 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
10922 : @return DB_SUCCESS
10923 : */
10924 :
10925 0 : INT db_sprintff(char *string, const char *format, const void *data, INT data_size, INT idx, DWORD type)
10926 : {
10927 0 : if (data_size == 0)
10928 0 : sprintf(string, "<NULL>");
10929 : else
10930 0 : switch (type) {
10931 0 : case TID_UINT8:
10932 0 : sprintf(string, format, *(((BYTE *) data) + idx));
10933 0 : break;
10934 0 : case TID_INT8:
10935 0 : sprintf(string, format, *(((char *) data) + idx));
10936 0 : break;
10937 0 : case TID_CHAR:
10938 0 : sprintf(string, format, *(((char *) data) + idx));
10939 0 : break;
10940 0 : case TID_UINT16:
10941 0 : sprintf(string, format, *(((WORD *) data) + idx));
10942 0 : break;
10943 0 : case TID_INT16:
10944 0 : sprintf(string, format, *(((short *) data) + idx));
10945 0 : break;
10946 0 : case TID_UINT32:
10947 0 : sprintf(string, format, *(((DWORD *) data) + idx));
10948 0 : break;
10949 0 : case TID_INT32:
10950 0 : sprintf(string, format, *(((INT *) data) + idx));
10951 0 : break;
10952 0 : case TID_UINT64:
10953 0 : sprintf(string, format, *(((UINT64 *) data) + idx));
10954 0 : break;
10955 0 : case TID_INT64:
10956 0 : sprintf(string, format, *(((INT64 *) data) + idx));
10957 0 : break;
10958 0 : case TID_BOOL:
10959 0 : sprintf(string, format, *(((BOOL *) data) + idx) ? 'y' : 'n');
10960 0 : break;
10961 0 : case TID_FLOAT:
10962 0 : if (ss_isnan(*(((float *) data) + idx)))
10963 0 : sprintf(string, "NAN");
10964 : else
10965 0 : sprintf(string, format, *(((float *) data) + idx));
10966 0 : break;
10967 0 : case TID_DOUBLE:
10968 0 : if (ss_isnan(*(((double *) data) + idx)))
10969 0 : sprintf(string, "NAN");
10970 : else
10971 0 : sprintf(string, format, *(((double *) data) + idx));
10972 0 : break;
10973 0 : case TID_BITFIELD:
10974 0 : sprintf(string, format, *(((DWORD *) data) + idx));
10975 0 : break;
10976 0 : case TID_STRING:
10977 : case TID_LINK:
10978 0 : mstrlcpy(string, ((char *) data) + data_size * idx, MAX_STRING_LENGTH);
10979 0 : break;
10980 0 : default:
10981 0 : sprintf(string, "<unknown>");
10982 0 : break;
10983 : }
10984 :
10985 0 : return DB_SUCCESS;
10986 : }
10987 :
10988 : /*------------------------------------------------------------------*/
10989 0 : INT 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 0 : if (data_size == 0)
11012 0 : sprintf(string, "<NULL>");
11013 : else
11014 0 : switch (type) {
11015 0 : case TID_UINT8:
11016 0 : sprintf(string, "0x%X", *(((BYTE *) data) + idx));
11017 0 : break;
11018 0 : case TID_INT8:
11019 0 : sprintf(string, "0x%X", *(((char *) data) + idx));
11020 0 : break;
11021 0 : case TID_CHAR:
11022 0 : sprintf(string, "%c", *(((char *) data) + idx));
11023 0 : break;
11024 0 : case TID_UINT16:
11025 0 : sprintf(string, "0x%X", *(((WORD *) data) + idx));
11026 0 : break;
11027 0 : case TID_INT16:
11028 0 : sprintf(string, "0x%hX", *(((short *) data) + idx));
11029 0 : break;
11030 0 : case TID_UINT32:
11031 0 : sprintf(string, "0x%X", *(((DWORD *) data) + idx));
11032 0 : break;
11033 0 : case TID_INT32:
11034 0 : sprintf(string, "0x%X", *(((INT *) data) + idx));
11035 0 : break;
11036 0 : case TID_UINT64:
11037 0 : sprintf(string, "0x%llX", *(((UINT64 *) data) + idx));
11038 0 : break;
11039 0 : case TID_INT64:
11040 0 : sprintf(string, "0x%llX", *(((INT64 *) data) + idx));
11041 0 : break;
11042 0 : case TID_BOOL:
11043 0 : sprintf(string, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11044 0 : break;
11045 0 : case TID_FLOAT:
11046 0 : if (ss_isnan(*(((float *) data) + idx)))
11047 0 : sprintf(string, "NAN");
11048 : else
11049 0 : sprintf(string, "%.7g", *(((float *) data) + idx));
11050 0 : break;
11051 0 : case TID_DOUBLE:
11052 0 : if (ss_isnan(*(((double *) data) + idx)))
11053 0 : sprintf(string, "NAN");
11054 : else
11055 0 : sprintf(string, "%.16lg", *(((double *) data) + idx));
11056 0 : break;
11057 0 : case TID_BITFIELD:
11058 0 : sprintf(string, "0x%X", *(((DWORD *) data) + idx));
11059 0 : break;
11060 0 : case TID_STRING:
11061 : case TID_LINK:
11062 0 : sprintf(string, "%s", ((char *) data) + data_size * idx);
11063 0 : break;
11064 0 : default:
11065 0 : sprintf(string, "<unknown>");
11066 0 : break;
11067 : }
11068 :
11069 0 : return DB_SUCCESS;
11070 : }
11071 :
11072 : /********************************************************************/
11073 : /**
11074 : Convert a database value to a string according to its type.
11075 :
11076 : This function is a convenient way to convert a binary ODB value into a
11077 : string depending on its type if is not known at compile time. If it is known, the
11078 : normal sprintf() function can be used.
11079 : \code
11080 : ...
11081 : for (j=0 ; j<key.num_values ; j++)
11082 : {
11083 : std::string s = db_sprintf(pdata, key.item_size, j, key.type);
11084 : }
11085 : ...
11086 : \endcode
11087 : @param data Value data.
11088 : @param data_size Size of single data element.
11089 : @param idx Index for array data.
11090 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
11091 : @return string output ASCII string of data.
11092 : */
11093 20 : std::string db_sprintf(const void *data, INT data_size, INT idx, DWORD type)
11094 : {
11095 20 : if (data_size == 0) {
11096 0 : return "<NULL>";
11097 : } else {
11098 : char buf[256];
11099 20 : switch (type) {
11100 0 : case TID_UINT8:
11101 0 : sprintf(buf, "%d", *(((BYTE *) data) + idx));
11102 0 : return buf;
11103 0 : case TID_INT8:
11104 0 : sprintf(buf, "%d", *(((char *) data) + idx));
11105 0 : return buf;
11106 0 : case TID_CHAR:
11107 0 : sprintf(buf, "%c", *(((char *) data) + idx));
11108 0 : return buf;
11109 0 : case TID_UINT16:
11110 0 : sprintf(buf, "%u", *(((WORD *) data) + idx));
11111 0 : return buf;
11112 0 : case TID_INT16:
11113 0 : sprintf(buf, "%d", *(((short *) data) + idx));
11114 0 : return buf;
11115 4 : case TID_UINT32:
11116 4 : sprintf(buf, "%u", *(((DWORD *) data) + idx));
11117 8 : return buf;
11118 4 : case TID_INT32:
11119 4 : sprintf(buf, "%d", *(((INT *) data) + idx));
11120 8 : return buf;
11121 0 : case TID_UINT64:
11122 0 : sprintf(buf, "%llu", *(((UINT64 *) data) + idx));
11123 0 : return buf;
11124 0 : case TID_INT64:
11125 0 : sprintf(buf, "%lld", *(((INT64 *) data) + idx));
11126 0 : return buf;
11127 8 : case TID_BOOL:
11128 8 : sprintf(buf, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11129 16 : return buf;
11130 0 : case TID_FLOAT:
11131 0 : if (ss_isnan(*(((float *) data) + idx))) {
11132 0 : return "NAN";
11133 : } else {
11134 0 : sprintf(buf, "%.7g", *(((float *) data) + idx));
11135 0 : return buf;
11136 : }
11137 0 : case TID_DOUBLE:
11138 0 : if (ss_isnan(*(((double *) data) + idx))) {
11139 0 : return "NAN";
11140 : } else {
11141 0 : sprintf(buf, "%.16lg", *(((double *) data) + idx));
11142 0 : return buf;
11143 : }
11144 0 : case TID_BITFIELD:
11145 0 : sprintf(buf, "%u", *(((DWORD *) data) + idx));
11146 0 : return buf;
11147 4 : case TID_STRING:
11148 : case TID_LINK:
11149 8 : return (((char *) data) + data_size * idx);
11150 0 : default:
11151 0 : return "<unknown>";
11152 : }
11153 : }
11154 : }
11155 :
11156 : /********************************************************************/
11157 : /**
11158 : Same as db_sprintf, but with additional format parameter
11159 :
11160 : @param string output ASCII string of data.
11161 : @param format Format specifier passed to sprintf()
11162 : @param data Value data.
11163 : @param data_size Size of single data element.
11164 : @param idx Index for array data.
11165 : @param type Type of key, one of TID_xxx (see @ref Midas_Data_Types).
11166 : @return DB_SUCCESS
11167 : */
11168 :
11169 0 : std::string db_sprintff(const char *format, const void *data, INT data_size, INT idx, DWORD type)
11170 : {
11171 0 : if (data_size == 0) {
11172 0 : return "<NULL>";
11173 : } else {
11174 : char buf[256];
11175 0 : switch (type) {
11176 0 : case TID_UINT8:
11177 0 : sprintf(buf, format, *(((BYTE *) data) + idx));
11178 0 : return buf;
11179 0 : case TID_INT8:
11180 0 : sprintf(buf, format, *(((char *) data) + idx));
11181 0 : return buf;
11182 0 : case TID_CHAR:
11183 0 : sprintf(buf, format, *(((char *) data) + idx));
11184 0 : return buf;
11185 0 : case TID_UINT16:
11186 0 : sprintf(buf, format, *(((WORD *) data) + idx));
11187 0 : return buf;
11188 0 : case TID_INT16:
11189 0 : sprintf(buf, format, *(((short *) data) + idx));
11190 0 : return buf;
11191 0 : case TID_UINT32:
11192 0 : sprintf(buf, format, *(((DWORD *) data) + idx));
11193 0 : return buf;
11194 0 : case TID_INT32:
11195 0 : sprintf(buf, format, *(((INT *) data) + idx));
11196 0 : return buf;
11197 0 : case TID_UINT64:
11198 0 : sprintf(buf, format, *(((UINT64 *) data) + idx));
11199 0 : return buf;
11200 0 : case TID_INT64:
11201 0 : sprintf(buf, format, *(((INT64 *) data) + idx));
11202 0 : return buf;
11203 0 : case TID_BOOL:
11204 0 : sprintf(buf, format, *(((BOOL *) data) + idx) ? 'y' : 'n');
11205 0 : return buf;
11206 0 : case TID_FLOAT:
11207 0 : if (ss_isnan(*(((float *) data) + idx))) {
11208 0 : return "NAN";
11209 : } else {
11210 0 : sprintf(buf, format, *(((float *) data) + idx));
11211 0 : return buf;
11212 : }
11213 0 : case TID_DOUBLE:
11214 0 : if (ss_isnan(*(((double *) data) + idx))) {
11215 0 : return "NAN";
11216 : } else {
11217 0 : sprintf(buf, format, *(((double *) data) + idx));
11218 0 : return buf;
11219 : }
11220 0 : case TID_BITFIELD:
11221 0 : sprintf(buf, format, *(((DWORD *) data) + idx));
11222 0 : return buf;
11223 0 : case TID_STRING:
11224 : case TID_LINK:
11225 0 : return (((char *) data) + data_size * idx);
11226 0 : default:
11227 0 : return "<unknown>";
11228 : }
11229 : }
11230 : }
11231 :
11232 : /**dox***************************************************************/
11233 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
11234 :
11235 : /*------------------------------------------------------------------*/
11236 0 : std::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 0 : if (data_size == 0) {
11259 0 : return "<NULL>";
11260 : } else {
11261 : char buf[256];
11262 0 : switch (type) {
11263 0 : case TID_UINT8:
11264 0 : sprintf(buf, "0x%X", *(((BYTE *) data) + idx));
11265 0 : return buf;
11266 0 : case TID_INT8:
11267 0 : sprintf(buf, "0x%X", *(((char *) data) + idx));
11268 0 : return buf;
11269 0 : case TID_CHAR:
11270 0 : sprintf(buf, "%c", *(((char *) data) + idx));
11271 0 : return buf;
11272 0 : case TID_UINT16:
11273 0 : sprintf(buf, "0x%X", *(((WORD *) data) + idx));
11274 0 : return buf;
11275 0 : case TID_INT16:
11276 0 : sprintf(buf, "0x%hX", *(((short *) data) + idx));
11277 0 : return buf;
11278 0 : case TID_UINT32:
11279 0 : sprintf(buf, "0x%X", *(((DWORD *) data) + idx));
11280 0 : return buf;
11281 0 : case TID_INT32:
11282 0 : sprintf(buf, "0x%X", *(((INT *) data) + idx));
11283 0 : return buf;
11284 0 : case TID_UINT64:
11285 0 : sprintf(buf, "0x%llX", *(((UINT64 *) data) + idx));
11286 0 : return buf;
11287 0 : case TID_INT64:
11288 0 : sprintf(buf, "0x%llX", *(((INT64 *) data) + idx));
11289 0 : return buf;
11290 0 : case TID_BOOL:
11291 0 : sprintf(buf, "%c", *(((BOOL *) data) + idx) ? 'y' : 'n');
11292 0 : return buf;
11293 0 : case TID_FLOAT:
11294 0 : if (ss_isnan(*(((float *) data) + idx))) {
11295 0 : return "NAN";
11296 : } else {
11297 0 : sprintf(buf, "%.7g", *(((float *) data) + idx));
11298 0 : return buf;
11299 : }
11300 0 : case TID_DOUBLE:
11301 0 : if (ss_isnan(*(((double *) data) + idx))) {
11302 0 : return "NAN";
11303 : } else {
11304 0 : sprintf(buf, "%.16lg", *(((double *) data) + idx));
11305 0 : return buf;
11306 : }
11307 0 : case TID_BITFIELD:
11308 0 : sprintf(buf, "0x%X", *(((DWORD *) data) + idx));
11309 0 : return buf;
11310 0 : case TID_STRING:
11311 : case TID_LINK:
11312 0 : return (((char *) data) + data_size * idx);
11313 0 : default:
11314 0 : return "<unknown>";
11315 : }
11316 : }
11317 : }
11318 :
11319 : /*------------------------------------------------------------------*/
11320 30 : INT 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 30 : DWORD value = 0;
11342 30 : BOOL hex = FALSE;
11343 :
11344 30 : if (data_str == NULL)
11345 0 : return 0;
11346 :
11347 30 : *data_size = rpc_tid_size(tid);
11348 30 : if (strncmp(data_str, "0x", 2) == 0) {
11349 0 : hex = TRUE;
11350 0 : sscanf(data_str + 2, "%x", &value);
11351 : }
11352 :
11353 30 : switch (tid) {
11354 0 : case TID_UINT8:
11355 : case TID_INT8:
11356 0 : if (hex)
11357 0 : *((char *) data + i) = (char) value;
11358 : else
11359 0 : *((char *) data + i) = (char) atoi(data_str);
11360 0 : break;
11361 0 : case TID_CHAR:
11362 0 : *((char *) data + i) = data_str[0];
11363 0 : break;
11364 0 : case TID_UINT16:
11365 0 : if (hex)
11366 0 : *((WORD *) data + i) = (WORD) value;
11367 : else
11368 0 : *((WORD *) data + i) = (WORD) atoi(data_str);
11369 0 : break;
11370 0 : case TID_INT16:
11371 0 : if (hex)
11372 0 : *((short int *) data + i) = (short int) value;
11373 : else
11374 0 : *((short int *) data + i) = (short int) atoi(data_str);
11375 0 : break;
11376 8 : case TID_UINT32:
11377 8 : if (hex)
11378 0 : value = strtoul(data_str, nullptr, 16);
11379 : else
11380 8 : value = strtoul(data_str, nullptr, 10);
11381 :
11382 8 : *((DWORD *) data + i) = value;
11383 8 : break;
11384 6 : case TID_INT32:
11385 6 : if (hex)
11386 0 : value = strtol(data_str, nullptr, 16);
11387 : else
11388 6 : value = strtol(data_str, nullptr, 10);
11389 :
11390 6 : *((INT *) data + i) = value;
11391 6 : break;
11392 0 : case TID_UINT64:
11393 0 : if (hex)
11394 0 : *((UINT64 *) data + i) = strtoull(data_str, nullptr, 16);
11395 : else
11396 0 : *((UINT64 *) data + i) = strtoull(data_str, nullptr, 10);
11397 0 : break;
11398 0 : case TID_INT64:
11399 0 : if (hex)
11400 0 : *((UINT64 *) data + i) = strtoll(data_str, nullptr, 16);
11401 : else
11402 0 : *((UINT64 *) data + i) = strtoll(data_str, nullptr, 10);
11403 0 : break;
11404 16 : case TID_BOOL:
11405 16 : if (data_str[0] == 'y' || data_str[0] == 'Y' ||
11406 16 : data_str[0] == 't' || data_str[0] == 'T' || atoi(data_str) > 0)
11407 0 : *((BOOL *) data + i) = 1;
11408 : else
11409 16 : *((BOOL *) data + i) = 0;
11410 16 : break;
11411 0 : case TID_FLOAT:
11412 0 : if (data_str[0] == 'n' || data_str[0] == 'N')
11413 0 : *((float *) data + i) = (float) ss_nan();
11414 : else
11415 0 : *((float *) data + i) = (float) atof(data_str);
11416 0 : break;
11417 0 : case TID_DOUBLE:
11418 0 : if (data_str[0] == 'n' || data_str[0] == 'N')
11419 0 : *((double *) data + i) = ss_nan();
11420 : else
11421 0 : *((double *) data + i) = atof(data_str);
11422 0 : break;
11423 0 : case TID_BITFIELD:
11424 0 : if (hex)
11425 0 : value = strtoul(data_str, nullptr, 16);
11426 : else
11427 0 : value = strtoul(data_str, nullptr, 10);
11428 :
11429 0 : *((DWORD *) data + i) = value;
11430 0 : break;
11431 0 : case TID_STRING:
11432 : case TID_LINK:
11433 0 : strcpy((char *) data, data_str);
11434 0 : *data_size = strlen(data_str) + 1;
11435 0 : break;
11436 : }
11437 :
11438 30 : return DB_SUCCESS;
11439 : }
11440 :
11441 : /*------------------------------------------------------------------*/
11442 :
11443 : #ifdef LOCAL_ROUTINES
11444 :
11445 0 : 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)
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 0 : KEYLIST *pkeylist = (KEYLIST *) ((char *) pheader + pkey->data);
11459 0 : if (!pkeylist->first_key)
11460 0 : return;
11461 : // FIXME: validate pkeylist->first_key
11462 0 : pkey = (const KEY *) ((char *) pheader + pkeylist->first_key);
11463 :
11464 : /* first browse through this level */
11465 : do {
11466 0 : pold = NULL;
11467 :
11468 0 : if (pkey->type == TID_LINK) {
11469 0 : const KEY *plink = db_resolve_link_locked(pheader, pkey, NULL, msg);
11470 :
11471 0 : if (!plink)
11472 0 : return;
11473 :
11474 0 : if (plink->type == TID_KEY) {
11475 0 : db_recurse_record_tree_locked(hDB, pheader, plink, data, total_size, base_align, NULL, bSet, convert_flags, msg);
11476 : } else {
11477 0 : pold = pkey;
11478 0 : pkey = plink;
11479 : }
11480 : }
11481 :
11482 0 : if (pkey->type != TID_KEY) {
11483 : /* correct for alignment */
11484 0 : align = 1;
11485 :
11486 0 : if (rpc_tid_size(pkey->type))
11487 0 : align = rpc_tid_size(pkey->type) < base_align ? rpc_tid_size(pkey->type) : base_align;
11488 :
11489 0 : if (max_align && align > *max_align)
11490 0 : *max_align = align;
11491 :
11492 0 : corr = VALIGN(*total_size, align) - *total_size;
11493 0 : *total_size += corr;
11494 0 : if (data)
11495 0 : *data = (void *) ((char *) (*data) + corr);
11496 :
11497 : /* calculate data size */
11498 0 : size = pkey->item_size * pkey->num_values;
11499 :
11500 0 : if (data) {
11501 0 : if (bSet) {
11502 0 : KEY* wpkey = (KEY*)pkey;
11503 : /* copy data if there is write access */
11504 0 : if (pkey->access_mode & MODE_WRITE) {
11505 0 : memcpy((char *) pheader + pkey->data, *data, pkey->item_size * pkey->num_values);
11506 :
11507 : /* convert data */
11508 0 : if (convert_flags) {
11509 0 : if (pkey->num_values > 1)
11510 0 : rpc_convert_data((char *) pheader + pkey->data,
11511 0 : pkey->type, RPC_FIXARRAY, pkey->item_size * pkey->num_values, convert_flags);
11512 : else
11513 0 : rpc_convert_single((char *) pheader + pkey->data, pkey->type, 0, convert_flags);
11514 : }
11515 :
11516 : /* update time */
11517 0 : wpkey->last_written = ss_time();
11518 :
11519 : /* notify clients which have key open */
11520 0 : 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 0 : if (pkey->access_mode & MODE_READ) {
11525 0 : memcpy(*data, (char *) pheader + pkey->data, pkey->item_size * pkey->num_values);
11526 :
11527 : /* convert data */
11528 0 : if (convert_flags) {
11529 0 : if (pkey->num_values > 1)
11530 0 : rpc_convert_data(*data, pkey->type,
11531 : RPC_FIXARRAY | RPC_OUTGOING,
11532 0 : pkey->item_size * pkey->num_values, convert_flags);
11533 : else
11534 0 : rpc_convert_single(*data, pkey->type, RPC_OUTGOING, convert_flags);
11535 : }
11536 : }
11537 : }
11538 :
11539 0 : *data = (char *) (*data) + size;
11540 : }
11541 :
11542 0 : *total_size += size;
11543 : } else {
11544 : /* align new substructure according to the maximum
11545 : align value in this structure */
11546 0 : align = 1;
11547 :
11548 0 : total_size_tmp = *total_size;
11549 0 : db_recurse_record_tree_locked(hDB, pheader, pkey, NULL, &total_size_tmp, base_align, &align, bSet, convert_flags, msg);
11550 :
11551 0 : if (max_align && align > *max_align)
11552 0 : *max_align = align;
11553 :
11554 0 : corr = VALIGN(*total_size, align) - *total_size;
11555 0 : *total_size += corr;
11556 0 : if (data)
11557 0 : *data = (void *) ((char *) (*data) + corr);
11558 :
11559 : /* now recurse subtree */
11560 0 : db_recurse_record_tree_locked(hDB, pheader, pkey, data, total_size, base_align, NULL, bSet, convert_flags, msg);
11561 :
11562 0 : corr = VALIGN(*total_size, align) - *total_size;
11563 0 : *total_size += corr;
11564 0 : if (data)
11565 0 : *data = (void *) ((char *) (*data) + corr);
11566 : }
11567 :
11568 0 : if (pold) {
11569 0 : pkey = pold;
11570 0 : pold = NULL;
11571 : }
11572 :
11573 0 : if (!pkey->next_key)
11574 0 : break;
11575 :
11576 : // FIXME: validate pkey->next_key
11577 0 : pkey = (KEY *) ((char *) pheader + pkey->next_key);
11578 0 : } while (TRUE);
11579 : }
11580 :
11581 0 : static 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 */
11592 0 : DATABASE_HEADER *pheader = _database[hDB - 1].database_header;
11593 :
11594 0 : const KEY* pkey = db_get_pkey(pheader, hKey, NULL, "db_recurse_record_tree", msg);
11595 :
11596 0 : if (!pkey) {
11597 0 : return;
11598 : }
11599 :
11600 0 : 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 :
11605 : /**dox***************************************************************/
11606 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
11607 :
11608 : /********************************************************************/
11609 : /**
11610 : Calculates the size of a record.
11611 : @param hDB ODB handle obtained via cm_get_experiment_database().
11612 : @param hKey Handle for key where search starts, zero for root.
11613 : @param align Byte alignment calculated by the stub and
11614 : passed to the rpc side to align data
11615 : according to local machine. Must be zero
11616 : when called from user level
11617 : @param buf_size Size of record structure
11618 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TYPE_MISMATCH,
11619 : DB_STRUCT_SIZE_MISMATCH, DB_NO_KEY
11620 : */
11621 0 : INT db_get_record_size(HNDLE hDB, HNDLE hKey, INT align, INT * buf_size)
11622 : {
11623 0 : if (rpc_is_remote()) {
11624 0 : align = ss_get_struct_align();
11625 0 : return rpc_call(RPC_DB_GET_RECORD_SIZE, hDB, hKey, align, buf_size);
11626 : }
11627 : #ifdef LOCAL_ROUTINES
11628 : {
11629 : KEY key;
11630 : INT status, max_align;
11631 :
11632 0 : if (!align)
11633 0 : align = ss_get_struct_align();
11634 :
11635 : /* check if key has subkeys */
11636 0 : status = db_get_key(hDB, hKey, &key);
11637 0 : if (status != DB_SUCCESS)
11638 0 : return status;
11639 :
11640 0 : if (key.type != TID_KEY) {
11641 : /* just a single key */
11642 0 : *buf_size = key.item_size * key.num_values;
11643 0 : return DB_SUCCESS;
11644 : }
11645 :
11646 0 : db_err_msg* msg = NULL;
11647 0 : db_lock_database(hDB);
11648 :
11649 : /* determine record size */
11650 0 : *buf_size = max_align = 0;
11651 0 : 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 0 : *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 :
11661 0 : db_unlock_database(hDB);
11662 0 : if (msg)
11663 0 : db_flush_msg(&msg);
11664 : }
11665 : #endif /* LOCAL_ROUTINES */
11666 :
11667 0 : return DB_SUCCESS;
11668 : }
11669 :
11670 : /********************************************************************/
11671 : /**
11672 : Copy a set of keys to local memory.
11673 :
11674 : An ODB sub-tree can be mapped to a C structure automatically via a
11675 : hot-link using the function db_open_record() or manually with this function.
11676 : Problems might occur if the ODB sub-tree contains values which don't match the
11677 : C structure. Although the structure size is checked against the sub-tree size, no
11678 : checking can be done if the type and order of the values in the structure are the
11679 : same than those in the ODB sub-tree. Therefore it is recommended to use the
11680 : function db_create_record() before db_get_record() is used which
11681 : ensures that both are equivalent.
11682 : \code
11683 : struct {
11684 : INT level1;
11685 : INT level2;
11686 : } trigger_settings;
11687 : char *trigger_settings_str =
11688 : "[Settings]\n\
11689 : level1 = INT : 0\n\
11690 : level2 = INT : 0";
11691 :
11692 : main()
11693 : {
11694 : HNDLE hDB, hkey;
11695 : INT size;
11696 : ...
11697 : cm_get_experiment_database(&hDB, NULL);
11698 : db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
11699 : db_find_key(hDB, 0, "/Equipment/Trigger/Settings", &hkey);
11700 : size = sizeof(trigger_settings);
11701 : db_get_record(hDB, hkey, &trigger_settings, &size, 0);
11702 : ...
11703 : }
11704 : \endcode
11705 : @param hDB ODB handle obtained via cm_get_experiment_database().
11706 : @param hKey Handle for key where search starts, zero for root.
11707 : @param data Pointer to the retrieved data.
11708 : @param buf_size Size of data structure, must be obtained via sizeof(RECORD-NAME).
11709 : @param align Byte alignment calculated by the stub and
11710 : passed to the rpc side to align data
11711 : according to local machine. Must be zero
11712 : when called from user level.
11713 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_STRUCT_SIZE_MISMATCH
11714 : */
11715 0 : INT db_get_record(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align)
11716 : {
11717 0 : if (rpc_is_remote()) {
11718 0 : align = ss_get_struct_align();
11719 0 : 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 0 : if (data && buf_size) {
11729 0 : memset(data, 0x00, *buf_size);
11730 : }
11731 :
11732 0 : convert_flags = 0;
11733 :
11734 0 : if (!align)
11735 0 : align = ss_get_struct_align();
11736 : else {
11737 : /* only convert data if called remotely, as indicated by align != 0 */
11738 0 : if (rpc_is_mserver()) {
11739 0 : convert_flags = rpc_get_convert_flags();
11740 : }
11741 : }
11742 :
11743 : /* check if key has subkeys */
11744 0 : status = db_get_key(hDB, hKey, &key);
11745 0 : if (status != DB_SUCCESS)
11746 0 : return status;
11747 :
11748 0 : if (key.type != TID_KEY) {
11749 : /* copy single key */
11750 0 : if (key.item_size * key.num_values != *buf_size) {
11751 0 : cm_msg(MERROR, "db_get_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d * %d = %d)",
11752 0 : db_get_path(hDB, hKey).c_str(), *buf_size, key.num_values, key.item_size, key.item_size * key.num_values);
11753 0 : return DB_STRUCT_SIZE_MISMATCH;
11754 : }
11755 :
11756 0 : db_get_data(hDB, hKey, data, buf_size, key.type);
11757 :
11758 0 : if (convert_flags) {
11759 0 : if (key.num_values > 1)
11760 0 : rpc_convert_data(data, key.type, RPC_OUTGOING | RPC_FIXARRAY, key.item_size * key.num_values, convert_flags);
11761 : else
11762 0 : rpc_convert_single(data, key.type, RPC_OUTGOING, convert_flags);
11763 : }
11764 :
11765 0 : return DB_SUCCESS;
11766 : }
11767 :
11768 : /* check record size */
11769 0 : db_get_record_size(hDB, hKey, align, &total_size);
11770 0 : if (total_size != *buf_size) {
11771 0 : 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);
11772 0 : return DB_STRUCT_SIZE_MISMATCH;
11773 : }
11774 :
11775 : /* get subkey data */
11776 0 : pdata = data;
11777 0 : total_size = 0;
11778 :
11779 0 : db_err_msg* msg = NULL;
11780 0 : db_lock_database(hDB);
11781 0 : db_recurse_record_tree_locked(hDB, hKey, &pdata, &total_size, align, NULL, FALSE, convert_flags, &msg);
11782 0 : db_unlock_database(hDB);
11783 0 : if (msg)
11784 0 : 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 0 : return DB_SUCCESS;
11794 : }
11795 :
11796 : /********************************************************************/
11797 : /**
11798 : Same as db_get_record() but if there is a record mismatch between ODB structure
11799 : and C record, it is automatically corrected by calling db_check_record()
11800 :
11801 : @param hDB ODB handle obtained via cm_get_experiment_database().
11802 : @param hKey Handle for key where search starts, zero for root.
11803 : @param data Pointer to the retrieved data.
11804 : @param buf_size Size of data structure, must be obtained via sizeof(RECORD-NAME).
11805 : @param align Byte alignment calculated by the stub and
11806 : passed to the rpc side to align data
11807 : according to local machine. Must be zero
11808 : when called from user level.
11809 : @param rec_str ASCII representation of ODB record in the format
11810 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_STRUCT_SIZE_MISMATCH
11811 : */
11812 0 : INT db_get_record1(HNDLE hDB, HNDLE hKey, void *data, INT * buf_size, INT align, const char *rec_str)
11813 : {
11814 0 : int size = *buf_size;
11815 0 : int odb_size = 0;
11816 : int status;
11817 : //char path[MAX_ODB_PATH];
11818 :
11819 : /* check record size first */
11820 :
11821 0 : status = db_get_record_size(hDB, hKey, align, &odb_size);
11822 0 : if (status != DB_SUCCESS)
11823 0 : return status;
11824 :
11825 : /* if size mismatch, call repair function */
11826 :
11827 0 : if (odb_size != size) {
11828 0 : std::string path = db_get_path(hDB, hKey);
11829 0 : cm_msg(MINFO, "db_get_record1", "Fixing ODB \"%s\" struct size mismatch (expected %d, odb size %d)", path.c_str(), size, odb_size);
11830 0 : status = db_create_record(hDB, hKey, "", rec_str);
11831 0 : if (status != DB_SUCCESS)
11832 0 : return status;
11833 0 : }
11834 :
11835 : /* run db_get_record(), if success, we are done */
11836 :
11837 0 : status = db_get_record(hDB, hKey, data, buf_size, align);
11838 0 : if (status == DB_SUCCESS)
11839 0 : return status;
11840 :
11841 : /* try repair with db_check_record() */
11842 :
11843 0 : status = db_check_record(hDB, hKey, "", rec_str, TRUE);
11844 0 : if (status != DB_SUCCESS)
11845 0 : 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 :
11852 0 : status = db_get_record_size(hDB, hKey, align, &odb_size);
11853 0 : if (status != DB_SUCCESS)
11854 0 : return status;
11855 :
11856 0 : std::string path = db_get_path(hDB, hKey);
11857 :
11858 0 : if (odb_size != size) {
11859 0 : 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());
11860 0 : status = db_create_record(hDB, hKey, "", rec_str);
11861 0 : if (status != DB_SUCCESS)
11862 0 : return status;
11863 : }
11864 :
11865 0 : cm_msg(MERROR, "db_get_record1", "repaired struct size mismatch of \"%s\"", path.c_str());
11866 :
11867 0 : *buf_size = size;
11868 0 : status = db_get_record(hDB, hKey, data, buf_size, align);
11869 :
11870 0 : return status;
11871 0 : }
11872 :
11873 0 : 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)
11874 : {
11875 0 : title[0] = 0;
11876 0 : key_name[0] = 0;
11877 0 : *tid = 0;
11878 0 : *n_data = 0;
11879 0 : *string_length = 0;
11880 0 : *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 0 : while (*rec_str == '\n')
11895 0 : rec_str++;
11896 :
11897 : /* check if it is a section title */
11898 0 : if (rec_str[0] == '[') {
11899 0 : rec_str++;
11900 :
11901 0 : title[0] = 0;
11902 :
11903 : /* extract title and append '/' */
11904 0 : mstrlcpy(title, rec_str, title_size);
11905 0 : char* p = strchr(title, ']');
11906 0 : if (p)
11907 0 : *p = 0;
11908 :
11909 0 : int len = strlen(title);
11910 0 : if (len > 0) {
11911 0 : if (title[len - 1] != '/')
11912 0 : mstrlcat(title, "/", title_size);
11913 : }
11914 :
11915 : // skip to the next end-of-line
11916 0 : const char* pend = strchr(rec_str, '\n');
11917 0 : if (pend)
11918 0 : rec_str = pend;
11919 : else
11920 0 : rec_str = rec_str+strlen(rec_str);
11921 :
11922 0 : while (*rec_str == '\n')
11923 0 : rec_str++;
11924 :
11925 0 : *out_rec_str = rec_str;
11926 0 : return DB_SUCCESS;
11927 : }
11928 :
11929 0 : if (rec_str[0] == ';') {
11930 : // skip to the next end-of-line
11931 0 : const char* pend = strchr(rec_str, '\n');
11932 0 : if (pend)
11933 0 : rec_str = pend;
11934 : else
11935 0 : rec_str = rec_str+strlen(rec_str);
11936 :
11937 0 : while (*rec_str == '\n')
11938 0 : rec_str++;
11939 :
11940 0 : *out_rec_str = rec_str;
11941 0 : return DB_SUCCESS;
11942 : }
11943 :
11944 0 : const char* peq = strchr(rec_str, '=');
11945 0 : if (!peq) {
11946 0 : cm_msg(MERROR, "db_parse_record", "do not see \'=\'");
11947 0 : return DB_INVALID_PARAM;
11948 : }
11949 :
11950 0 : int key_name_len = peq - rec_str;
11951 :
11952 : // remove trailing equals sign and trailing spaces
11953 0 : while (key_name_len > 1) {
11954 0 : if (rec_str[key_name_len-1] == '=') {
11955 0 : key_name_len--;
11956 0 : continue;
11957 : }
11958 0 : if (rec_str[key_name_len-1] == ' ') {
11959 0 : key_name_len--;
11960 0 : continue;
11961 : }
11962 0 : break;
11963 : }
11964 :
11965 0 : memcpy(key_name, rec_str, key_name_len);
11966 0 : key_name[key_name_len] = 0;
11967 :
11968 0 : rec_str = peq + 1; // consume the "=" sign
11969 :
11970 0 : while (*rec_str == ' ') // consume spaces
11971 0 : rec_str++;
11972 :
11973 : // extract type id
11974 : char stid[256];
11975 : int i;
11976 0 : for (i=0; i<(int)sizeof(stid)-1; i++) {
11977 0 : char s = *rec_str;
11978 0 : if (s == 0) break;
11979 0 : if (s == ' ') break;
11980 0 : if (s == '\n') break;
11981 0 : if (s == '[') break;
11982 0 : stid[i] = s;
11983 0 : rec_str++;
11984 : }
11985 0 : stid[i] = 0;
11986 :
11987 0 : DWORD xtid = 0;
11988 0 : for (xtid = 0; xtid < TID_LAST; xtid++) {
11989 0 : if (strcmp(rpc_tid_name(xtid), stid) == 0) {
11990 0 : *tid = xtid;
11991 0 : break;
11992 : }
11993 : }
11994 :
11995 : //printf("tid [%s], tid %d\n", stid, *tid);
11996 :
11997 0 : if (xtid == TID_LAST) {
11998 0 : cm_msg(MERROR, "db_parse_record", "do not see \':\'");
11999 0 : return DB_INVALID_PARAM;
12000 : }
12001 :
12002 0 : while (*rec_str == ' ') // consume spaces
12003 0 : rec_str++;
12004 :
12005 0 : *n_data = 1;
12006 :
12007 0 : if (*rec_str == '[') {
12008 : // decode array size
12009 0 : rec_str++; // cosume the '['
12010 0 : *n_data = atoi(rec_str);
12011 0 : const char *pbr = strchr(rec_str, ']');
12012 0 : if (!pbr) {
12013 0 : cm_msg(MERROR, "db_parse_record", "do not see closing bracket \']\'");
12014 0 : return DB_INVALID_PARAM;
12015 : }
12016 0 : rec_str = pbr + 1; // skip the closing bracket
12017 : }
12018 :
12019 0 : while (*rec_str == ' ') // consume spaces
12020 0 : rec_str++;
12021 :
12022 0 : const char* pcol = strchr(rec_str, ':');
12023 0 : if (!pcol) {
12024 0 : cm_msg(MERROR, "db_parse_record", "do not see \':\'");
12025 0 : return DB_INVALID_PARAM;
12026 : }
12027 :
12028 0 : rec_str = pcol + 1; // skip the ":"
12029 :
12030 0 : while (*rec_str == ' ') // consume spaces
12031 0 : rec_str++;
12032 :
12033 0 : *string_length = 0;
12034 0 : if (xtid == TID_LINK || xtid == TID_STRING) {
12035 : // extract string length
12036 0 : const char* pbr = strchr(rec_str, '[');
12037 0 : if (pbr) {
12038 0 : *string_length = atoi(pbr+1);
12039 : }
12040 : }
12041 :
12042 : // skip to the next end-of-line
12043 0 : const char* pend = strchr(rec_str, '\n');
12044 0 : if (pend)
12045 0 : rec_str = pend;
12046 : else
12047 0 : rec_str = rec_str+strlen(rec_str);
12048 :
12049 0 : while (*rec_str == '\n')
12050 0 : rec_str++;
12051 :
12052 0 : *out_rec_str = rec_str;
12053 0 : return DB_SUCCESS;
12054 : }
12055 :
12056 0 : 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)
12057 : {
12058 0 : assert(tid > 0);
12059 0 : assert(n_data > 0);
12060 0 : int tsize = rpc_tid_size(tid);
12061 0 : int offset = *buf_ptr - buf_start;
12062 0 : int align = 0;
12063 0 : if (tsize && (offset%tsize != 0)) {
12064 0 : while (offset%tsize != 0) {
12065 0 : align++;
12066 0 : *(*buf_ptr) = 0xFF; // pad bytes for correct data alignement
12067 0 : (*buf_ptr)++;
12068 0 : (*buf_remain)--;
12069 0 : offset++;
12070 : }
12071 : }
12072 0 : 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 0 : if (tsize > 0) {
12074 0 : int xsize = tsize*n_data;
12075 0 : if (xsize > *buf_remain) {
12076 0 : cm_msg(MERROR, "db_get_record2", "buffer overrun at key \"%s\", size %d, buffer remaining %d", key_name, xsize, *buf_remain);
12077 0 : return DB_INVALID_PARAM;
12078 : }
12079 0 : int ysize = xsize;
12080 0 : int status = db_get_value(hDB, hKey, key_name, *buf_ptr, &ysize, tid, FALSE);
12081 : //printf("status %d, xsize %d\n", status, xsize);
12082 0 : if (status != DB_SUCCESS) {
12083 0 : cm_msg(MERROR, "db_get_record2", "cannot read \"%s\", db_get_value() status %d", key_name, status);
12084 0 : memset(*buf_ptr, 0, xsize);
12085 0 : *buf_ptr += xsize;
12086 0 : *buf_remain -= xsize;
12087 0 : return status;
12088 : }
12089 0 : *buf_ptr += xsize;
12090 0 : *buf_remain -= xsize;
12091 0 : } else if (tid == TID_STRING) {
12092 0 : int xstatus = 0;
12093 : int i;
12094 0 : for (i=0; i<n_data; i++) {
12095 0 : int xsize = string_length;
12096 0 : if (xsize > *buf_remain) {
12097 0 : 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 0 : return DB_INVALID_PARAM;
12099 : }
12100 : char xkey_name[MAX_ODB_PATH+100];
12101 0 : sprintf(xkey_name, "%s[%d]", key_name, i);
12102 0 : int status = db_get_value(hDB, hKey, xkey_name, *buf_ptr, &xsize, tid, FALSE);
12103 : //printf("status %d, string length %d, xsize %d, actual len %d\n", status, string_length, xsize, (int)strlen(*buf_ptr));
12104 0 : if (status == DB_TRUNCATED) {
12105 : // make sure string is NUL terminated
12106 0 : (*buf_ptr)[string_length-1] = 0;
12107 0 : cm_msg(MERROR, "db_get_record2", "string key \"%s\" index %d, string value was truncated", key_name, i);
12108 0 : } else if (status != DB_SUCCESS) {
12109 0 : cm_msg(MERROR, "db_get_record2", "cannot read string \"%s\"[%d], db_get_value() status %d", key_name, i, status);
12110 0 : memset(*buf_ptr, 0, string_length);
12111 0 : xstatus = status;
12112 : }
12113 0 : *buf_ptr += string_length;
12114 0 : *buf_remain -= string_length;
12115 : }
12116 0 : if (xstatus != 0) {
12117 0 : return xstatus;
12118 : }
12119 : } else {
12120 0 : cm_msg(MERROR, "db_get_record2", "cannot read key \"%s\" of unsupported type %d", key_name, tid);
12121 0 : return DB_INVALID_PARAM;
12122 : }
12123 0 : return DB_SUCCESS;
12124 : }
12125 :
12126 : /********************************************************************/
12127 : /**
12128 : Copy a set of keys to local memory.
12129 :
12130 : An ODB sub-tree can be mapped to a C structure automatically via a
12131 : hot-link using the function db_open_record1() or manually with this function.
12132 : For correct operation, the description string *must* match the C data
12133 : structure. If the contents of ODB sub-tree does not exactly match
12134 : the description string, db_get_record2() will try to read as much as it can
12135 : and return DB_TRUNCATED to inform the user that there was a mismatch somewhere.
12136 : To ensure that the ODB sub-tree matches the desciption string, call db_create_record()
12137 : or db_check_record() before calling db_get_record2(). Unlike db_get_record()
12138 : and db_get_record1(), this function will not complain about data strucure mismatches.
12139 : It will ignore all extra entries in the ODB sub-tree and it will set to zero the C-structure
12140 : data fields that do not have corresponding ODB entries.
12141 : \code
12142 : struct {
12143 : INT level1;
12144 : INT level2;
12145 : } trigger_settings;
12146 : const char *trigger_settings_str =
12147 : "[Settings]\n\
12148 : level1 = INT : 0\n\
12149 : level2 = INT : 0";
12150 :
12151 : main()
12152 : {
12153 : HNDLE hDB, hkey;
12154 : INT size;
12155 : ...
12156 : cm_get_experiment_database(&hDB, NULL);
12157 : db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
12158 : db_find_key(hDB, 0, "/Equipment/Trigger/Settings", &hkey);
12159 : size = sizeof(trigger_settings);
12160 : db_get_record2(hDB, hkey, &trigger_settings, &size, 0, trigger_settings_str);
12161 : ...
12162 : }
12163 : \endcode
12164 : @param hDB ODB handle obtained via cm_get_experiment_database().
12165 : @param hKey Handle for key where search starts, zero for root.
12166 : @param data Pointer to the retrieved data.
12167 : @param buf_size Size of data structure, must be obtained via sizeof(data).
12168 : @param align Byte alignment calculated by the stub and
12169 : passed to the rpc side to align data
12170 : according to local machine. Must be zero
12171 : when called from user level.
12172 : @param rec_str Description of the data structure, see db_create_record()
12173 : @param correct Must be set to zero
12174 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_STRUCT_SIZE_MISMATCH
12175 : */
12176 0 : INT db_get_record2(HNDLE hDB, HNDLE hKey, void *data, INT * xbuf_size, INT align, const char *rec_str, BOOL correct)
12177 : {
12178 0 : int status = DB_SUCCESS;
12179 :
12180 0 : printf("db_get_record2!\n");
12181 :
12182 0 : assert(data != NULL);
12183 0 : assert(xbuf_size != NULL);
12184 0 : assert(*xbuf_size > 0);
12185 0 : assert(correct == 0);
12186 :
12187 0 : int truncated = 0;
12188 : #if 1
12189 0 : char* r1 = NULL;
12190 0 : int rs = *xbuf_size;
12191 : if (1) {
12192 0 : r1 = (char*)malloc(rs);
12193 0 : assert(r1 != NULL);
12194 0 : memset(data, 0xFF, *xbuf_size);
12195 0 : memset(r1, 0xFF, rs);
12196 : //status = db_get_record1(hDB, hKey, r1, &rs, 0, rec_str);
12197 0 : status = db_get_record(hDB, hKey, r1, &rs, 0);
12198 0 : printf("db_get_record status %d\n", status);
12199 : }
12200 : #endif
12201 :
12202 0 : char* buf_start = (char*)data;
12203 0 : int buf_size = *xbuf_size;
12204 :
12205 0 : char* buf_ptr = buf_start;
12206 0 : int buf_remain = buf_size;
12207 :
12208 0 : while (rec_str && *rec_str != 0) {
12209 : char title[256];
12210 : char key_name[MAX_ODB_PATH];
12211 0 : int tid = 0;
12212 0 : int n_data = 0;
12213 0 : int string_length = 0;
12214 0 : const char* rec_str_next = NULL;
12215 :
12216 0 : 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 :
12220 0 : rec_str = rec_str_next;
12221 :
12222 0 : if (status != DB_SUCCESS) {
12223 0 : return status;
12224 : }
12225 :
12226 0 : if (key_name[0] == 0) {
12227 : // skip title strings, comments, etc
12228 0 : continue;
12229 : }
12230 :
12231 0 : status = db_get_record2_read_element(hDB, hKey, key_name, tid, n_data, string_length, buf_start, &buf_ptr, &buf_remain, correct);
12232 0 : if (status == DB_INVALID_PARAM) {
12233 0 : cm_msg(MERROR, "db_get_record2", "error: cannot continue reading odb record because of previous fatal error, status %d", status);
12234 0 : return DB_INVALID_PARAM;
12235 0 : } if (status != DB_SUCCESS) {
12236 0 : truncated = 1;
12237 : }
12238 :
12239 0 : rec_str = rec_str_next;
12240 : }
12241 :
12242 0 : if (r1) {
12243 0 : int ok = -1;
12244 : int i;
12245 0 : for (i=0; i<rs; i++) {
12246 0 : if (r1[i] != buf_start[i]) {
12247 0 : ok = i;
12248 0 : break;
12249 : }
12250 : }
12251 0 : if (ok>=0 || buf_remain>0) {
12252 0 : printf("db_get_record2: miscompare at %d out of %d, buf_remain %d\n", ok, rs, buf_remain);
12253 : } else {
12254 0 : printf("db_get_record2: check ok\n");
12255 : }
12256 : }
12257 :
12258 0 : if (buf_remain > 0) {
12259 : // FIXME: we finished processing the data definition string, but unused space remains in the buffer
12260 0 : return DB_TRUNCATED;
12261 : }
12262 :
12263 0 : if (truncated)
12264 0 : return DB_TRUNCATED;
12265 : else
12266 0 : return DB_SUCCESS;
12267 : }
12268 :
12269 : /********************************************************************/
12270 : /**
12271 : Copy a set of keys from local memory to the database.
12272 :
12273 : An ODB sub-tree can be mapped to a C structure automatically via a
12274 : hot-link using the function db_open_record() or manually with this function.
12275 : Problems might occur if the ODB sub-tree contains values which don't match the
12276 : C structure. Although the structure size is checked against the sub-tree size, no
12277 : checking can be done if the type and order of the values in the structure are the
12278 : same than those in the ODB sub-tree. Therefore it is recommended to use the
12279 : function db_create_record() before using this function.
12280 : \code
12281 : ...
12282 : memset(&lazyst,0,size);
12283 : if (db_find_key(hDB, pLch->hKey, "Statistics",&hKeyst) == DB_SUCCESS)
12284 : status = db_set_record(hDB, hKeyst, &lazyst, size, 0);
12285 : else
12286 : cm_msg(MERROR,"task","record %s/statistics not found", pLch->name)
12287 : ...
12288 : \endcode
12289 : @param hDB ODB handle obtained via cm_get_experiment_database().
12290 : @param hKey Handle for key where search starts, zero for root.
12291 : @param data Pointer where data is stored.
12292 : @param buf_size Size of data structure, must be obtained via sizeof(RECORD-NAME).
12293 : @param align Byte alignment calculated by the stub and
12294 : passed to the rpc side to align data
12295 : according to local machine. Must be zero
12296 : when called from user level.
12297 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_TYPE_MISMATCH, DB_STRUCT_SIZE_MISMATCH
12298 : */
12299 0 : INT db_set_record(HNDLE hDB, HNDLE hKey, void *data, INT buf_size, INT align)
12300 : {
12301 0 : if (rpc_is_remote()) {
12302 0 : align = ss_get_struct_align();
12303 0 : 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 0 : convert_flags = 0;
12313 :
12314 0 : if (!align)
12315 0 : align = ss_get_struct_align();
12316 : else {
12317 : /* only convert data if called remotely, as indicated by align != 0 */
12318 0 : if (rpc_is_mserver()) {
12319 0 : convert_flags = rpc_get_convert_flags();
12320 : }
12321 : }
12322 :
12323 : /* check if key has subkeys */
12324 0 : db_get_key(hDB, hKey, &key);
12325 0 : if (key.type != TID_KEY) {
12326 : /* copy single key */
12327 0 : if (key.item_size * key.num_values != buf_size) {
12328 0 : cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
12329 0 : return DB_STRUCT_SIZE_MISMATCH;
12330 : }
12331 :
12332 0 : if (convert_flags) {
12333 0 : if (key.num_values > 1)
12334 0 : rpc_convert_data(data, key.type, RPC_FIXARRAY, key.item_size * key.num_values, convert_flags);
12335 : else
12336 0 : rpc_convert_single(data, key.type, 0, convert_flags);
12337 : }
12338 :
12339 0 : db_set_data(hDB, hKey, data, key.total_size, key.num_values, key.type);
12340 0 : return DB_SUCCESS;
12341 : }
12342 :
12343 : /* check record size */
12344 0 : db_get_record_size(hDB, hKey, align, &total_size);
12345 0 : if (total_size != buf_size) {
12346 0 : cm_msg(MERROR, "db_set_record", "struct size mismatch for \"%s\"", key.name);
12347 0 : return DB_STRUCT_SIZE_MISMATCH;
12348 : }
12349 :
12350 : /* set subkey data */
12351 0 : pdata = data;
12352 0 : total_size = 0;
12353 :
12354 0 : db_lock_database(hDB);
12355 0 : db_err_msg* msg = NULL;
12356 0 : db_allow_write_locked(&_database[hDB-1], "db_set_record");
12357 0 : db_recurse_record_tree_locked(hDB, hKey, &pdata, &total_size, align, NULL, TRUE, convert_flags, &msg);
12358 0 : db_unlock_database(hDB);
12359 0 : if (msg)
12360 0 : db_flush_msg(&msg);
12361 : }
12362 : #endif /* LOCAL_ROUTINES */
12363 :
12364 0 : return DB_SUCCESS;
12365 : }
12366 :
12367 : /**dox***************************************************************/
12368 : #ifndef DOXYGEN_SHOULD_SKIP_THIS
12369 :
12370 : /*------------------------------------------------------------------*/
12371 2 : INT db_add_open_record(HNDLE hDB, HNDLE hKey, WORD access_mode)
12372 : /********************************************************************\
12373 :
12374 : Routine: db_add_open_record
12375 :
12376 : Purpose: Server part of db_open_record. Internal use only.
12377 :
12378 : \********************************************************************/
12379 : {
12380 2 : if (rpc_is_remote())
12381 0 : 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 2 : if (hDB > _database_entries || hDB <= 0) {
12389 0 : cm_msg(MERROR, "db_add_open_record", "invalid database handle");
12390 0 : return DB_INVALID_HANDLE;
12391 : }
12392 :
12393 2 : db_err_msg* msg = NULL;
12394 :
12395 : /* lock database */
12396 2 : db_lock_database(hDB);
12397 :
12398 2 : DATABASE *pdb = &_database[hDB - 1];
12399 2 : DATABASE_HEADER *pheader = pdb->database_header;
12400 2 : DATABASE_CLIENT *pclient = db_get_my_client_locked(pdb);
12401 :
12402 : /* check if key is already open */
12403 2 : for (i = 0; i < pclient->max_index; i++) {
12404 0 : if (pclient->open_record[i].handle == hKey)
12405 0 : break;
12406 : }
12407 :
12408 2 : if (i < pclient->max_index) {
12409 0 : db_unlock_database(hDB);
12410 0 : return DB_SUCCESS;
12411 : }
12412 :
12413 : /* not found, search free entry */
12414 2 : for (i = 0; i < pclient->max_index; i++) {
12415 0 : if (pclient->open_record[i].handle == 0)
12416 0 : break;
12417 : }
12418 :
12419 : /* check if maximum number reached */
12420 2 : if (i == MAX_OPEN_RECORDS) {
12421 0 : db_unlock_database(hDB);
12422 0 : return DB_NO_MEMORY;
12423 : }
12424 :
12425 2 : db_allow_write_locked(pdb, "db_add_open_record");
12426 :
12427 2 : KEY *pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_add_open_record", &msg);
12428 :
12429 2 : if (!pkey) {
12430 0 : db_unlock_database(hDB);
12431 0 : if (msg)
12432 0 : db_flush_msg(&msg);
12433 0 : return status;
12434 : }
12435 :
12436 2 : if (i == pclient->max_index)
12437 2 : pclient->max_index++;
12438 :
12439 2 : pclient->open_record[i].handle = hKey;
12440 2 : pclient->open_record[i].access_mode = access_mode;
12441 :
12442 : /* increment notify_count */
12443 2 : pkey->notify_count++;
12444 :
12445 2 : pclient->num_open_records++;
12446 :
12447 : /* set exclusive bit if requested */
12448 2 : if (access_mode & MODE_WRITE)
12449 0 : db_set_mode(hDB, hKey, (WORD) (pkey->access_mode | MODE_EXCLUSIVE), 2);
12450 :
12451 2 : db_unlock_database(hDB);
12452 : }
12453 : #endif /* LOCAL_ROUTINES */
12454 :
12455 2 : return DB_SUCCESS;
12456 : }
12457 :
12458 : #ifdef LOCAL_ROUTINES
12459 :
12460 2 : static int db_remove_open_record_wlocked(DATABASE* pdb, DATABASE_HEADER* pheader, HNDLE hKey)
12461 : {
12462 2 : int status = DB_SUCCESS;
12463 :
12464 2 : DATABASE_CLIENT *pclient = db_get_my_client_locked(pdb);
12465 :
12466 : /* search key */
12467 : int idx;
12468 2 : for (idx = 0; idx < pclient->max_index; idx++)
12469 2 : if (pclient->open_record[idx].handle == hKey)
12470 2 : break;
12471 :
12472 2 : if (idx == pclient->max_index) {
12473 0 : return DB_INVALID_HANDLE;
12474 : }
12475 :
12476 2 : KEY* pkey = (KEY*)db_get_pkey(pheader, hKey, &status, "db_remove_open_record_wlocked", NULL);
12477 :
12478 2 : if (!pkey)
12479 0 : return status;
12480 :
12481 : /* decrement notify_count */
12482 :
12483 2 : if (pkey->notify_count > 0)
12484 2 : pkey->notify_count--;
12485 :
12486 2 : pclient->num_open_records--;
12487 :
12488 : /* remove exclusive flag */
12489 2 : if (pclient->open_record[idx].access_mode & MODE_WRITE)
12490 0 : db_set_mode_wlocked(pheader, pkey, (WORD) (pkey->access_mode & ~MODE_EXCLUSIVE), 2, NULL);
12491 :
12492 2 : memset(&pclient->open_record[idx], 0, sizeof(OPEN_RECORD));
12493 :
12494 : /* calculate new max_index entry */
12495 : int i;
12496 4 : for (i = pclient->max_index - 1; i >= 0; i--)
12497 2 : if (pclient->open_record[i].handle != 0)
12498 0 : break;
12499 2 : pclient->max_index = i + 1;
12500 :
12501 2 : return DB_SUCCESS;
12502 : }
12503 : #endif // LOCAL_ROUTINES
12504 :
12505 :
12506 2 : INT db_remove_open_record(HNDLE hDB, HNDLE hKey, BOOL lock)
12507 : /********************************************************************\
12508 :
12509 : Routine: db_remove_open_record
12510 :
12511 : Purpose: Gets called by db_close_record. Internal use only.
12512 :
12513 : \********************************************************************/
12514 : {
12515 2 : if (rpc_is_remote())
12516 0 : return rpc_call(RPC_DB_REMOVE_OPEN_RECORD, hDB, hKey, lock);
12517 :
12518 2 : int status = DB_SUCCESS;
12519 :
12520 : #ifdef LOCAL_ROUTINES
12521 : {
12522 2 : if (hDB > _database_entries || hDB <= 0) {
12523 0 : cm_msg(MERROR, "db_remove_open_record", "invalid database handle %d", hDB);
12524 0 : return DB_INVALID_HANDLE;
12525 : }
12526 :
12527 2 : db_lock_database(hDB);
12528 :
12529 2 : DATABASE *pdb = &_database[hDB - 1];
12530 2 : DATABASE_HEADER *pheader = pdb->database_header;
12531 :
12532 2 : db_allow_write_locked(pdb, "db_remove_open_record");
12533 :
12534 2 : status = db_remove_open_record_wlocked(pdb, pheader, hKey);
12535 :
12536 2 : db_unlock_database(hDB);
12537 : }
12538 : #endif /* LOCAL_ROUTINES */
12539 :
12540 2 : return status;
12541 : }
12542 :
12543 : /*------------------------------------------------------------------*/
12544 :
12545 : #ifdef LOCAL_ROUTINES
12546 :
12547 168 : static INT db_notify_clients_locked(const DATABASE_HEADER* pheader, HNDLE hDB, HNDLE hKeyMod, int index, BOOL bWalk, db_err_msg** msg)
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;
12557 : KEYLIST *pkeylist;
12558 : INT i, j;
12559 : char str[80];
12560 : int status;
12561 :
12562 168 : hKey = hKeyMod;
12563 :
12564 168 : const KEY* pkey = db_get_pkey(pheader, hKey, &status, "db_notify_clients", msg);
12565 :
12566 168 : if (!pkey) {
12567 0 : return status;
12568 : }
12569 :
12570 : do {
12571 :
12572 : /* check which client has record open */
12573 878 : if (pkey->notify_count)
12574 0 : for (i = 0; i < pheader->max_client_index; i++) {
12575 0 : const DATABASE_CLIENT* pclient = &pheader->client[i];
12576 0 : for (j = 0; j < pclient->max_index; j++)
12577 0 : if (pclient->open_record[j].handle == hKey) {
12578 : /* send notification to remote process */
12579 0 : sprintf(str, "O %d %d %d %d", hDB, hKey, hKeyMod, index);
12580 0 : ss_resume(pclient->port, str);
12581 : }
12582 : }
12583 :
12584 878 : if (pkey->parent_keylist == 0 || !bWalk)
12585 168 : return DB_SUCCESS;
12586 :
12587 : // FIXME: validate pkey->parent_keylist
12588 710 : pkeylist = (KEYLIST *) ((char *) pheader + pkey->parent_keylist);
12589 710 : pkey = db_get_pkey(pheader, pkeylist->parent, &status, "db_notify_clients", msg);
12590 710 : if (!pkey) {
12591 0 : return status;
12592 : }
12593 710 : hKey = db_pkey_to_hkey(pheader, pkey);
12594 710 : } while (TRUE);
12595 :
12596 : }
12597 :
12598 : #endif /* LOCAL_ROUTINES */
12599 : /*------------------------------------------------------------------*/
12600 :
12601 :
12602 0 : INT db_notify_clients(HNDLE hDB, HNDLE hKeyMod, int index, BOOL bWalk)
12603 : /********************************************************************\
12604 :
12605 : Routine: db_notify_clients
12606 :
12607 : Purpose: Gets called by db_set_xxx functions. Internal use only.
12608 : \********************************************************************/
12609 : {
12610 0 : if (rpc_is_remote()) {
12611 0 : cm_msg(MERROR, "db_notify_clients", "db_notify_clients() does not work in remotely connected MIDAS clients");
12612 0 : return DB_INVALID_HANDLE;
12613 : }
12614 :
12615 : #ifdef LOCAL_ROUTINES
12616 : {
12617 0 : db_err_msg* msg = NULL;
12618 0 : int status = db_lock_database(hDB);
12619 0 : if (status != DB_SUCCESS)
12620 0 : return status;
12621 0 : DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
12622 0 : db_notify_clients_locked(pheader, hDB, hKeyMod, index, bWalk, &msg);
12623 0 : db_unlock_database(hDB);
12624 0 : if (msg)
12625 0 : db_flush_msg(&msg);
12626 : }
12627 : #endif
12628 0 : return DB_SUCCESS;
12629 : }
12630 :
12631 0 : INT db_notify_clients_array(HNDLE hDB, HNDLE hKeys[], INT size)
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 0 : if (rpc_is_remote())
12645 0 : return rpc_call(RPC_DB_NOTIFY_CLIENTS_ARRAY, hDB, hKeys, size);
12646 :
12647 : #ifdef LOCAL_ROUTINES
12648 : {
12649 0 : int status = db_lock_database(hDB);
12650 0 : if (status != DB_SUCCESS)
12651 0 : return status;
12652 0 : db_err_msg* msg = NULL;
12653 0 : DATABASE_HEADER* pheader = _database[hDB - 1].database_header;
12654 0 : int count = size/sizeof(INT);
12655 0 : for (int i=0 ; i<count; i++) {
12656 0 : db_notify_clients_locked(pheader, hDB, hKeys[i], -1, TRUE, &msg);
12657 : }
12658 0 : db_unlock_database(hDB);
12659 0 : if (msg)
12660 0 : db_flush_msg(&msg);
12661 : }
12662 : #endif
12663 0 : return DB_SUCCESS;
12664 : }
12665 :
12666 : /*------------------------------------------------------------------*/
12667 4 : static void merge_records(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
12668 : {
12669 : char full_name[MAX_ODB_PATH];
12670 : INT status, size;
12671 : HNDLE hKeyInit;
12672 : KEY initkey, key;
12673 :
12674 : /* avoid compiler warnings */
12675 4 : status = level;
12676 :
12677 : /* compose name of init key */
12678 4 : std::string s = db_get_path(hDB, hKey);
12679 4 : mstrlcpy(full_name, s.c_str(), sizeof(full_name));
12680 4 : *strchr(full_name, 'O') = 'I';
12681 :
12682 : /* if key in init record found, copy original data to init data */
12683 4 : status = db_find_key(hDB, 0, full_name, &hKeyInit);
12684 4 : if (status == DB_SUCCESS) {
12685 4 : status = db_get_key(hDB, hKeyInit, &initkey);
12686 4 : if (status != DB_SUCCESS) {
12687 0 : cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', db_get_key() status %d", full_name, status);
12688 0 : return;
12689 : }
12690 4 : status = db_get_key(hDB, hKey, &key);
12691 4 : if (status != DB_SUCCESS) {
12692 0 : cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', second db_get_key() status %d", full_name, status);
12693 0 : return;
12694 : }
12695 :
12696 4 : if (initkey.type != TID_KEY && initkey.type == key.type) {
12697 2 : char* allocbuffer = NULL;
12698 : char stackbuffer[10000];
12699 2 : char* buffer = stackbuffer;
12700 2 : size = sizeof(stackbuffer);
12701 : while (1) {
12702 : /* copy data from original key to new key */
12703 2 : status = db_get_data(hDB, hKey, buffer, &size, initkey.type);
12704 2 : if (status == DB_SUCCESS) {
12705 2 : status = db_set_data(hDB, hKeyInit, buffer, initkey.total_size, initkey.num_values, initkey.type);
12706 2 : if (status != DB_SUCCESS) {
12707 0 : cm_msg(MERROR, "merge_records", "merge_record error at \'%s\', db_set_data() status %d", full_name, status);
12708 0 : return;
12709 : }
12710 2 : break;
12711 : }
12712 0 : if (status == DB_TRUNCATED) {
12713 0 : size *= 2;
12714 0 : allocbuffer = (char *)realloc(allocbuffer, size);
12715 0 : assert(allocbuffer != NULL);
12716 0 : buffer = allocbuffer;
12717 0 : continue;
12718 : }
12719 0 : cm_msg(MERROR, "merge_records", "aborting on unexpected failure of db_get_data(%s), status %d", full_name, status);
12720 0 : abort();
12721 : }
12722 2 : if (allocbuffer)
12723 0 : free(allocbuffer);
12724 : }
12725 0 : } else if (status == DB_NO_KEY) {
12726 : /* do nothing */
12727 0 : } else if (status == DB_INVALID_LINK) {
12728 0 : status = db_find_link(hDB, 0, full_name, &hKeyInit);
12729 0 : if (status == DB_SUCCESS) {
12730 0 : size = sizeof(full_name);
12731 0 : status = db_get_data(hDB, hKeyInit, full_name, &size, TID_LINK);
12732 : }
12733 0 : cm_msg(MERROR, "merge_records", "Invalid link \"%s\"", full_name);
12734 : } else {
12735 0 : cm_msg(MERROR, "merge_records", "aborting on unexpected failure of db_find_key(%s), status %d", full_name, status);
12736 0 : abort();
12737 : }
12738 4 : }
12739 :
12740 : static int _global_open_count; // FIXME: this is not thread-safe
12741 :
12742 4 : static void check_open_keys(HNDLE hDB, HNDLE hKey, KEY * pkey, INT level, void *info)
12743 : {
12744 4 : if (pkey->notify_count)
12745 0 : _global_open_count++;
12746 4 : }
12747 :
12748 : /**dox***************************************************************/
12749 : #endif /* DOXYGEN_SHOULD_SKIP_THIS */
12750 :
12751 : /********************************************************************/
12752 : /**
12753 : Create a record. If a part of the record exists alreay,
12754 : merge it with the init_str (use values from the init_str only when they are
12755 : not in the existing record).
12756 :
12757 : This functions creates a ODB sub-tree according to an ASCII
12758 : representation of that tree. See db_copy() for a description. It can be used to
12759 : create a sub-tree which exactly matches a C structure. The sub-tree can then
12760 : later mapped to the C structure ("hot-link") via the function db_open_record().
12761 :
12762 : If a sub-tree exists already which exactly matches the ASCII representation, it is
12763 : not modified. If part of the tree exists, it is merged with the ASCII representation
12764 : where the ODB values have priority, only values not present in the ODB are
12765 : created with the default values of the ASCII representation. It is therefore
12766 : recommended that before creating an ODB hot-link the function
12767 : db_create_record() is called to insure that the ODB tree and the C structure
12768 : contain exactly the same values in the same order.
12769 :
12770 : Following example creates a record under /Equipment/Trigger/Settings,
12771 : opens a hot-link between that record and a local C structure
12772 : trigger_settings and registers a callback function trigger_update()
12773 : which gets called each time the record is changed.
12774 : \code
12775 : struct {
12776 : INT level1;
12777 : INT level2;
12778 : } trigger_settings;
12779 : char *trigger_settings_str =
12780 : "[Settings]\n\
12781 : level1 = INT : 0\n\
12782 : level2 = INT : 0";
12783 : void trigger_update(INT hDB, INT hkey, void *info)
12784 : {
12785 : printf("New levels: %d %d\n",
12786 : trigger_settings.level1,
12787 : trigger_settings.level2);
12788 : }
12789 : main()
12790 : {
12791 : HNDLE hDB, hkey;
12792 : char[128] info;
12793 : ...
12794 : cm_get_experiment_database(&hDB, NULL);
12795 : db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
12796 : db_find_key(hDB, 0,"/Equipment/Trigger/Settings", &hkey);
12797 : db_open_record(hDB, hkey, &trigger_settings,
12798 : sizeof(trigger_settings), MODE_READ, trigger_update, info);
12799 : ...
12800 : }
12801 : \endcode
12802 : @param hDB ODB handle obtained via cm_get_experiment_database().
12803 : @param hKey Handle for key where search starts, zero for root.
12804 : @param orig_key_name Name of key to search, can contain directories.
12805 : @param init_str Initialization string in the format of the db_copy/db_save functions.
12806 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_FULL, DB_NO_ACCESS, DB_OPEN_RECORD
12807 : */
12808 2 : INT 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;
12812 : HNDLE hKeyTmp, hKeyTmpO, hKeyOrig, hSubkey;
12813 :
12814 2 : if (rpc_is_remote())
12815 0 : return rpc_call(RPC_DB_CREATE_RECORD, hDB, hKey, orig_key_name, init_str);
12816 :
12817 : /* make this function atomic */
12818 2 : db_lock_database(hDB);
12819 :
12820 : /* strip trailing '/' */
12821 2 : mstrlcpy(key_name, orig_key_name, sizeof(key_name));
12822 2 : if (strlen(key_name) > 1 && key_name[strlen(key_name) - 1] == '/')
12823 0 : key_name[strlen(key_name) - 1] = 0;
12824 :
12825 : /* merge temporay record and original record */
12826 2 : status = db_find_key(hDB, hKey, key_name, &hKeyOrig);
12827 2 : if (status == DB_SUCCESS) {
12828 2 : assert(hKeyOrig != 0);
12829 : #ifdef CHECK_OPEN_RECORD
12830 : /* check if key or subkey is opened */
12831 2 : _global_open_count = 0; // FIXME: this is not thread safe
12832 2 : db_scan_tree_link(hDB, hKeyOrig, 0, check_open_keys, NULL);
12833 2 : if (_global_open_count) {
12834 0 : db_unlock_database(hDB);
12835 0 : return DB_OPEN_RECORD;
12836 : }
12837 : #endif
12838 : /* create temporary records */
12839 2 : sprintf(str, "/System/Tmp/%sI", ss_tid_to_string(ss_gettid()).c_str());
12840 : //printf("db_create_record str [%s]\n", str);
12841 2 : db_find_key(hDB, 0, str, &hKeyTmp);
12842 2 : if (hKeyTmp)
12843 0 : db_delete_key(hDB, hKeyTmp, FALSE);
12844 2 : db_create_key(hDB, 0, str, TID_KEY);
12845 2 : status = db_find_key(hDB, 0, str, &hKeyTmp);
12846 2 : if (status != DB_SUCCESS) {
12847 0 : db_unlock_database(hDB);
12848 0 : return status;
12849 : }
12850 :
12851 2 : sprintf(str, "/System/Tmp/%sO", ss_tid_to_string(ss_gettid()).c_str());
12852 : //printf("db_create_record str [%s]\n", str);
12853 2 : db_find_key(hDB, 0, str, &hKeyTmpO);
12854 2 : if (hKeyTmpO)
12855 0 : db_delete_key(hDB, hKeyTmpO, FALSE);
12856 2 : db_create_key(hDB, 0, str, TID_KEY);
12857 2 : status = db_find_key(hDB, 0, str, &hKeyTmpO);
12858 2 : if (status != DB_SUCCESS) {
12859 0 : db_unlock_database(hDB);
12860 0 : return status;
12861 : }
12862 :
12863 2 : status = db_paste(hDB, hKeyTmp, init_str);
12864 2 : if (status != DB_SUCCESS) {
12865 0 : db_unlock_database(hDB);
12866 0 : return status;
12867 : }
12868 :
12869 2 : buffer_size = 10000;
12870 2 : buffer = (char *) malloc(buffer_size);
12871 2 : assert(buffer != NULL);
12872 :
12873 : do {
12874 2 : size = buffer_size;
12875 2 : status = db_copy(hDB, hKeyOrig, buffer, &size, "");
12876 2 : if (status == DB_TRUNCATED) {
12877 0 : buffer_size += 10000;
12878 0 : buffer = (char *) realloc(buffer, buffer_size);
12879 0 : assert(buffer != NULL);
12880 0 : continue;
12881 : }
12882 2 : if (status != DB_SUCCESS) {
12883 0 : free(buffer);
12884 0 : db_unlock_database(hDB);
12885 0 : return status;
12886 : }
12887 :
12888 2 : } while (status != DB_SUCCESS);
12889 :
12890 2 : status = db_paste(hDB, hKeyTmpO, buffer);
12891 2 : if (status != DB_SUCCESS) {
12892 0 : free(buffer);
12893 0 : db_unlock_database(hDB);
12894 0 : return status;
12895 : }
12896 :
12897 : /* merge temporay record and original record */
12898 2 : db_scan_tree_link(hDB, hKeyTmpO, 0, merge_records, NULL);
12899 :
12900 : /* delete original record */
12901 : while (1) {
12902 4 : db_enum_link(hDB, hKeyOrig, 0, &hSubkey);
12903 4 : if (!hSubkey)
12904 2 : break;
12905 :
12906 2 : status = db_delete_key(hDB, hSubkey, FALSE);
12907 2 : if (status != DB_SUCCESS) {
12908 0 : free(buffer);
12909 0 : db_unlock_database(hDB);
12910 0 : return status;
12911 : }
12912 : }
12913 :
12914 : /* copy merged record to original record */
12915 : do {
12916 2 : size = buffer_size;
12917 2 : status = db_copy(hDB, hKeyTmp, buffer, &size, "");
12918 2 : if (status == DB_TRUNCATED) {
12919 0 : buffer_size += 10000;
12920 0 : buffer = (char *) realloc(buffer, buffer_size);
12921 0 : continue;
12922 : }
12923 2 : if (status != DB_SUCCESS) {
12924 0 : free(buffer);
12925 0 : db_unlock_database(hDB);
12926 0 : return status;
12927 : }
12928 :
12929 2 : } while (status != DB_SUCCESS);
12930 :
12931 2 : status = db_paste(hDB, hKeyOrig, buffer);
12932 2 : if (status != DB_SUCCESS) {
12933 0 : free(buffer);
12934 0 : db_unlock_database(hDB);
12935 0 : return status;
12936 : }
12937 :
12938 : /* delete temporary records */
12939 2 : db_delete_key(hDB, hKeyTmp, FALSE);
12940 2 : db_delete_key(hDB, hKeyTmpO, FALSE);
12941 :
12942 2 : free(buffer);
12943 2 : buffer = NULL;
12944 0 : } else if (status == DB_NO_KEY) {
12945 : /* create fresh record */
12946 0 : db_create_key(hDB, hKey, key_name, TID_KEY);
12947 0 : status = db_find_key(hDB, hKey, key_name, &hKeyTmp);
12948 0 : if (status != DB_SUCCESS) {
12949 0 : db_unlock_database(hDB);
12950 0 : return status;
12951 : }
12952 :
12953 0 : status = db_paste(hDB, hKeyTmp, init_str);
12954 0 : if (status != DB_SUCCESS) {
12955 0 : db_unlock_database(hDB);
12956 0 : return status;
12957 : }
12958 : } else {
12959 0 : cm_msg(MERROR, "db_create_record", "aborting on unexpected failure of db_find_key(%s), status %d", key_name, status);
12960 0 : abort();
12961 : }
12962 :
12963 2 : db_unlock_database(hDB);
12964 :
12965 2 : return DB_SUCCESS;
12966 : }
12967 :
12968 : /********************************************************************/
12969 : /**
12970 : This function ensures that a certain ODB subtree matches
12971 : a given C structure, by comparing the init_str with the
12972 : current ODB structure. If the record does not exist at all,
12973 : it is created with the default values in init_str. If it
12974 : does exist but does not match the variables in init_str,
12975 : the function returns an error if correct=FALSE or calls
12976 : db_create_record() if correct=TRUE.
12977 : @param hDB ODB handle obtained via cm_get_experiment_database().
12978 : @param hKey Handle for key where search starts, zero for root.
12979 : @param keyname Name of key to search, can contain directories.
12980 : @param rec_str ASCII representation of ODB record in the format
12981 : @param correct If TRUE, correct ODB record if necessary
12982 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_KEY, DB_STRUCT_MISMATCH
12983 : */
12984 0 : INT db_check_record(HNDLE hDB, HNDLE hKey, const char *keyname, const char *rec_str, BOOL correct)
12985 : {
12986 : char line[MAX_STRING_LENGTH];
12987 : char title[MAX_STRING_LENGTH];
12988 : char key_name[MAX_STRING_LENGTH];
12989 : char info_str[MAX_STRING_LENGTH + 50];
12990 : char *pc;
12991 : const char *pold, *rec_str_orig;
12992 : DWORD tid;
12993 : INT i, j, n_data, string_length, status;
12994 : HNDLE hKeyRoot, hKeyTest;
12995 : KEY key;
12996 : int bad_string_length;
12997 :
12998 0 : if (rpc_is_remote())
12999 0 : return rpc_call(RPC_DB_CHECK_RECORD, hDB, hKey, keyname, rec_str, correct);
13000 :
13001 : /* check if record exists */
13002 0 : status = db_find_key(hDB, hKey, keyname, &hKeyRoot);
13003 :
13004 : /* create record if not */
13005 0 : if (status == DB_NO_KEY) {
13006 0 : if (correct)
13007 0 : return db_create_record(hDB, hKey, keyname, rec_str);
13008 0 : return DB_NO_KEY;
13009 : }
13010 :
13011 0 : assert(hKeyRoot);
13012 :
13013 0 : title[0] = 0;
13014 0 : rec_str_orig = rec_str;
13015 :
13016 0 : db_get_key(hDB, hKeyRoot, &key);
13017 0 : if (key.type == TID_KEY)
13018 : /* get next subkey which is not of type TID_KEY */
13019 0 : db_get_next_link(hDB, hKeyRoot, &hKeyTest);
13020 : else
13021 : /* test root key itself */
13022 0 : hKeyTest = hKeyRoot;
13023 :
13024 0 : if (hKeyTest == 0 && *rec_str != 0) {
13025 0 : if (correct)
13026 0 : return db_create_record(hDB, hKey, keyname, rec_str_orig);
13027 :
13028 0 : return DB_STRUCT_MISMATCH;
13029 : }
13030 :
13031 : do {
13032 0 : if (*rec_str == 0)
13033 0 : break;
13034 :
13035 0 : for (i = 0; *rec_str != '\n' && *rec_str && i < MAX_STRING_LENGTH; i++)
13036 0 : line[i] = *rec_str++;
13037 :
13038 0 : if (i == MAX_STRING_LENGTH) {
13039 0 : cm_msg(MERROR, "db_check_record", "line too long");
13040 0 : return DB_TRUNCATED;
13041 : }
13042 :
13043 0 : line[i] = 0;
13044 0 : if (*rec_str == '\n')
13045 0 : rec_str++;
13046 :
13047 : /* check if it is a section title */
13048 0 : if (line[0] == '[') {
13049 : /* extract title and append '/' */
13050 0 : strcpy(title, line + 1);
13051 0 : if (strchr(title, ']'))
13052 0 : *strchr(title, ']') = 0;
13053 0 : if (title[0] && title[strlen(title) - 1] != '/')
13054 0 : strcat(title, "/");
13055 : } else {
13056 : /* valid data line if it includes '=' and no ';' */
13057 0 : if (strchr(line, '=') && line[0] != ';') {
13058 : /* copy type info and data */
13059 0 : pc = strchr(line, '=') + 1;
13060 0 : while (*pc == ' ')
13061 0 : pc++;
13062 0 : strcpy(info_str, pc);
13063 :
13064 : /* extract key name */
13065 0 : *strchr(line, '=') = 0;
13066 :
13067 0 : pc = &line[strlen(line) - 1];
13068 0 : while (*pc == ' ')
13069 0 : *pc-- = 0;
13070 :
13071 0 : mstrlcpy(key_name, line, sizeof(key_name));
13072 :
13073 : /* evaluate type info */
13074 0 : strcpy(line, info_str);
13075 0 : if (strchr(line, ' '))
13076 0 : *strchr(line, ' ') = 0;
13077 :
13078 0 : n_data = 1;
13079 0 : if (strchr(line, '[')) {
13080 0 : n_data = atol(strchr(line, '[') + 1);
13081 0 : *strchr(line, '[') = 0;
13082 : }
13083 :
13084 0 : for (tid = 0; tid < TID_LAST; tid++)
13085 0 : if (strcmp(rpc_tid_name(tid), line) == 0)
13086 0 : break;
13087 0 : if (tid == TID_LAST) {
13088 0 : for (tid = 0; tid < TID_LAST; tid++)
13089 0 : if (strcmp(rpc_tid_name_old(tid), line) == 0)
13090 0 : break;
13091 : }
13092 :
13093 0 : string_length = 0;
13094 :
13095 0 : if (tid == TID_LAST)
13096 0 : cm_msg(MERROR, "db_check_record", "found unknown data type \"%s\" in ODB file", line);
13097 : else {
13098 : /* skip type info */
13099 0 : pc = info_str;
13100 0 : while (*pc != ' ' && *pc)
13101 0 : pc++;
13102 0 : while ((*pc == ' ' || *pc == ':') && *pc)
13103 0 : pc++;
13104 :
13105 0 : if (n_data > 1) {
13106 0 : info_str[0] = 0;
13107 0 : if (!*rec_str)
13108 0 : break;
13109 :
13110 0 : for (j = 0; *rec_str != '\n' && *rec_str; j++)
13111 0 : info_str[j] = *rec_str++;
13112 0 : info_str[j] = 0;
13113 0 : if (*rec_str == '\n')
13114 0 : rec_str++;
13115 : }
13116 :
13117 0 : for (i = 0; i < n_data; i++) {
13118 : /* strip trailing \n */
13119 0 : pc = &info_str[strlen(info_str) - 1];
13120 0 : while (*pc == '\n' || *pc == '\r')
13121 0 : *pc-- = 0;
13122 :
13123 0 : if (tid == TID_STRING || tid == TID_LINK) {
13124 0 : if (!string_length) {
13125 0 : if (info_str[1] == '=')
13126 0 : string_length = -1;
13127 : else {
13128 0 : pc = strchr(info_str, '[');
13129 0 : if (pc != NULL)
13130 0 : string_length = atoi(pc + 1);
13131 : else
13132 0 : string_length = -1;
13133 : }
13134 0 : if (string_length > MAX_STRING_LENGTH) {
13135 0 : string_length = MAX_STRING_LENGTH;
13136 0 : cm_msg(MERROR, "db_check_record", "found string exceeding MAX_STRING_LENGTH");
13137 : }
13138 : }
13139 :
13140 0 : if (string_length == -1) {
13141 : /* multi-line string */
13142 0 : if (strstr(rec_str, "\n====#$@$#====\n") != NULL) {
13143 0 : string_length = (POINTER_T) strstr(rec_str, "\n====#$@$#====\n") - (POINTER_T) rec_str + 1;
13144 :
13145 0 : rec_str = strstr(rec_str, "\n====#$@$#====\n") + strlen("\n====#$@$#====\n");
13146 : } else
13147 0 : cm_msg(MERROR, "db_check_record", "found multi-line string without termination sequence");
13148 : } else {
13149 0 : if (strchr(info_str, ']'))
13150 0 : pc = strchr(info_str, ']') + 1;
13151 : else
13152 0 : pc = info_str + 2;
13153 0 : while (*pc && *pc != ' ')
13154 0 : pc++;
13155 0 : while (*pc && *pc == ' ')
13156 0 : pc++;
13157 :
13158 : /* limit string size */
13159 0 : *(pc + string_length - 1) = 0;
13160 : }
13161 : } else {
13162 0 : pc = info_str;
13163 :
13164 0 : if (n_data > 1 && info_str[0] == '[') {
13165 0 : pc = strchr(info_str, ']') + 1;
13166 0 : while (*pc && *pc == ' ')
13167 0 : pc++;
13168 : }
13169 : }
13170 :
13171 0 : if (i < n_data - 1) {
13172 0 : info_str[0] = 0;
13173 0 : if (!*rec_str)
13174 0 : break;
13175 :
13176 0 : pold = rec_str;
13177 :
13178 0 : for (j = 0; *rec_str != '\n' && *rec_str; j++)
13179 0 : info_str[j] = *rec_str++;
13180 0 : info_str[j] = 0;
13181 0 : if (*rec_str == '\n')
13182 0 : rec_str++;
13183 :
13184 : /* test if valid data */
13185 0 : if (tid != TID_STRING && tid != TID_LINK) {
13186 0 : if (info_str[0] == 0 || (strchr(info_str, '=')
13187 0 : && strchr(info_str, ':')))
13188 0 : rec_str = pold;
13189 : }
13190 : }
13191 : }
13192 :
13193 : /* if rec_str contains key, but not ODB, return mismatch */
13194 0 : if (!hKeyTest) {
13195 0 : if (correct)
13196 0 : return db_create_record(hDB, hKey, keyname, rec_str_orig);
13197 :
13198 0 : return DB_STRUCT_MISMATCH;
13199 : }
13200 :
13201 0 : status = db_get_key(hDB, hKeyTest, &key);
13202 0 : assert(status == DB_SUCCESS);
13203 :
13204 0 : bad_string_length = 0;
13205 :
13206 0 : if (key.type == TID_STRING) {
13207 : //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);
13208 0 : if (string_length > 0 && string_length != key.item_size) {
13209 0 : bad_string_length = 1;
13210 : }
13211 : }
13212 :
13213 : /* check rec_str vs. ODB key */
13214 0 : if (!equal_ustring(key.name, key_name) || key.type != tid || key.num_values != n_data || bad_string_length) {
13215 : //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);
13216 0 : if (correct)
13217 0 : return db_create_record(hDB, hKey, keyname, rec_str_orig);
13218 :
13219 0 : return DB_STRUCT_MISMATCH;
13220 : }
13221 :
13222 : /* get next key in ODB */
13223 0 : db_get_next_link(hDB, hKeyTest, &hKeyTest);
13224 : }
13225 : }
13226 : }
13227 : } while (TRUE);
13228 :
13229 0 : return DB_SUCCESS;
13230 : }
13231 :
13232 : /********************************************************************/
13233 : /**
13234 : Open a record. Create a local copy and maintain an automatic update.
13235 :
13236 : This function opens a hot-link between an ODB sub-tree and a local
13237 : structure. The sub-tree is copied to the structure automatically every time it is
13238 : modified by someone else. Additionally, a callback function can be declared
13239 : which is called after the structure has been updated. The callback function
13240 : receives the database handle and the key handle as parameters.
13241 :
13242 : Problems might occur if the ODB sub-tree contains values which don't match the
13243 : C structure. Although the structure size is checked against the sub-tree size, no
13244 : checking can be done if the type and order of the values in the structure are the
13245 : same than those in the ODB sub-tree. Therefore it is recommended to use the
13246 : function db_create_record() before db_open_record() is used which
13247 : ensures that both are equivalent.
13248 :
13249 : The access mode might either be MODE_READ or MODE_WRITE. In read mode,
13250 : the ODB sub-tree is automatically copied to the local structure when modified by
13251 : other clients. In write mode, the local structure is copied to the ODB sub-tree if it
13252 : has been modified locally. This update has to be manually scheduled by calling
13253 : db_send_changed_records() periodically in the main loop. The system
13254 : keeps a copy of the local structure to determine if its contents has been changed.
13255 :
13256 : If MODE_ALLOC is or'ed with the access mode, the memory for the structure is
13257 : allocated internally. The structure pointer must contain a pointer to a pointer to
13258 : the structure. The internal memory is released when db_close_record() is
13259 : called.
13260 : - To open a record in write mode.
13261 : \code
13262 : struct {
13263 : INT level1;
13264 : INT level2;
13265 : } trigger_settings;
13266 : char *trigger_settings_str =
13267 : "[Settings]\n\
13268 : level1 = INT : 0\n\
13269 : level2 = INT : 0";
13270 : main()
13271 : {
13272 : HNDLE hDB, hkey, i=0;
13273 : ...
13274 : cm_get_experiment_database(&hDB, NULL);
13275 : db_create_record(hDB, 0, "/Equipment/Trigger", trigger_settings_str);
13276 : db_find_key(hDB, 0,"/Equipment/Trigger/Settings", &hkey);
13277 : db_open_record(hDB, hkey, &trigger_settings, sizeof(trigger_settings)
13278 : , MODE_WRITE, NULL);
13279 : do
13280 : {
13281 : trigger_settings.level1 = i++;
13282 : db_send_changed_records()
13283 : status = cm_yield(1000);
13284 : } while (status != RPC_SHUTDOWN && status != SS_ABORT);
13285 : ...
13286 : }
13287 : \endcode
13288 : @param hDB ODB handle obtained via cm_get_experiment_database().
13289 : @param hKey Handle for key where search starts, zero for root.
13290 : @param ptr If access_mode includes MODE_ALLOC:
13291 : Address of pointer which points to the record data after
13292 : the call if access_mode includes not MODE_ALLOC:
13293 : Address of record if ptr==NULL, only the dispatcher is called.
13294 : @param rec_size Record size in bytes
13295 : @param access_mode Mode for opening record, either MODE_READ or
13296 : MODE_WRITE. May be or'ed with MODE_ALLOC to
13297 : let db_open_record allocate the memory for the record.
13298 : @param (*dispatcher) Function which gets called when record is updated.The
13299 : argument list composed of: HNDLE hDB, HNDLE hKey, void *info
13300 : @param info Additional info passed to the dispatcher.
13301 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_MEMORY, DB_NO_ACCESS, DB_STRUCT_SIZE_MISMATCH
13302 : */
13303 0 : INT db_open_record(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size,
13304 : WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info)
13305 : {
13306 : INT idx, status, size;
13307 : KEY key;
13308 : void *data;
13309 :
13310 : /* allocate new space for the local record list */
13311 0 : if (_record_list_entries == 0) {
13312 0 : _record_list = (RECORD_LIST *) malloc(sizeof(RECORD_LIST));
13313 0 : assert(_record_list != NULL);
13314 0 : memset(_record_list, 0, sizeof(RECORD_LIST));
13315 0 : if (_record_list == NULL) {
13316 0 : cm_msg(MERROR, "db_open_record", "not enough memory");
13317 0 : return DB_NO_MEMORY;
13318 : }
13319 :
13320 0 : _record_list_entries = 1;
13321 0 : idx = 0;
13322 : } else {
13323 : /* check for a deleted entry */
13324 0 : for (idx = 0; idx < _record_list_entries; idx++)
13325 0 : if (!_record_list[idx].handle)
13326 0 : break;
13327 :
13328 : /* if not found, create new one */
13329 0 : if (idx == _record_list_entries) {
13330 0 : _record_list = (RECORD_LIST *) realloc(_record_list, sizeof(RECORD_LIST) * (_record_list_entries + 1));
13331 0 : if (_record_list == NULL) {
13332 0 : cm_msg(MERROR, "db_open_record", "not enough memory");
13333 0 : return DB_NO_MEMORY;
13334 : }
13335 :
13336 0 : memset(&_record_list[_record_list_entries], 0, sizeof(RECORD_LIST));
13337 :
13338 0 : _record_list_entries++;
13339 : }
13340 : }
13341 :
13342 0 : db_get_key(hDB, hKey, &key);
13343 :
13344 : /* check record size */
13345 0 : status = db_get_record_size(hDB, hKey, 0, &size);
13346 0 : if (status != DB_SUCCESS) {
13347 0 : _record_list_entries--;
13348 0 : cm_msg(MERROR, "db_open_record", "cannot get record size, db_get_record_size() status %d", status);
13349 0 : return DB_NO_MEMORY;
13350 : }
13351 :
13352 0 : if (size != rec_size && ptr != NULL) {
13353 0 : _record_list_entries--;
13354 0 : std::string path = db_get_path(hDB, hKey);
13355 0 : cm_msg(MERROR, "db_open_record", "struct size mismatch for \"%s\" (expected size: %d, size in ODB: %d)", path.c_str(), rec_size, size);
13356 0 : return DB_STRUCT_SIZE_MISMATCH;
13357 0 : }
13358 :
13359 : /* check for read access */
13360 0 : if (((key.access_mode & MODE_EXCLUSIVE) && (access_mode & MODE_WRITE))
13361 0 : || (!(key.access_mode & MODE_WRITE) && (access_mode & MODE_WRITE))
13362 0 : || (!(key.access_mode & MODE_READ) && (access_mode & MODE_READ))) {
13363 0 : _record_list_entries--;
13364 0 : return DB_NO_ACCESS;
13365 : }
13366 :
13367 0 : if (access_mode & MODE_ALLOC) {
13368 0 : data = malloc(size);
13369 :
13370 0 : if (data == NULL) {
13371 0 : _record_list_entries--;
13372 0 : cm_msg(MERROR, "db_open_record", "not enough memory, malloc(%d) returned NULL", size);
13373 0 : return DB_NO_MEMORY;
13374 : }
13375 :
13376 0 : memset(data, 0, size);
13377 :
13378 0 : *((void **) ptr) = data;
13379 : } else {
13380 0 : data = ptr;
13381 : }
13382 :
13383 : /* copy record to local memory */
13384 0 : if (access_mode & MODE_READ && data != NULL) {
13385 0 : status = db_get_record(hDB, hKey, data, &size, 0);
13386 0 : if (status != DB_SUCCESS) {
13387 0 : _record_list_entries--;
13388 0 : cm_msg(MERROR, "db_open_record", "cannot get record, db_get_record() status %d", status);
13389 0 : return DB_NO_MEMORY;
13390 : }
13391 : }
13392 :
13393 : /* copy local record to ODB */
13394 0 : if (access_mode & MODE_WRITE) {
13395 : /* only write to ODB if not in MODE_ALLOC */
13396 0 : if ((access_mode & MODE_ALLOC) == 0) {
13397 0 : status = db_set_record(hDB, hKey, data, size, 0);
13398 0 : if (status != DB_SUCCESS) {
13399 0 : _record_list_entries--;
13400 0 : cm_msg(MERROR, "db_open_record", "cannot set record, db_set_record() status %d", status);
13401 0 : return DB_NO_MEMORY;
13402 : }
13403 : }
13404 :
13405 : /* init a local copy of the record */
13406 0 : _record_list[idx].copy = malloc(size);
13407 0 : if (_record_list[idx].copy == NULL) {
13408 0 : cm_msg(MERROR, "db_open_record", "not enough memory");
13409 0 : return DB_NO_MEMORY;
13410 : }
13411 :
13412 0 : memcpy(_record_list[idx].copy, data, size);
13413 : }
13414 :
13415 : /* initialize record list */
13416 0 : _record_list[idx].handle = hKey;
13417 0 : _record_list[idx].hDB = hDB;
13418 0 : _record_list[idx].access_mode = access_mode;
13419 0 : _record_list[idx].data = data;
13420 0 : _record_list[idx].buf_size = size;
13421 0 : _record_list[idx].dispatcher = dispatcher;
13422 0 : _record_list[idx].info = info;
13423 :
13424 : /* add record entry in database structure */
13425 0 : return db_add_open_record(hDB, hKey, (WORD) (access_mode & ~MODE_ALLOC));
13426 : }
13427 :
13428 : /********************************************************************/
13429 : /**
13430 : Open a record. Create a local copy and maintain an automatic update.
13431 :
13432 : This function is same as db_open_record(), except that it calls
13433 : db_check_record(), db_get_record1() and db_create_record()
13434 : to ensure that the ODB structure matches
13435 :
13436 : Parameters are the same as for db_open_record():
13437 :
13438 : @param hDB ODB handle obtained via cm_get_experiment_database().
13439 : @param hKey Handle for key where search starts, zero for root.
13440 : @param ptr If access_mode includes MODE_ALLOC:
13441 : Address of pointer which points to the record data after
13442 : the call if access_mode includes not MODE_ALLOC:
13443 : Address of record if ptr==NULL, only the dispatcher is called.
13444 : @param rec_size Record size in bytes
13445 : @param access_mode Mode for opening record, either MODE_READ or
13446 : MODE_WRITE. May be or'ed with MODE_ALLOC to
13447 : let db_open_record allocate the memory for the record.
13448 : @param (*dispatcher) Function which gets called when record is updated.The
13449 : argument list composed of: HNDLE hDB, HNDLE hKey, void *info
13450 : @param info Additional info passed to the dispatcher.
13451 : @param rec_str ASCII representation of ODB record in the format
13452 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_MEMORY, DB_NO_ACCESS, DB_STRUCT_SIZE_MISMATCH
13453 : */
13454 0 : INT db_open_record1(HNDLE hDB, HNDLE hKey, void *ptr, INT rec_size,
13455 : WORD access_mode, void (*dispatcher) (INT, INT, void *), void *info,
13456 : const char *rec_str)
13457 : {
13458 0 : if (rec_str) {
13459 : int status;
13460 0 : if (rec_size) {
13461 : char* pbuf;
13462 0 : int size = rec_size;
13463 0 : pbuf = (char*)malloc(size);
13464 0 : assert(pbuf != NULL);
13465 0 : status = db_get_record1(hDB, hKey, pbuf, &size, 0, rec_str);
13466 0 : free(pbuf);
13467 0 : if (status != DB_SUCCESS)
13468 0 : return status;
13469 : }
13470 :
13471 0 : status = db_check_record(hDB, hKey, "", rec_str, TRUE);
13472 0 : if (status != DB_SUCCESS)
13473 0 : return status;
13474 : }
13475 :
13476 0 : return db_open_record(hDB, hKey, ptr, rec_size, access_mode, dispatcher, info);
13477 : }
13478 :
13479 : /********************************************************************/
13480 : /**
13481 : Close a record previously opend with db_open_record.
13482 : @param hDB ODB handle obtained via cm_get_experiment_database().
13483 : @param hKey Handle for key where search starts, zero for root.
13484 : @return DB_SUCCESS, DB_INVALID_HANDLE
13485 : */
13486 0 : INT db_close_record(HNDLE hDB, HNDLE hKey)
13487 : {
13488 : #ifdef LOCAL_ROUTINES
13489 : {
13490 : INT i;
13491 :
13492 0 : for (i = 0; i < _record_list_entries; i++)
13493 0 : if (_record_list[i].handle == hKey && _record_list[i].hDB == hDB)
13494 0 : break;
13495 :
13496 0 : if (i == _record_list_entries)
13497 0 : return DB_INVALID_HANDLE;
13498 :
13499 : /* remove record entry from database structure */
13500 0 : db_remove_open_record(hDB, hKey, TRUE);
13501 :
13502 : /* free local memory */
13503 0 : if (_record_list[i].access_mode & MODE_ALLOC) {
13504 0 : free(_record_list[i].data);
13505 0 : _record_list[i].data = NULL;
13506 : }
13507 :
13508 0 : if (_record_list[i].access_mode & MODE_WRITE) {
13509 0 : free(_record_list[i].copy);
13510 0 : _record_list[i].copy = NULL;
13511 : }
13512 :
13513 0 : memset(&_record_list[i], 0, sizeof(RECORD_LIST));
13514 : }
13515 : #endif /* LOCAL_ROUTINES */
13516 :
13517 0 : return DB_SUCCESS;
13518 : }
13519 :
13520 : /********************************************************************/
13521 : /**
13522 : Release local memory for open records.
13523 : This routines is called by db_close_all_databases() and
13524 : cm_disconnect_experiment()
13525 : @return DB_SUCCESS, DB_INVALID_HANDLE
13526 : */
13527 2 : INT db_close_all_records()
13528 : {
13529 : INT i;
13530 :
13531 2 : for (i = 0; i < _record_list_entries; i++) {
13532 0 : if (_record_list[i].handle) {
13533 0 : if (_record_list[i].access_mode & MODE_WRITE) {
13534 0 : free(_record_list[i].copy);
13535 0 : _record_list[i].copy = NULL;
13536 : }
13537 :
13538 0 : if (_record_list[i].access_mode & MODE_ALLOC) {
13539 0 : free(_record_list[i].data);
13540 0 : _record_list[i].data = NULL;
13541 : }
13542 :
13543 0 : memset(&_record_list[i], 0, sizeof(RECORD_LIST));
13544 : }
13545 : }
13546 :
13547 2 : if (_record_list_entries > 0) {
13548 0 : _record_list_entries = 0;
13549 0 : free(_record_list);
13550 0 : _record_list = NULL;
13551 : }
13552 :
13553 2 : return DB_SUCCESS;
13554 : }
13555 :
13556 : /********************************************************************/
13557 : /**
13558 : db_open_record() and db_watch() event handler
13559 :
13560 : @param hDB ODB handle obtained via cm_get_experiment_database().
13561 : @param hKey Handle for key which changed.
13562 : @param index Index for array keys.
13563 : @return DB_SUCCESS, DB_INVALID_HANDLE
13564 : */
13565 0 : INT db_update_record_local(INT hDB, INT hKeyRoot, INT hKey, int index)
13566 : {
13567 : INT i, size, status;
13568 :
13569 0 : status = DB_INVALID_HANDLE;
13570 :
13571 : /* check all record entries for matching key */
13572 0 : for (i = 0; i < _record_list_entries; i++)
13573 0 : if (_record_list[i].handle == hKeyRoot) {
13574 0 : status = DB_SUCCESS;
13575 :
13576 : /* get updated data if record not opened in write mode */
13577 0 : if ((_record_list[i].access_mode & MODE_WRITE) == 0) {
13578 0 : size = _record_list[i].buf_size;
13579 0 : if (_record_list[i].data != NULL) {
13580 0 : status = db_get_record(hDB, hKeyRoot, _record_list[i].data, &size, 0); // db_open_record() update
13581 : //printf("db_open_record update status %d, size %d %d\n", status, _record_list[i].buf_size, size);
13582 : }
13583 :
13584 : /* call dispatcher if requested */
13585 0 : if (_record_list[i].dispatcher)
13586 0 : _record_list[i].dispatcher(hDB, hKeyRoot, _record_list[i].info);
13587 : }
13588 : }
13589 :
13590 : /* check all watch entries for matching key */
13591 0 : for (i = 0; i < _watch_list_entries; i++)
13592 0 : if (_watch_list[i].handle == hKeyRoot) {
13593 0 : status = DB_SUCCESS;
13594 :
13595 : /* call dispatcher if requested */
13596 0 : if (_watch_list[i].dispatcher)
13597 0 : _watch_list[i].dispatcher(hDB, hKey, index, _watch_list[i].info);
13598 : }
13599 :
13600 0 : return status;
13601 : }
13602 :
13603 : /********************************************************************/
13604 : /**
13605 : Relay db_open_record() and db_watch() notification to the remote client.
13606 : @param hDB ODB handle obtained via cm_get_experiment_database().
13607 : @param hKey Handle for key which changed.
13608 : @param index Index for array keys.
13609 : @param s client socket.
13610 : @return DB_SUCCESS, DB_INVALID_HANDLE
13611 : */
13612 0 : INT db_update_record_mserver(INT hDB, INT hKeyRoot, INT hKey, int index, int client_socket)
13613 : {
13614 : char buffer[32];
13615 :
13616 0 : int convert_flags = rpc_get_convert_flags();
13617 :
13618 0 : NET_COMMAND* nc = (NET_COMMAND *) buffer;
13619 :
13620 0 : nc->header.routine_id = MSG_ODB;
13621 0 : nc->header.param_size = 4 * sizeof(INT);
13622 0 : *((INT *) nc->param) = hDB;
13623 0 : *((INT *) nc->param + 1) = hKeyRoot;
13624 0 : *((INT *) nc->param + 2) = hKey;
13625 0 : *((INT *) nc->param + 3) = index;
13626 :
13627 0 : if (convert_flags) {
13628 0 : rpc_convert_single(&nc->header.routine_id, TID_UINT32, RPC_OUTGOING, convert_flags);
13629 0 : rpc_convert_single(&nc->header.param_size, TID_UINT32, RPC_OUTGOING, convert_flags);
13630 0 : rpc_convert_single(&nc->param[0], TID_UINT32, RPC_OUTGOING, convert_flags);
13631 0 : rpc_convert_single(&nc->param[4], TID_UINT32, RPC_OUTGOING, convert_flags);
13632 0 : rpc_convert_single(&nc->param[8], TID_UINT32, RPC_OUTGOING, convert_flags);
13633 0 : rpc_convert_single(&nc->param[12], TID_UINT32, RPC_OUTGOING, convert_flags);
13634 : }
13635 :
13636 : /* send the update notification to the client */
13637 0 : send_tcp(client_socket, buffer, sizeof(NET_COMMAND_HEADER) + 4 * sizeof(INT), 0);
13638 :
13639 0 : return DB_SUCCESS;
13640 : }
13641 :
13642 : /********************************************************************/
13643 : /**
13644 : Send all records to the ODB which were changed locally since the last
13645 : call to this function.
13646 :
13647 : This function is valid if used in conjunction with db_open_record()
13648 : under the condition the record is open as MODE_WRITE access code.
13649 :
13650 : - Full example dbchange.c which can be compiled as follow
13651 : \code
13652 : gcc -DOS_LINUX -I/midas/include -o dbchange dbchange.c
13653 : /midas/linux/lib/libmidas.a -lutil}
13654 :
13655 : \begin{verbatim}
13656 : //------- dbchange.c
13657 : #include "midas.h"
13658 : #include "msystem.h"
13659 : \endcode
13660 :
13661 : \code
13662 : //-------- BOF dbchange.c
13663 : typedef struct {
13664 : INT my_number;
13665 : float my_rate;
13666 : } MY_STATISTICS;
13667 :
13668 : MY_STATISTICS myrec;
13669 :
13670 : #define MY_STATISTICS(_name) char *_name[] = {\
13671 : "My Number = INT : 0",\
13672 : "My Rate = FLOAT : 0",\
13673 : "",\
13674 : NULL }
13675 :
13676 : HNDLE hDB, hKey;
13677 :
13678 : // Main
13679 : int main(unsigned int argc,char **argv)
13680 : {
13681 : char host_name[HOST_NAME_LENGTH];
13682 : char expt_name[HOST_NAME_LENGTH];
13683 : INT lastnumber, status, msg;
13684 : BOOL debug=FALSE;
13685 : char i, ch;
13686 : DWORD update_time, mainlast_time;
13687 : MY_STATISTICS (my_stat);
13688 :
13689 : // set default
13690 : host_name[0] = 0;
13691 : expt_name[0] = 0;
13692 :
13693 : // get default
13694 : cm_get_environment(host_name, sizeof(host_name), expt_name, sizeof(expt_name));
13695 :
13696 : // get parameters
13697 : for (i=1 ; i<argc ; i++)
13698 : {
13699 : if (argv[i][0] == '-' && argv[i][1] == 'd')
13700 : debug = TRUE;
13701 : else if (argv[i][0] == '-')
13702 : {
13703 : if (i+1 >= argc || argv[i+1][0] == '-')
13704 : goto usage;
13705 : if (strncmp(argv[i],"-e",2) == 0)
13706 : strcpy(expt_name, argv[++i]);
13707 : else if (strncmp(argv[i],"-h",2)==0)
13708 : strcpy(host_name, argv[++i]);
13709 : }
13710 : else
13711 : {
13712 : usage:
13713 : printf("usage: dbchange [-h <Hostname>] [-e <Experiment>]\n");
13714 : return 0;
13715 : }
13716 : }
13717 :
13718 : // connect to experiment
13719 : status = cm_connect_experiment(host_name, expt_name, "dbchange", 0);
13720 : if (status != CM_SUCCESS)
13721 : return 1;
13722 :
13723 : // Connect to DB
13724 : cm_get_experiment_database(&hDB, &hKey);
13725 :
13726 : // Create a default structure in ODB
13727 : db_create_record(hDB, 0, "My statistics", strcomb1(my_stat).c_str());
13728 :
13729 : // Retrieve key for that strucutre in ODB
13730 : if (db_find_key(hDB, 0, "My statistics", &hKey) != DB_SUCCESS)
13731 : {
13732 : cm_msg(MERROR, "mychange", "cannot find My statistics");
13733 : goto error;
13734 : }
13735 :
13736 : // Hot link this structure in Write mode
13737 : status = db_open_record(hDB, hKey, &myrec, sizeof(MY_STATISTICS), MODE_WRITE, NULL, NULL);
13738 : if (status != DB_SUCCESS)
13739 : {
13740 : cm_msg(MERROR, "mychange", "cannot open My statistics record");
13741 : goto error;
13742 : }
13743 :
13744 : // initialize ss_getchar()
13745 : ss_getchar(0);
13746 :
13747 : // Main loop
13748 : do
13749 : {
13750 : // Update local structure
13751 : if ((ss_millitime() - update_time) > 100)
13752 : {
13753 : myrec.my_number += 1;
13754 : if (myrec.my_number - lastnumber) {
13755 : myrec.my_rate = 1000.f * (float) (myrec.my_number - lastnumber)
13756 : / (float) (ss_millitime() - update_time);
13757 : }
13758 : update_time = ss_millitime();
13759 : lastnumber = myrec.my_number;
13760 : }
13761 :
13762 : // Publish local structure to ODB (db_send_changed_record)
13763 : if ((ss_millitime() - mainlast_time) > 5000)
13764 : {
13765 : db_send_changed_records(); // <------- Call
13766 : mainlast_time = ss_millitime();
13767 : }
13768 :
13769 : // Check for keyboard interaction
13770 : ch = 0;
13771 : while (ss_kbhit())
13772 : {
13773 : ch = ss_getchar(0);
13774 : if (ch == -1)
13775 : ch = getchar();
13776 : if ((char) ch == '!')
13777 : break;
13778 : }
13779 : msg = cm_yield(20);
13780 : } while (msg != RPC_SHUTDOWN && msg != SS_ABORT && ch != '!');
13781 :
13782 : error:
13783 : cm_disconnect_experiment();
13784 : return 1;
13785 : }
13786 : //-------- EOF dbchange.c
13787 : \endcode
13788 : @return DB_SUCCESS
13789 : */
13790 0 : INT db_send_changed_records()
13791 : {
13792 : INT i;
13793 :
13794 0 : for (i = 0; i < _record_list_entries; i++)
13795 0 : if (_record_list[i].access_mode & MODE_WRITE) {
13796 0 : if (memcmp(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size) != 0) {
13797 0 : if (rpc_is_remote()) {
13798 0 : int align = ss_get_struct_align();
13799 0 : rpc_call(RPC_DB_SET_RECORD|RPC_NO_REPLY, _record_list[i].hDB, _record_list[i].handle, _record_list[i].data, _record_list[i].buf_size, align);
13800 : } else {
13801 0 : db_set_record(_record_list[i].hDB, _record_list[i].handle, _record_list[i].data, _record_list[i].buf_size, 0);
13802 : }
13803 0 : memcpy(_record_list[i].copy, _record_list[i].data, _record_list[i].buf_size);
13804 : }
13805 : }
13806 :
13807 0 : return DB_SUCCESS;
13808 : }
13809 :
13810 : /*------------------------------------------------------------------*/
13811 :
13812 : /********************************************************************/
13813 : /**
13814 : Watch an ODB subtree. The callback function gets called whenever a
13815 : key in the watched subtree changes. The callback function
13816 : receives 4 parameters: the database handle, the key handle of the entry that
13817 : changed (could be a child if you're watching a directory), the index that changed
13818 : (if it was part of an array), a user-specified 'info' parameter.
13819 :
13820 : @param hDB ODB handle obtained via cm_get_experiment_database().
13821 : @param hKey Handle for key at top of subtree to watch, zero for root.
13822 : @param (*dispatcher) Function which gets called when record is updated.The
13823 : argument list composed of: HNDLE hDB, HNDLE hKey, INT idx, void* info
13824 : @return DB_SUCCESS, DB_INVALID_HANDLE, DB_NO_MEMORY, DB_NO_ACCESS, DB_STRUCT_SIZE_MISMATCH
13825 : */
13826 2 : INT db_watch(HNDLE hDB, HNDLE hKey, void (*dispatcher) (INT, INT, INT, void*), void* info)
13827 : {
13828 : INT idx, status;
13829 : KEY key;
13830 :
13831 : /* check for valid key */
13832 2 : assert(hKey);
13833 :
13834 : /* allocate new space for the local record list */
13835 2 : if (_watch_list_entries == 0) {
13836 2 : _watch_list = (WATCH_LIST *) malloc(sizeof(WATCH_LIST));
13837 2 : assert(_watch_list != NULL);
13838 2 : memset(_watch_list, 0, sizeof(WATCH_LIST));
13839 2 : if (_watch_list == NULL) {
13840 0 : cm_msg(MERROR, "db_watch", "not enough memory");
13841 0 : return DB_NO_MEMORY;
13842 : }
13843 :
13844 2 : _watch_list_entries = 1;
13845 2 : idx = 0;
13846 : } else {
13847 : /* check for a deleted entry */
13848 0 : for (idx = 0; idx < _watch_list_entries; idx++)
13849 0 : if (!_watch_list[idx].handle)
13850 0 : break;
13851 :
13852 : /* if not found, create new one */
13853 0 : if (idx == _watch_list_entries) {
13854 0 : _watch_list = (WATCH_LIST *) realloc(_watch_list, sizeof(WATCH_LIST) * (_watch_list_entries + 1));
13855 0 : if (_watch_list == NULL) {
13856 0 : cm_msg(MERROR, "db_watch", "not enough memory");
13857 0 : return DB_NO_MEMORY;
13858 : }
13859 :
13860 0 : memset(&_watch_list[_watch_list_entries], 0, sizeof(WATCH_LIST));
13861 :
13862 0 : _watch_list_entries++;
13863 : }
13864 : }
13865 :
13866 : /* check key */
13867 2 : status = db_get_key(hDB, hKey, &key);
13868 2 : if (status != DB_SUCCESS) {
13869 0 : _watch_list_entries--;
13870 0 : std::string path = db_get_path(hDB, hKey);
13871 0 : cm_msg(MERROR, "db_watch", "cannot get key \"%s\"", path.c_str());
13872 0 : return DB_NO_MEMORY;
13873 0 : }
13874 :
13875 : /* check for read access */
13876 2 : if (!(key.access_mode & MODE_READ)) {
13877 0 : _watch_list_entries--;
13878 0 : std::string path = db_get_path(hDB, hKey);
13879 0 : cm_msg(MERROR, "db_watch", "cannot get key \"%s\"", path.c_str());
13880 0 : return DB_NO_ACCESS;
13881 0 : }
13882 :
13883 : /* initialize record list */
13884 2 : _watch_list[idx].handle = hKey;
13885 2 : _watch_list[idx].hDB = hDB;
13886 2 : _watch_list[idx].dispatcher = dispatcher;
13887 2 : _watch_list[idx].info = info;
13888 :
13889 : /* add record entry in database structure */
13890 2 : return db_add_open_record(hDB, hKey, MODE_WATCH);
13891 : }
13892 :
13893 :
13894 : /********************************************************************/
13895 : /**
13896 : Remove watch callback from a key previously watched with db_watch.
13897 : @param hDB ODB handle obtained via cm_get_experiment_database().
13898 : @param hKey Handle for key, zero for root.
13899 : @return DB_SUCCESS, DB_INVALID_HANDLE
13900 : */
13901 2 : INT db_unwatch(HNDLE hDB, HNDLE hKey)
13902 : {
13903 : #ifdef LOCAL_ROUTINES
13904 : {
13905 : INT i;
13906 :
13907 2 : for (i = 0; i < _watch_list_entries; i++)
13908 2 : if (_watch_list[i].handle == hKey && _watch_list[i].hDB == hDB)
13909 2 : break;
13910 :
13911 2 : if (i == _watch_list_entries)
13912 0 : return DB_INVALID_HANDLE;
13913 :
13914 : /* remove record entry from database structure */
13915 2 : db_remove_open_record(hDB, hKey, TRUE);
13916 :
13917 2 : memset(&_watch_list[i], 0, sizeof(WATCH_LIST));
13918 : }
13919 : #endif /* LOCAL_ROUTINES */
13920 :
13921 2 : return DB_SUCCESS;
13922 : }
13923 :
13924 : /********************************************************************/
13925 : /**
13926 : Closes all watched variables.
13927 : This routines is called by db_close_all_databases() and
13928 : cm_disconnect_experiment()
13929 : @return DB_SUCCESS, DB_INVALID_HANDLE
13930 : */
13931 2 : INT db_unwatch_all()
13932 : {
13933 4 : for (int i = _watch_list_entries-1; i >= 0 ; i--) {
13934 2 : if ((_watch_list[i].hDB == 0) && (_watch_list[i].handle == 0)) {
13935 : // empty or deleted watch list entry
13936 0 : continue;
13937 : }
13938 2 : db_unwatch(_watch_list[i].hDB, _watch_list[i].handle);
13939 : }
13940 :
13941 2 : return DB_SUCCESS;
13942 : }
13943 :
13944 : /*------------------------------------------------------------------*/
13945 :
13946 : /* C++ wrapper for db_get_value */
13947 :
13948 51 : 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)
13949 : {
13950 : int status;
13951 : int hkey;
13952 :
13953 : //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);
13954 :
13955 51 : if (index > 0 && create) {
13956 0 : cm_msg(MERROR, "db_get_value_string", "cannot resize odb string arrays, please use db_resize_string() instead");
13957 0 : return DB_OUT_OF_RANGE;
13958 : }
13959 :
13960 51 : status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
13961 51 : if (status == DB_SUCCESS) {
13962 : KEY key;
13963 47 : status = db_get_key(hdb, hkey, &key);
13964 47 : if (status != DB_SUCCESS)
13965 0 : return status;
13966 47 : if (index < 0 || index >= key.num_values)
13967 0 : return DB_OUT_OF_RANGE;
13968 47 : int size = key.item_size;
13969 47 : if (size == 0) {
13970 0 : if (s)
13971 0 : *s = "";
13972 : //printf("db_get_value_string: return empty string, item_size %d\n", key.item_size);
13973 0 : return DB_SUCCESS;
13974 : }
13975 47 : char* buf = (char*)malloc(size);
13976 47 : assert(buf != NULL);
13977 47 : status = db_get_data_index(hdb, hkey, buf, &size, index, TID_STRING);
13978 47 : if (status != DB_SUCCESS) {
13979 0 : free(buf);
13980 0 : return status;
13981 : }
13982 47 : if (s)
13983 47 : *s = buf;
13984 47 : free(buf);
13985 : //printf("db_get_value_string: return [%s], len %d, item_size %d, size %d\n", s->c_str(), s->length(), key.item_size, size);
13986 47 : return DB_SUCCESS;
13987 4 : } else if (!create) {
13988 : // does not exist and not asked to create it
13989 0 : return status;
13990 : } else {
13991 : //printf("db_get_value_string: creating [%s]\n", key_name);
13992 4 : status = db_create_key(hdb, hKeyRoot, key_name, TID_STRING);
13993 4 : if (status != DB_SUCCESS)
13994 0 : return status;
13995 4 : status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
13996 4 : if (status != DB_SUCCESS)
13997 0 : return status;
13998 4 : assert(s != NULL);
13999 4 : if (create_string_length == 0) {
14000 4 : int size = s->length() + 1; // 1 byte for final \0
14001 4 : status = db_set_data_index(hdb, hkey, s->c_str(), size, index, TID_STRING);
14002 : } else {
14003 0 : char* buf = (char*)malloc(create_string_length);
14004 0 : assert(buf);
14005 0 : mstrlcpy(buf, s->c_str(), create_string_length);
14006 0 : status = db_set_data_index(hdb, hkey, buf, create_string_length, index, TID_STRING);
14007 0 : free(buf);
14008 : }
14009 4 : if (status != DB_SUCCESS)
14010 0 : return status;
14011 : //printf("db_get_value_string: created with value [%s]\n", s->c_str());
14012 4 : return DB_SUCCESS;
14013 : }
14014 : // NOT REACHED
14015 : }
14016 :
14017 : /* C++ wrapper for db_set_value */
14018 :
14019 1 : INT EXPRT db_set_value_string(HNDLE hDB, HNDLE hKeyRoot, const char *key_name, const std::string* s)
14020 : {
14021 1 : assert(s != NULL);
14022 1 : int size = s->length() + 1; // 1 byte for final \0
14023 : //printf("db_set_value_string: key_name [%s], string [%s], size %d\n", key_name, s->c_str(), size);
14024 1 : return db_set_value(hDB, hKeyRoot, key_name, s->c_str(), size, 1, TID_STRING);
14025 : }
14026 :
14027 : /********************************************************************/
14028 : /**
14029 : Change size of string arrays.
14030 :
14031 : This function can change the number of elements and the string element length of an array of strings.
14032 : @param hDB ODB handle obtained via cm_get_experiment_database().
14033 : @param hKey Handle for key where search starts, zero for root.
14034 : @param key_name Odb key name, if NULL, will resize ODB entry pointed to by hKey
14035 : @param num_values New number of array elements, if 0, remains unchanged
14036 : @param max_string_length New max string length for array elements, if 0, remains unchanged
14037 : @return DB_SUCCESS, or error from db_find_key, db_create_key, db_get_data(), db_set_data()
14038 : */
14039 0 : INT EXPRT db_resize_string(HNDLE hdb, HNDLE hKeyRoot, const char *key_name, int num_values, int max_string_length)
14040 : {
14041 : int status;
14042 : int hkey;
14043 :
14044 : //printf("db_resize_string: key_name [%s], num_values %d, max_string_length %d\n", key_name, num_values, max_string_length);
14045 :
14046 0 : int old_num_values = 0;
14047 0 : int old_item_size = 0;
14048 0 : int old_size = 0;
14049 0 : char* old_data = NULL;
14050 :
14051 0 : if (key_name) {
14052 0 : status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
14053 : } else {
14054 0 : hkey = hKeyRoot;
14055 0 : status = DB_SUCCESS;
14056 : }
14057 0 : if (status == DB_SUCCESS) {
14058 : KEY key;
14059 0 : status = db_get_key(hdb, hkey, &key);
14060 0 : if (status != DB_SUCCESS)
14061 0 : return status;
14062 0 : old_num_values = key.num_values;
14063 0 : old_item_size = key.item_size;
14064 0 : old_size = old_num_values * old_item_size;
14065 0 : if (old_size > 0) {
14066 0 : old_data = (char*)malloc(old_size);
14067 0 : assert(old_data != NULL);
14068 0 : int size = old_size;
14069 0 : status = db_get_data(hdb, hkey, old_data, &size, TID_STRING);
14070 0 : if (status != DB_SUCCESS) {
14071 0 : free(old_data);
14072 0 : return status;
14073 : }
14074 0 : assert(size == old_size);
14075 : }
14076 : } else {
14077 0 : status = db_create_key(hdb, hKeyRoot, key_name, TID_STRING);
14078 0 : if (status != DB_SUCCESS)
14079 0 : return status;
14080 0 : status = db_find_key(hdb, hKeyRoot, key_name, &hkey);
14081 0 : if (status != DB_SUCCESS)
14082 0 : return status;
14083 : }
14084 :
14085 : //printf("old_num_values %d, old_item_size %d, old_size %d\n", old_num_values, old_item_size, old_size);
14086 :
14087 0 : int item_size = max_string_length;
14088 :
14089 0 : if (item_size < 1)
14090 0 : item_size = old_item_size;
14091 :
14092 0 : if (num_values < 1)
14093 0 : num_values = old_num_values;
14094 :
14095 0 : int new_size = num_values * item_size;
14096 0 : char* new_data = (char*)malloc(new_size);
14097 0 : assert(new_data);
14098 :
14099 0 : memset(new_data, 0, new_size);
14100 :
14101 0 : if (old_data) {
14102 0 : int num = old_num_values;
14103 0 : if (num > num_values)
14104 0 : num = num_values;
14105 :
14106 : //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);
14107 :
14108 0 : for (int i=0; i<num; i++) {
14109 0 : const char* old_ptr = old_data + i*old_item_size;
14110 0 : char* new_ptr = new_data + i*item_size;
14111 0 : mstrlcpy(new_ptr, old_ptr, item_size);
14112 : }
14113 : }
14114 :
14115 0 : status = db_set_data(hdb, hkey, new_data, new_size, num_values, TID_STRING);
14116 :
14117 0 : if (old_data)
14118 0 : free(old_data);
14119 0 : if (new_data)
14120 0 : free(new_data);
14121 :
14122 0 : return status;
14123 : }
14124 :
14125 0 : MJsonNode* db_scl(HNDLE hDB)
14126 : {
14127 : #ifdef LOCAL_ROUTINES
14128 0 : MJsonNode* scl = MJsonNode::MakeObject();
14129 0 : MJsonNode* clients = MJsonNode::MakeArray();
14130 :
14131 0 : scl->AddToObject("now", MJsonNode::MakeNumber(ss_time_sec()));
14132 0 : scl->AddToObject("clients", clients);
14133 :
14134 : /* lock database */
14135 0 : db_lock_database(hDB);
14136 :
14137 0 : DATABASE *pdb = &_database[hDB - 1];
14138 0 : DATABASE_HEADER *pheader = pdb->database_header;
14139 :
14140 0 : DWORD now = ss_millitime();
14141 :
14142 : /* list clients */
14143 0 : for (int i = 0; i < pheader->max_client_index; i++) {
14144 0 : DATABASE_CLIENT *pclient = &pheader->client[i];
14145 0 : if (pclient->pid) {
14146 0 : MJsonNode* c = MJsonNode::MakeObject();
14147 0 : c->AddToObject("slot", MJsonNode::MakeNumber(i));
14148 0 : c->AddToObject("pid", MJsonNode::MakeNumber(pclient->pid));
14149 0 : c->AddToObject("name", MJsonNode::MakeString(pclient->name));
14150 0 : std::string path = msprintf("/System/Clients/%d/Host", pclient->pid);
14151 0 : const KEY* pkey = db_find_pkey_locked(pheader, NULL, path.c_str(), NULL, NULL);
14152 0 : if (pkey) {
14153 0 : int host_size = pkey->total_size;
14154 0 : char* host = (char*)malloc(host_size);
14155 0 : assert(host != NULL);
14156 0 : db_get_data_locked(pheader, pkey, 0, host, &host_size, TID_STRING, NULL);
14157 0 : c->AddToObject("host", MJsonNode::MakeString(host));
14158 0 : free(host);
14159 : }
14160 0 : c->AddToObject("watchdog_timeout_millisec", MJsonNode::MakeNumber(pclient->watchdog_timeout));
14161 : // "now" and "last_activity" is millisecond time wrapped around at 32 bits, must do unsigned 32-bit math here, not in javascript
14162 0 : c->AddToObject("last_activity_millisec", MJsonNode::MakeNumber(now - pclient->last_activity));
14163 0 : clients->AddToArray(c);
14164 0 : }
14165 : }
14166 :
14167 0 : db_unlock_database(hDB);
14168 :
14169 0 : return scl;
14170 : #else
14171 : return MJsonNode::MakeNull();
14172 : #endif
14173 : }
14174 :
14175 0 : MJsonNode* db_sor(HNDLE hDB, const char* root_path)
14176 : {
14177 : //printf("db_sor(%s)\n", root_path);
14178 0 : assert(root_path != NULL);
14179 : #ifdef LOCAL_ROUTINES
14180 0 : size_t root_path_len = strlen(root_path);
14181 0 : MJsonNode* sor = MJsonNode::MakeArray();
14182 :
14183 : /* lock database */
14184 0 : db_lock_database(hDB);
14185 :
14186 0 : DATABASE *pdb = &_database[hDB - 1];
14187 0 : DATABASE_HEADER *pheader = pdb->database_header;
14188 :
14189 : /* list clients */
14190 0 : for (int i = 0; i < pheader->max_client_index; i++) {
14191 0 : DATABASE_CLIENT *pclient = &pheader->client[i];
14192 0 : if (pclient->pid) {
14193 0 : for (int j = 0; j < pclient->max_index; j++) {
14194 0 : std::string path = db_get_path_locked(pheader, pclient->open_record[j].handle);
14195 0 : if (path.length() < root_path_len)
14196 0 : continue;
14197 0 : if (strncmp(root_path, path.c_str(), root_path_len) != 0)
14198 0 : continue;
14199 0 : MJsonNode* c = MJsonNode::MakeObject();
14200 0 : c->AddToObject("name", MJsonNode::MakeString(pclient->name));
14201 : //c->AddToObject("handle", MJsonNode::MakeNumber(pclient->open_record[j].handle));
14202 0 : c->AddToObject("access_mode", MJsonNode::MakeNumber(pclient->open_record[j].access_mode));
14203 0 : c->AddToObject("flags", MJsonNode::MakeNumber(pclient->open_record[j].flags));
14204 0 : c->AddToObject("path", MJsonNode::MakeString(path.c_str()));
14205 0 : sor->AddToArray(c);
14206 0 : }
14207 : }
14208 : }
14209 :
14210 0 : db_unlock_database(hDB);
14211 :
14212 0 : return sor;
14213 : #else
14214 : return MJsonNode::MakeNull();
14215 : #endif
14216 : }
14217 :
14218 : /** @} *//* end of odbfunctionc */
14219 :
14220 : /* emacs
14221 : * Local Variables:
14222 : * tab-width: 8
14223 : * c-basic-offset: 3
14224 : * indent-tabs-mode: nil
14225 : * End:
14226 : */
|