source: tags/arb-6.0-rc1/ARBDB/adtools.cxx

Last change on this file was 11874, checked in by westram, 10 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.2 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adtools.cxx                                       //
4//   Purpose   : misc functions                                    //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "gb_data.h"
12#include "gb_main.h"
13
14#include <arb_sort.h>
15#include <arb_file.h>
16#include <arb_sleep.h>
17#include <arb_str.h>
18#include <arb_strarray.h>
19#include <ad_cb.h>
20
21#include <algorithm>
22#include "ad_remote.h"
23
24using namespace std;
25
26GBDATA *GBT_create(GBDATA *father, const char *key, long delete_level) {
27    GBDATA *gbd = GB_create_container(father, key);
28    if (gbd) {
29        GB_ERROR error = GB_write_security_delete(gbd, delete_level);
30        if (error) {
31            GB_export_error(error);
32            gbd = NULL; // assuming caller will abort the transaction
33        }
34    }
35    return gbd;
36}
37
38GBDATA *GBT_find_or_create(GBDATA *father, const char *key, long delete_level) {
39    GBDATA *gbd = GB_entry(father, key);
40    if (!gbd) gbd = GBT_create(father, key, delete_level);
41    return gbd;
42}
43
44
45/* the following functions were meant to use user defined values.
46 *
47 * Especially for 'ECOLI' there is already a possibility to
48 * specify a different reference in edit4, but there's no
49 * data model in the DB for it. Consider whether it makes sense,
50 * if secedit uses it as well.
51 *
52 * Note: Don't change the result type to 'const char *' even if the functions always
53 *       return the same atm. That may change.
54 */
55
56char *GBT_get_default_helix   (GBDATA *) { return strdup("HELIX"); }
57char *GBT_get_default_helix_nr(GBDATA *) { return strdup("HELIX_NR"); }
58char *GBT_get_default_ref     (GBDATA *) { return strdup("ECOLI"); }
59
60
61// ----------------
62//      scan DB
63
64#define GBT_SUM_LEN 4096                            // maximum length of path
65
66struct GB_DbScanner : virtual Noncopyable {
67    GB_HASH   *hash_table;
68    StrArray&  result; // not owned!
69    char      *buffer;
70
71    GB_DbScanner(StrArray& result_)
72        : result(result_)
73    {
74        hash_table = GBS_create_hash(1024, GB_MIND_CASE);
75        buffer     = (char*)malloc(GBT_SUM_LEN);
76        buffer[0]  = 0;
77    }
78
79    ~GB_DbScanner() {
80        GBS_free_hash(hash_table);
81        free(buffer);
82    }
83};
84
85static void gbt_scan_db_rek(GBDATA *gbd, char *prefix, int deep, GB_DbScanner *scanner) {
86    GB_TYPES type = GB_read_type(gbd);
87    if (type == GB_DB) {
88        int len_of_prefix = strlen(prefix);
89        for (GBDATA *gb2 = GB_child(gbd); gb2; gb2 = GB_nextChild(gb2)) {  // find everything
90            if (deep) {
91                const char *key = GB_read_key_pntr(gb2);
92                if (key[0] != '@') { // skip internal containers
93                    sprintf(&prefix[len_of_prefix], "/%s", key);
94                    gbt_scan_db_rek(gb2, prefix, 1, scanner);
95                }
96            }
97            else {
98                prefix[len_of_prefix] = 0;
99                gbt_scan_db_rek(gb2, prefix, 1, scanner);
100            }
101        }
102        prefix[len_of_prefix] = 0;
103    }
104    else {
105        gb_assert(!prefix[0] || GB_check_hkey(prefix) == NULL);
106        LocallyModify<char> firstCharWithType(prefix[0], char(type)); // first char always is '/' -> use to store type in hash
107        GBS_incr_hash(scanner->hash_table, prefix);
108    }
109}
110
111struct scan_db_insert {
112    GB_DbScanner *scanner;
113    const char   *datapath;
114};
115
116static long gbs_scan_db_insert(const char *key, long val, void *cd_insert_data) {
117    scan_db_insert *insert    = (scan_db_insert *)cd_insert_data;
118    char           *to_insert = 0;
119
120    if (!insert->datapath) {
121        to_insert = strdup(key);
122    }
123    else {
124        bool do_insert = ARB_strBeginsWith(key+1, insert->datapath);
125        gb_assert(implicated(!do_insert, !ARB_strBeginsWith(insert->datapath, key+1))); // oops - previously inserted also in this case. inspect!
126
127        if (do_insert) {                                         // datapath matches
128            to_insert    = strdup(key+strlen(insert->datapath)); // cut off prefix
129            to_insert[0] = key[0]; // copy type
130        }
131    }
132
133    if (to_insert) {
134        GB_DbScanner *scanner = insert->scanner;
135        scanner->result.put(to_insert);
136    }
137
138    return val;
139}
140
141static int gbs_scan_db_compare(const void *left, const void *right, void *) {
142    return strcmp((GB_CSTR)left+1, (GB_CSTR)right+1);
143}
144
145
146void GBT_scan_db(StrArray& fieldNames, GBDATA *gbd, const char *datapath) {
147    /*! scan CONTAINER for existing sub-keys
148     *
149     * recurses completely downwards the DB tree
150     *
151     * @param fieldNames gets filled with result strings
152     * - each string is the path to a node beyond gbd
153     * - every string exists only once
154     * - the first character of a string is the type of the entry
155     * - the strings are sorted alphabetically
156     * @param gbd node where search starts
157     * @param datapath if != NULL, only keys with prefix datapath are scanned and
158     * the prefix is removed from the resulting key_names.
159     *
160     * Note: this function is incredibly slow when called from clients
161     */
162
163    {
164        GB_DbScanner scanner(fieldNames);
165        gbt_scan_db_rek(gbd, scanner.buffer, 0, &scanner);
166        scan_db_insert insert = { &scanner, datapath, };
167        GBS_hash_do_loop(scanner.hash_table, gbs_scan_db_insert, &insert);
168    }
169    fieldNames.sort(gbs_scan_db_compare, 0);
170}
171
172// --------------------------
173//      message injection
174
175static void new_gbt_message_created_cb(GBDATA *gb_pending_messages) {
176    static int avoid_deadlock = 0;
177
178    if (!avoid_deadlock) {
179        GBDATA *gb_msg;
180
181        avoid_deadlock++;
182        GB_push_transaction(gb_pending_messages);
183
184        for (gb_msg = GB_entry(gb_pending_messages, "msg"); gb_msg;) {
185            {
186                const char *msg = GB_read_char_pntr(gb_msg);
187                GB_warning(msg);
188            }
189            {
190                GBDATA *gb_next_msg = GB_nextEntry(gb_msg);
191                GB_delete(gb_msg);
192                gb_msg              = gb_next_msg;
193            }
194        }
195
196        GB_pop_transaction(gb_pending_messages);
197        avoid_deadlock--;
198    }
199}
200
201void GBT_install_message_handler(GBDATA *gb_main) {
202    GBDATA *gb_pending_messages;
203
204    GB_push_transaction(gb_main);
205    gb_pending_messages = GB_search(gb_main, ERROR_CONTAINER_PATH, GB_CREATE_CONTAINER);
206    gb_assert(gb_pending_messages);
207    GB_add_callback(gb_pending_messages, GB_CB_SON_CREATED, makeDatabaseCallback(new_gbt_message_created_cb));
208    GB_pop_transaction(gb_main);
209
210#if defined(DEBUG) && 0
211    GBT_message(GB_get_root(gb_pending_messages), GBS_global_string("GBT_install_message_handler installed for gb_main=%p", gb_main));
212#endif // DEBUG
213}
214
215
216void GBT_message(GBDATA *gb_main, const char *msg) {
217    /*! When called in client(or server) this causes the DB server to show the message.
218     * Message is shown via GB_warning (which uses aw_message in GUIs)
219     *
220     * Note: The message is not shown before the transaction ends.
221     * If the transaction is aborted, the message is never shown!
222     *
223     * see also : GB_warning()
224     */
225
226    GB_ERROR error = GB_push_transaction(gb_main);
227
228    if (!error) {
229        GBDATA *gb_pending_messages = GB_search(gb_main, ERROR_CONTAINER_PATH, GB_CREATE_CONTAINER);
230        GBDATA *gb_msg              = gb_pending_messages ? GB_create(gb_pending_messages, "msg", GB_STRING) : 0;
231
232        if (!gb_msg) error = GB_await_error();
233        else {
234            gb_assert(msg);
235            error = GB_write_string(gb_msg, msg);
236        }
237    }
238    error = GB_end_transaction(gb_main, error);
239
240    if (error) {
241        fprintf(stderr, "GBT_message: Failed to write message '%s'\n(Reason: %s)\n", msg, error);
242    }
243}
244
245char *GBT_read_string(GBDATA *gb_container, const char *fieldpath) {
246    /*! Read value from database field (of type GB_STRING)
247     *
248     * @param gb_container where to start search for field
249     * @param fieldpath relative path from gb_container
250     *
251     * @return
252     * NULL in case of error (use GB_await_error()) or when field does not exist.
253     * otherwise returns a heap copy.
254     *
255     * other functions return a pointer to a temporary variable (invalidated by next call)
256     */
257
258    GBDATA *gbd;
259    char   *result = NULL;
260
261    GB_push_transaction(gb_container);
262    gbd = GB_search(gb_container, fieldpath, GB_FIND);
263    if (gbd) result = GB_read_string(gbd);
264    GB_pop_transaction(gb_container);
265    return result;
266}
267
268char *GBT_read_as_string(GBDATA *gb_container, const char *fieldpath) {
269    /*! like GBT_read_string()
270     *
271     * but
272     * - field may be of any type (result gets converted to reasonable char*)
273     */
274
275    GBDATA *gbd;
276    char   *result = NULL;
277
278    GB_push_transaction(gb_container);
279    gbd = GB_search(gb_container, fieldpath, GB_FIND);
280    if (gbd) result = GB_read_as_string(gbd);
281    GB_pop_transaction(gb_container);
282    return result;
283}
284
285const char *GBT_read_char_pntr(GBDATA *gb_container, const char *fieldpath) {
286    /*! like GBT_read_string()
287     *
288     * but
289     * - result is not a heap-copy and may be invalidated by further DB access
290     *   (especially by overwriting that fields)
291     *
292     * Note: Under no circumstances you may modify the result!
293     */
294
295    GBDATA     *gbd;
296    const char *result = NULL;
297
298    GB_push_transaction(gb_container);
299    gbd = GB_search(gb_container, fieldpath, GB_FIND);
300    if (gbd) result = GB_read_char_pntr(gbd);
301    GB_pop_transaction(gb_container);
302    return result;
303}
304
305NOT4PERL long *GBT_read_int(GBDATA *gb_container, const char *fieldpath) {
306    /*! similar to GBT_read_string()
307     *
308     * but
309     * - for fields of type GB_INT
310     * - result gets invalidated by next call
311     */
312
313    GBDATA *gbd;
314    long   *result = NULL;
315
316    GB_push_transaction(gb_container);
317    gbd = GB_search(gb_container, fieldpath, GB_FIND);
318    if (gbd) {
319        static long result_var;
320        result_var = GB_read_int(gbd);
321        result     = &result_var;
322    }
323    GB_pop_transaction(gb_container);
324    return result;
325}
326
327NOT4PERL double *GBT_read_float(GBDATA *gb_container, const char *fieldpath) {
328    /*! similar to GBT_read_string()
329     *
330     * but
331     * - for fields of type GB_FLOAT
332     * - result gets invalidated by next call
333     */
334
335    GBDATA *gbd;
336    double *result = NULL;
337
338    GB_push_transaction(gb_container);
339    gbd = GB_search(gb_container, fieldpath, GB_FIND);
340    if (gbd) {
341        static double result_var;
342        result_var = GB_read_float(gbd);
343        result     = &result_var;
344    }
345    GB_pop_transaction(gb_container);
346    return result;
347}
348
349char *GBT_readOrCreate_string(GBDATA *gb_container, const char *fieldpath, const char *default_value) {
350    /*! like GBT_read_string(),
351     *
352     * but if field does not exist, it will be created and initialized with 'default_value'
353     */
354    GBDATA *gb_string;
355    char   *result = NULL;
356
357    GB_push_transaction(gb_container);
358    gb_string             = GB_searchOrCreate_string(gb_container, fieldpath, default_value);
359    if (gb_string) result = GB_read_string(gb_string);
360    GB_pop_transaction(gb_container);
361    return result;
362}
363
364const char *GBT_readOrCreate_char_pntr(GBDATA *gb_container, const char *fieldpath, const char *default_value) {
365    /*! like GBT_read_char_pntr(),
366     *
367     * but if field does not exist, it will be created and initialized with 'default_value'
368     */
369
370    GBDATA     *gb_string;
371    const char *result = NULL;
372
373    GB_push_transaction(gb_container);
374    gb_string             = GB_searchOrCreate_string(gb_container, fieldpath, default_value);
375    if (gb_string) result = GB_read_char_pntr(gb_string);
376    GB_pop_transaction(gb_container);
377    return result;
378}
379
380NOT4PERL long *GBT_readOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value) {
381    /*! like GBT_read_int(),
382     *
383     * but if field does not exist, it will be created and initialized with 'default_value'
384     */
385
386    GBDATA *gb_int;
387    long   *result = NULL;
388
389    GB_push_transaction(gb_container);
390    gb_int = GB_searchOrCreate_int(gb_container, fieldpath, default_value);
391    if (gb_int) {
392        static long result_var;
393        result_var = GB_read_int(gb_int);
394        result     = &result_var;
395    }
396    GB_pop_transaction(gb_container);
397    return result;
398}
399
400NOT4PERL double *GBT_readOrCreate_float(GBDATA *gb_container, const char *fieldpath, double default_value) {
401    /*! like GBT_read_float(),
402     *
403     * but if field does not exist, it will be created and initialized with 'default_value'
404     */
405
406    gb_assert(default_value == default_value); // !nan
407   
408    GBDATA *gb_float;
409    double *result = NULL;
410
411    GB_push_transaction(gb_container);
412    gb_float = GB_searchOrCreate_float(gb_container, fieldpath, default_value);
413    if (gb_float) {
414        static double result_var;
415        result_var = GB_read_float(gb_float);
416        result     = &result_var;
417    }
418    else {
419        gb_assert(0);
420    }
421    GB_pop_transaction(gb_container);
422    return result;
423}
424
425// -------------------------------------------------------------------
426//      overwrite existing or create new database field
427//      (field must not exist twice or more - it has to be unique!!)
428
429GB_ERROR GBT_write_string(GBDATA *gb_container, const char *fieldpath, const char *content) {
430    /*! Write content to database field of type GB_STRING
431     *
432     * if field exists, it will be overwritten.
433     * if field does not exist, it will be created.
434     *
435     * The field should be unique, i.e. it should not exist twice or more in it's parent container
436     *
437     * @return GB_ERROR on failure
438     */
439    GB_ERROR  error = GB_push_transaction(gb_container); // @@@ result unused
440    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_STRING);
441    if (!gbd) error = GB_await_error();
442    else {
443        error = GB_write_string(gbd, content);
444        gb_assert(GB_nextEntry(gbd) == 0); // only one entry should exist (sure you want to use this function?)
445    }
446    return GB_end_transaction(gb_container, error);
447}
448
449GB_ERROR GBT_write_int(GBDATA *gb_container, const char *fieldpath, long content) {
450    /*! like GBT_write_string(),
451     *
452     * but for fields of type GB_INT
453     */
454    GB_ERROR  error = GB_push_transaction(gb_container); // @@@ result unused
455    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_INT);
456    if (!gbd) error = GB_await_error();
457    else {
458        error = GB_write_int(gbd, content);
459        gb_assert(GB_nextEntry(gbd) == 0); // only one entry should exist (sure you want to use this function?)
460    }
461    return GB_end_transaction(gb_container, error);
462}
463
464GB_ERROR GBT_write_byte(GBDATA *gb_container, const char *fieldpath, unsigned char content) {
465    /*! like GBT_write_string(),
466     *
467     * but for fields of type GB_BYTE
468     */
469    GB_ERROR  error = GB_push_transaction(gb_container); // @@@ result unused
470    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_BYTE);
471    if (!gbd) error = GB_await_error();
472    else {
473        error = GB_write_byte(gbd, content);
474        gb_assert(GB_nextEntry(gbd) == 0); // only one entry should exist (sure you want to use this function?)
475    }
476    return GB_end_transaction(gb_container, error);
477}
478
479
480GB_ERROR GBT_write_float(GBDATA *gb_container, const char *fieldpath, double content) {
481    /*! like GBT_write_string(),
482     *
483     * but for fields of type GB_FLOAT
484     */
485
486    gb_assert(content == content); // !nan
487
488    GB_ERROR  error = GB_push_transaction(gb_container); // @@@ result unused
489    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_FLOAT);
490    if (!gbd) error = GB_await_error();
491    else {
492        error = GB_write_float(gbd, content);
493        gb_assert(GB_nextEntry(gbd) == 0); // only one entry should exist (sure you want to use this function?)
494    }
495    return GB_end_transaction(gb_container, error);
496}
497
498
499static GBDATA *GB_test_link_follower(GBDATA *gb_main, GBDATA */*gb_link*/, const char *link) {
500    GBDATA *linktarget = GB_search(gb_main, "tmp/link/string", GB_STRING);
501    GB_write_string(linktarget, GBS_global_string("Link is '%s'", link));
502    return GB_get_father(linktarget);
503}
504
505// --------------------
506//      save & load
507
508GBDATA *GBT_open(const char *path, const char *opent) {
509    /*! Open a database,
510     *  create an index for species and extended names (server only!) and
511     *  disable saving in the PT_SERVER directory.
512     *
513     * @param path filename of the DB
514     * @param opent see GB_login()
515     * @return database handle (or NULL in which case an error is exported)
516     * @see GB_open()
517     */
518
519    GBDATA *gbd = GB_open(path, opent);
520    if (gbd) {
521        GB_disable_path(gbd, GB_path_in_ARBLIB("pts/*"));
522        GB_ERROR error = NULL;
523        {
524            GB_transaction ta(gbd);
525
526            if (!strchr(path, ':')) {
527                GBDATA *species_data = GB_search(gbd, "species_data", GB_FIND); // do NOT create species_data here!
528                if (species_data) {
529                    long hash_size = max(GB_number_of_subentries(species_data), GBT_SPECIES_INDEX_SIZE);
530                    error          = GB_create_index(species_data, "name", GB_IGNORE_CASE, hash_size);
531
532                    if (!error) {
533                        GBDATA *extended_data = GBT_get_SAI_data(gbd);
534                        hash_size             = max(GB_number_of_subentries(extended_data), GBT_SAI_INDEX_SIZE);
535                        error                 = GB_create_index(extended_data, "name", GB_IGNORE_CASE, hash_size);
536                    }
537                }
538            }
539            if (!error) {
540                GBDATA *gb_tmp = GB_search(gbd, "tmp", GB_CREATE_CONTAINER);
541                if (gb_tmp) error = GB_set_temporary(gb_tmp);
542            }
543
544            if (!error) {
545                {
546                    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
547                    Main->table_hash = GBS_create_hash(256, GB_MIND_CASE);
548                    GB_install_link_follower(gbd, "REF", GB_test_link_follower);
549                }
550                GBT_install_table_link_follower(gbd);
551            }
552        }
553        if (error) {
554            GB_close(gbd);
555            gbd = NULL;
556            GB_export_error(error);
557        }
558    }
559    gb_assert(contradicted(gbd, GB_have_error()));
560    return gbd;
561}
562
563/* --------------------------------------------------------------------------------
564 * Remote commands
565 *
566 * Note: These commands may seem to be unused from inside ARB.
567 * They get used, but only indirectly via the macro-function.
568 *
569 * Search for
570 * - BIO::remote_action         (use of GBT_remote_action)
571 * - BIO::remote_awar           (use of GBT_remote_awar)
572 * - BIO::remote_read_awar      (use of GBT_remote_read_awar)
573 */
574
575static GBDATA *wait_for_dbentry(GBDATA *gb_main, const char *entry) {
576    MacroTalkSleep increasing;
577    GBDATA *gbd;
578    while (1) {
579        GB_begin_transaction(gb_main);
580        gbd = GB_search(gb_main, entry, GB_FIND);
581        GB_commit_transaction(gb_main);
582        if (gbd) break;
583        increasing.sleep();
584    }
585    return gbd;
586}
587
588static GBDATA *wait_for_dbentry_verboose(GBDATA *gb_main, const char *entry) {
589    GB_warningf("[waiting for DBENTRY '%s']", entry); // only prints onto console (ARB_NT is blocked as long as start_remote_command_for_application blocks)
590    GBDATA *gbd = wait_for_dbentry(gb_main, entry);
591    GB_warningf("[found DBENTRY '%s']", entry);
592    return gbd;
593}
594
595static GB_ERROR gbt_wait_for_remote_action(GBDATA *gb_main, GBDATA *gb_action, const char *awar_read) {
596    // waits until remote action has finished
597
598    MacroTalkSleep increasing;
599    GB_ERROR error = 0;
600    while (!error) {
601        increasing.sleep();
602        error = GB_begin_transaction(gb_main);
603        if (!error) {
604            char *ac = GB_read_string(gb_action);
605            if (ac[0] == 0) { // action has been cleared from remote side
606                GBDATA *gb_result = GB_search(gb_main, awar_read, GB_STRING);
607                error             = GB_read_char_pntr(gb_result); // check for errors
608            }
609            free(ac);
610        }
611        error = GB_end_transaction(gb_main, error);
612    }
613
614    return error; // may be error or result
615}
616
617static void mark_as_macro_executor(GBDATA *gb_main, bool mark) {
618    // call this with 'mark' = true to mark yourself as "client running a macro"
619    // -> GB_close will automatically announce termination of this client to main DB (MACRO_TRIGGER_TERMINATED)
620    // do NOT call with 'mark' = false
621
622    static bool client_is_macro_executor = false;
623    if (mark) {
624        if (!client_is_macro_executor) {
625            GB_atclose(gb_main, (void (*)(GBDATA*, void*))mark_as_macro_executor, 0);
626            client_is_macro_executor = true;
627        }
628    }
629    else {
630        // called via GB_atclose callback (i.e. at end of currently executed macro file)
631        GB_ERROR error = NULL;
632        gb_assert(client_is_macro_executor);
633        if (client_is_macro_executor) {
634            GB_transaction ta(gb_main);
635
636            GBDATA *gb_terminated = GB_search(gb_main, MACRO_TRIGGER_TERMINATED, GB_FIND);
637            gb_assert(gb_terminated); // should have been created by macro caller
638            if (gb_terminated) {
639                error = GB_write_int(gb_terminated, GB_read_int(gb_terminated)+1); // notify macro caller
640            }
641
642            error = ta.close(error);
643        }
644        if (error) GBT_message(gb_main, error);
645    }
646}
647
648inline GB_ERROR set_intEntry_to(GBDATA *gb_main, const char *path, int value) {
649    GBDATA *gbd = GB_searchOrCreate_int(gb_main, path, value);
650    return gbd ? GB_write_int(gbd, value) : GB_await_error();
651}
652
653#if defined(DEBUG)
654// # define DUMP_AUTH_HANDSHAKE // see also ../SL/MACROS/dbserver.cxx@DUMP_AUTHORIZATION
655#endif
656
657#if defined(DUMP_AUTH_HANDSHAKE)
658# define IF_DUMP_HANDSHAKE(cmd) cmd
659#else
660# define IF_DUMP_HANDSHAKE(cmd)
661#endif
662
663GB_ERROR GB_set_macro_error(GBDATA *gb_main, const char *curr_error) {
664    GB_ERROR        error          = NULL;
665    GB_transaction  ta(gb_main);
666    GBDATA         *gb_macro_error = GB_searchOrCreate_string(gb_main, MACRO_TRIGGER_ERROR, curr_error);
667    if (gb_macro_error) {
668        const char *prev_error = GB_read_char_pntr(gb_macro_error);
669        if (prev_error && prev_error[0]) { // already have an error
670            if (strstr(prev_error, curr_error) == 0) { // do not add message twice
671                error = GB_write_string(gb_macro_error, GBS_global_string("%s\n%s", prev_error, curr_error));
672            }
673        }
674        else {
675            error = GB_write_string(gb_macro_error, curr_error);
676        }
677    }
678    return error;
679}
680GB_ERROR GB_get_macro_error(GBDATA *gb_main) {
681    GB_ERROR error = NULL;
682
683    GB_transaction  ta(gb_main);
684    GBDATA         *gb_macro_error = GB_search(gb_main, MACRO_TRIGGER_ERROR, GB_FIND);
685    if (gb_macro_error) {
686        const char *macro_error       = GB_read_char_pntr(gb_macro_error);
687        if (!macro_error) macro_error = GBS_global_string("failed to retrieve error message (Reason: %s)", GB_await_error());
688        if (macro_error[0]) error     = GBS_global_string("macro-error: %s", macro_error);
689    }
690    return error;
691}
692GB_ERROR GB_clear_macro_error(GBDATA *gb_main) {
693    GB_transaction  ta(gb_main);
694    GB_ERROR        error          = NULL;
695    GBDATA         *gb_macro_error = GB_search(gb_main, MACRO_TRIGGER_ERROR, GB_FIND);
696    if (gb_macro_error) error      = GB_write_string(gb_macro_error, "");
697    return error;
698}
699
700static GB_ERROR start_remote_command_for_application(GBDATA *gb_main, const remote_awars& remote) {
701    // Called before any remote command will be written to DB.
702    // Application specific initialization is done here.
703
704    mark_as_macro_executor(gb_main, true);
705
706    bool wait_for_app = false;
707
708    GB_ERROR error    = GB_begin_transaction(gb_main);
709    if (!error) error = GB_get_macro_error(gb_main);
710    if (!error) {
711        GBDATA *gb_granted     = GB_searchOrCreate_int(gb_main, remote.granted(), 0);
712        if (!gb_granted) error = GB_await_error();
713        else {
714            if (GB_read_int(gb_granted)) {
715                // ok - authorization already granted
716            }
717            else {
718                error        = set_intEntry_to(gb_main, remote.authReq(), 1); // ask client to ack execution
719                wait_for_app = !error;
720
721                if (!error) {
722                    IF_DUMP_HANDSHAKE(fprintf(stderr, "AUTH_HANDSHAKE [set %s to 1]\n", remote.authReq()));
723                }
724            }
725        }
726    }
727    error = GB_end_transaction(gb_main, error);
728
729    MacroTalkSleep increasing;
730    while (wait_for_app) {
731        gb_assert(!error);
732
733        IF_DUMP_HANDSHAKE(fprintf(stderr, "AUTH_HANDSHAKE [waiting for %s]\n", remote.authAck()));
734
735        GBDATA *gb_authAck = wait_for_dbentry_verboose(gb_main, remote.authAck());
736        if (gb_authAck) {
737            error = GB_begin_transaction(gb_main);
738            if (!error) {
739                long ack_pid = GB_read_int(gb_authAck);
740                if (ack_pid) {
741                    IF_DUMP_HANDSHAKE(fprintf(stderr, "AUTH_HANDSHAKE [got authAck %li]\n", ack_pid));
742                   
743                    GBDATA *gb_granted = GB_searchOrCreate_int(gb_main, remote.granted(), ack_pid);
744                    long    old_pid    = GB_read_int(gb_granted);
745
746                    if (old_pid != ack_pid) { // we have two applications with same id that acknowledged the execution request
747                        if (old_pid == 0) {
748                            error             = GB_write_int(gb_granted, ack_pid); // grant rights to execute remote-command to ack_pid
749                            if (!error) error = GB_write_int(gb_authAck, 0);       // allow a second application to acknowledge the request
750                            if (!error) {
751                                wait_for_app = false;
752                                IF_DUMP_HANDSHAKE(fprintf(stderr, "AUTH_HANDSHAKE [granted permission to execute macros to pid %li]\n", ack_pid));
753                            }
754                        }
755                    }
756                    else {
757                        error = GB_write_int(gb_authAck, 0); // allow a second application to acknowledge the request
758                    }
759                }
760                else {
761                    // old entry with value 0 -> wait until client acknowledges authReq
762                    IF_DUMP_HANDSHAKE(fprintf(stderr, "AUTH_HANDSHAKE [no %s yet]\n", remote.authAck()));
763                }
764            }
765            error = GB_end_transaction(gb_main, error);
766        }
767        else {
768            gb_assert(0); // happens when?
769        }
770
771        if (wait_for_app) {
772            increasing.sleep();
773        }
774    }
775
776    return error;
777}
778
779GB_ERROR GBT_remote_action(GBDATA *gb_main, const char *application, const char *action_name) {
780    // needs to be public (needed by perl-macros)
781
782    remote_awars remote(application);
783    GB_ERROR     error = start_remote_command_for_application(gb_main, remote);
784
785    if (!error) {
786        GBDATA *gb_action = wait_for_dbentry(gb_main, remote.action());
787        error             = GB_begin_transaction(gb_main);
788        if (!error) error = GB_write_string(gb_action, action_name); // write command
789        error             = GB_end_transaction(gb_main, error);
790        if (!error) error = gbt_wait_for_remote_action(gb_main, gb_action, remote.result());
791    }
792    return error;
793}
794
795GB_ERROR GBT_remote_awar(GBDATA *gb_main, const char *application, const char *awar_name, const char *value) {
796    // needs to be public (needed by perl-macros)
797
798    remote_awars remote(application);
799    GB_ERROR     error = start_remote_command_for_application(gb_main, remote);
800
801    if (!error) {
802        GBDATA *gb_awar   = wait_for_dbentry(gb_main, remote.awar());
803        error             = GB_begin_transaction(gb_main);
804        if (!error) error = GB_write_string(gb_awar, awar_name);
805        if (!error) error = GBT_write_string(gb_main, remote.value(), value);
806        error             = GB_end_transaction(gb_main, error);
807        if (!error) error = gbt_wait_for_remote_action(gb_main, gb_awar, remote.result());
808    }
809    return error;
810}
811
812GB_ERROR GBT_remote_read_awar(GBDATA *gb_main, const char *application, const char *awar_name) {
813    // needs to be public (needed by perl-macros)
814
815    remote_awars remote(application);
816    GB_ERROR     error = start_remote_command_for_application(gb_main, remote);
817
818    if (!error) {
819        GBDATA *gb_awar   = wait_for_dbentry(gb_main, remote.awar());
820        error             = GB_begin_transaction(gb_main);
821        if (!error) error = GB_write_string(gb_awar, awar_name);
822        if (!error) error = GBT_write_string(gb_main, remote.action(), "AWAR_REMOTE_READ");
823        error             = GB_end_transaction(gb_main, error);
824        if (!error) error = gbt_wait_for_remote_action(gb_main, gb_awar, remote.value());
825    }
826    return error;
827}
828
829inline char *find_macro_in(const char *dir, const char *macroname) {
830    char *full = GBS_global_string_copy("%s/%s", dir, macroname);
831    if (!GB_is_readablefile(full)) {
832        freeset(full, GBS_global_string_copy("%s.amc", full));
833        if (!GB_is_readablefile(full)) freenull(full);
834    }
835    return full;
836}
837
838static char *fullMacroname(const char *macro_name) {
839    /*! detect full path of 'macro_name'
840     * @param macro_name full path or path relative to ARBMACRO or ARBMACROHOME
841     * @return full path or NULL (in which case an error is exported)
842     */
843
844    gb_assert(!GB_have_error());
845
846    if (GB_is_readablefile(macro_name)) return strdup(macro_name);
847
848    char *in_ARBMACROHOME = find_macro_in(GB_getenvARBMACROHOME(), macro_name);
849    char *in_ARBMACRO     = find_macro_in(GB_getenvARBMACRO(),     macro_name);
850    char *result          = NULL;
851
852    if (in_ARBMACROHOME) {
853        if (in_ARBMACRO) {
854            GB_export_errorf("ambiguous macro name '%s'\n"
855                             "('%s' and\n"
856                             " '%s' exist both.\n"
857                             " You have to rename or delete one of them!)",
858                             macro_name, in_ARBMACROHOME, in_ARBMACRO);
859        }
860        else reassign(result, in_ARBMACROHOME);
861    }
862    else {
863        if (in_ARBMACRO) reassign(result, in_ARBMACRO);
864        else GB_export_errorf("Failed to detect macro '%s'", macro_name);
865    }
866
867    free(in_ARBMACRO);
868    free(in_ARBMACROHOME);
869
870    gb_assert(contradicted(result, GB_have_error()));
871
872    return result;
873}
874
875inline const char *relative_inside(const char *dir, const char *fullpath) {
876    if (ARB_strBeginsWith(fullpath, dir)) {
877        const char *result = fullpath+strlen(dir);
878        if (result[0] == '/') return result+1;
879    }
880    return NULL;
881}
882
883const char *GBT_relativeMacroname(const char *macro_name) {
884    /*! make macro_name relative if it is located in or below ARBMACROHOME or ARBMACRO.
885     * Inverse function of fullMacroname()
886     * @return pointer into macro_name (relative part) or macro_name itself (if macro is located somewhere else)
887     */
888
889    const char *result  = relative_inside(GB_getenvARBMACROHOME(), macro_name);
890    if (!result) result = relative_inside(GB_getenvARBMACRO(), macro_name);
891    if (!result) result = macro_name;
892    return result;
893}
894
895GB_ERROR GBT_macro_execute(const char *macro_name, bool loop_marked, bool run_async) {
896    /*! simply execute a macro
897     *
898     * In doubt: do not use this function, instead use ../SL/MACROS/macros.hxx@execute_macro
899     *
900     * @param macro_name contains the macro filename (either with full path or relative to directories pointed to by ARBMACRO or ARBMACROHOME)
901     * @param loop_marked if true -> call macro once for each marked species
902     * @param run_async if true -> run perl asynchronously (will not detect perl failure in that case!)
903     */
904
905    GB_ERROR  error     = NULL;
906    char     *fullMacro = fullMacroname(macro_name);
907    if (!fullMacro) {
908        error = GB_await_error();
909    }
910    else {
911        char *perl_args = NULL;
912        if (loop_marked) {
913            const char *with_all_marked = GB_path_in_ARBHOME("PERL_SCRIPTS/MACROS/with_all_marked.pl");
914            perl_args = GBS_global_string_copy("'%s' '%s'", with_all_marked, fullMacro);
915        }
916        else {
917            perl_args = GBS_global_string_copy("'%s'", fullMacro);
918        }
919
920        char *cmd = GBS_global_string_copy("perl %s %s", perl_args, run_async ? "&" : "");
921
922        error = GBK_system(cmd);
923
924        free(cmd);
925        free(perl_args);
926        free(fullMacro);
927    }
928    return error;
929}
930
931// ---------------------------
932//      self-notification
933// ---------------------------
934// provides a mechanism to notify ARB after some external tool finishes
935
936#define ARB_NOTIFICATIONS "tmp/notify"
937
938/* DB structure for notifications :
939 *
940 * ARB_NOTIFICATIONS/counter        GB_INT      counts used ids
941 * ARB_NOTIFICATIONS/notify/id      GB_INT      id of notification
942 * ARB_NOTIFICATIONS/notify/message GB_STRING   message of notification (set by callback)
943 */
944
945typedef void (*notify_cb_type)(const char *message, void *client_data);
946
947struct NotifyCb {
948    notify_cb_type  cb;
949    void           *client_data;
950};
951
952static void notify_cb(GBDATA *gb_message, NotifyCb *pending, GB_CB_TYPE cb_type) {
953    if (cb_type != GB_CB_DELETE) {
954        GB_remove_callback(gb_message, GB_CB_CHANGED_OR_DELETED, makeDatabaseCallback(notify_cb, pending));
955    }
956
957    if (cb_type == GB_CB_CHANGED) {
958        int         cb_done = 0;
959        GB_ERROR    error   = 0;
960        const char *message = GB_read_char_pntr(gb_message);
961        if (!message) error = GB_await_error();
962        else {
963            pending->cb(message, pending->client_data);
964            cb_done = 1;
965        }
966
967        if (!cb_done) {
968            gb_assert(error);
969            GB_warningf("Notification failed (Reason: %s)\n", error);
970            gb_assert(0);
971        }
972    }
973    else { // called from GB_remove_last_notification
974        gb_assert(cb_type == GB_CB_DELETE);
975    }
976
977    free(pending);
978}
979
980static int allocateNotificationID(GBDATA *gb_main, NotifyCb *pending) {
981    /* returns a unique notification ID
982     * or 0 (use GB_get_error() in this case)
983     */
984
985    int      id    = 0;
986    GB_ERROR error = GB_push_transaction(gb_main);
987
988    if (!error) {
989        GBDATA *gb_notify = GB_search(gb_main, ARB_NOTIFICATIONS, GB_CREATE_CONTAINER);
990
991        if (gb_notify) {
992            GBDATA *gb_counter = GB_searchOrCreate_int(gb_notify, "counter", 0);
993
994            if (gb_counter) {
995                int newid = GB_read_int(gb_counter) + 1; // increment counter
996                error     = GB_write_int(gb_counter, newid);
997
998                if (!error) {
999                    // change transaction (to never use id twice!)
1000                    error             = GB_pop_transaction(gb_main);
1001                    if (!error) error = GB_push_transaction(gb_main);
1002
1003                    if (!error) {
1004                        GBDATA *gb_notification = GB_create_container(gb_notify, "notify");
1005
1006                        if (gb_notification) {
1007                            error = GBT_write_int(gb_notification, "id", newid);
1008                            if (!error) {
1009                                GBDATA *gb_message = GB_searchOrCreate_string(gb_notification, "message", "");
1010
1011                                if (gb_message) {
1012                                    error = GB_add_callback(gb_message, GB_CB_CHANGED_OR_DELETED, makeDatabaseCallback(notify_cb, pending));
1013                                    if (!error) {
1014                                        id = newid; // success
1015                                    }
1016                                }
1017                            }
1018                        }
1019                    }
1020                }
1021            }
1022        }
1023    }
1024
1025    if (!id) {
1026        if (!error) error = GB_await_error();
1027        error = GBS_global_string("Failed to allocate notification ID (%s)", error);
1028    }
1029    error = GB_end_transaction(gb_main, error);
1030    if (error) GB_export_error(error);
1031
1032    return id;
1033}
1034
1035
1036char *GB_generate_notification(GBDATA *gb_main,
1037                               void (*cb)(const char *message, void *client_data),
1038                               const char *message, void *client_data)
1039{
1040    /* generates a call to 'arb_notify', meant to be inserted into some external system call.
1041     * When that call is executed, the callback instantiated here will be called.
1042     *
1043     * Tip : To return variable results from the shell script, use the name of an environment
1044     *       variable in 'message' (e.g. "$RESULT")
1045     */
1046
1047    int       id;
1048    char     *arb_notify_call = 0;
1049    NotifyCb *pending         = (NotifyCb*)malloc(sizeof(*pending));
1050
1051    pending->cb          = cb;
1052    pending->client_data = client_data;
1053
1054    id = allocateNotificationID(gb_main, pending);
1055    if (id) {
1056        arb_notify_call = GBS_global_string_copy("arb_notify %i \"%s\"", id, message);
1057    }
1058    else {
1059        free(pending);
1060    }
1061
1062    return arb_notify_call;
1063}
1064
1065GB_ERROR GB_remove_last_notification(GBDATA *gb_main) {
1066    // aborts the last notification
1067    GB_ERROR error = GB_push_transaction(gb_main);
1068
1069    if (!error) {
1070        GBDATA *gb_notify = GB_search(gb_main, ARB_NOTIFICATIONS, GB_CREATE_CONTAINER);
1071        if (gb_notify) {
1072            GBDATA *gb_counter = GB_entry(gb_notify, "counter");
1073            if (gb_counter) {
1074                int     id    = GB_read_int(gb_counter);
1075                GBDATA *gb_id = GB_find_int(gb_notify, "id", id, SEARCH_GRANDCHILD);
1076
1077                if (!gb_id) {
1078                    error = GBS_global_string("No notification for ID %i", id);
1079                    gb_assert(0);           // GB_generate_notification() has not been called for 'id'!
1080                }
1081                else {
1082                    GBDATA *gb_message = GB_brother(gb_id, "message");
1083
1084                    if (!gb_message) {
1085                        error = "Missing 'message' entry";
1086                    }
1087                    else {
1088                        error = GB_delete(gb_message); // calls notify_cb
1089                    }
1090                }
1091            }
1092            else {
1093                error = "No notification generated yet";
1094            }
1095        }
1096    }
1097
1098    error = GB_end_transaction(gb_main, error);
1099    return error;
1100}
1101
1102GB_ERROR GB_notify(GBDATA *gb_main, int id, const char *message) {
1103    /* called via 'arb_notify'
1104     * 'id' has to be generated by GB_generate_notification()
1105     * 'message' is passed to notification callback belonging to id
1106     */
1107
1108    GB_ERROR  error     = 0;
1109    GBDATA   *gb_notify = GB_search(gb_main, ARB_NOTIFICATIONS, GB_FIND);
1110
1111    if (!gb_notify) {
1112        error = "Missing notification data";
1113        gb_assert(0);           // GB_generate_notification() has not been called!
1114    }
1115    else {
1116        GBDATA *gb_id = GB_find_int(gb_notify, "id", id, SEARCH_GRANDCHILD);
1117
1118        if (!gb_id) {
1119            error = GBS_global_string("No notification for ID %i", id);
1120        }
1121        else {
1122            GBDATA *gb_message = GB_brother(gb_id, "message");
1123
1124            if (!gb_message) {
1125                error = "Missing 'message' entry";
1126            }
1127            else {
1128                // callback the instantiating DB client
1129                error = GB_write_string(gb_message, message);
1130            }
1131        }
1132    }
1133
1134    return error;
1135}
1136
1137// --------------------------------------------------------------------------------
1138
1139#ifdef UNIT_TESTS
1140#include <test_unit.h>
1141
1142#define TEST_EXPECT_SCANNED_EQUALS(path,expected) do {  \
1143        GB_transaction ta(gb_main);                     \
1144        StrArray fields;                                \
1145        GBT_scan_db(fields, gb_main, path);             \
1146        char  *joined = GBT_join_names(fields, ',');    \
1147        TEST_EXPECT_EQUAL(joined, expected);            \
1148        free(joined);                                   \
1149    } while (0)
1150
1151void TEST_scan_db() {
1152    GB_shell  shell;
1153    GBDATA   *gb_main = GB_open("TEST_loadsave.arb", "r");
1154
1155
1156    TEST_EXPECT_SCANNED_EQUALS(NULL,
1157                               "alignment/aligned,alignment/alignment_len,alignment/alignment_name,alignment/alignment_rem,alignment/alignment_type,alignment/alignment_write_security,alignment/auto_format,"
1158                               "byte,bytes,bytes,bytes,"
1159                               "extended/acc,extended/ali_16s/data,extended/aligned,extended/errors,extended/full_name,extended/name,"
1160                               "\nfloats,\tints,\tints_empty,"
1161                               "key_data/key/key_hidden,key_data/key/key_name,key_data/key/key_type,"
1162                               "species/AL,species/ARB_color,species/acc,species/ali_16s/data,species/bits_test,species/float_test,species/full_name,species/name,species/seqcheck,species/tax,"
1163                               "str,str_percent,use");
1164
1165    TEST_EXPECT_SCANNED_EQUALS("species", "/AL,/ARB_color,/acc,/ali_16s/data,/bits_test,/float_test,/full_name,/name,/seqcheck,/tax");
1166
1167    TEST_REJECT(GB_have_error());
1168   
1169    GB_close(gb_main);
1170}
1171
1172static arb_test::match_expectation macroFoundAs(const char *shortName, const char *expectedFullName_tmp, const char *expectedRelName, const char *partOfError) {
1173    char *expectedFullName = nulldup(expectedFullName_tmp);
1174
1175    using namespace   arb_test;
1176    expectation_group expected;
1177
1178    {
1179        char *found = fullMacroname(shortName);
1180        expected.add(that(found).is_equal_to(expectedFullName));
1181        if (found) {
1182            expected.add(that(GBT_relativeMacroname(found)).is_equal_to(expectedRelName));
1183        }
1184        free(found);
1185    }
1186
1187    GB_ERROR error = GB_have_error() ? GB_await_error() : NULL;
1188    if (partOfError) {
1189        if (error) {
1190            expected.add(that(error).does_contain(partOfError));
1191        }
1192        else {
1193            expected.add(that(error).does_differ_from_NULL());
1194        }
1195    }
1196    else {
1197        expected.add(that(error).is_equal_to_NULL());
1198    }
1199
1200    free(expectedFullName);
1201    return all().ofgroup(expected);
1202}
1203
1204#define TEST_FULLMACRO_EQUALS(shortName,fullName,relName) TEST_EXPECTATION(macroFoundAs(shortName, fullName, relName, NULL))
1205#define TEST_FULLMACRO_FAILS(shortName,expectedErrorPart) TEST_EXPECTATION(macroFoundAs(shortName, NULL, NULL, expectedErrorPart))
1206
1207void TEST_find_macros() {
1208    gb_getenv_hook old = GB_install_getenv_hook(arb_test::fakeenv);
1209
1210#define TEST         "test"
1211#define RESERVED     "reserved4ut"
1212#define TEST_AMC     TEST ".amc"
1213#define RESERVED_AMC RESERVED ".amc"
1214
1215    // unlink test.amc in ARBMACROHOME (from previous run)
1216    // ../UNIT_TESTER/run/homefake/.arb_prop/macros
1217    char *test_amc = strdup(GB_concat_path(GB_getenvARBMACROHOME(), TEST_AMC));
1218    char *res_amc  = strdup(GB_concat_path(GB_getenvARBMACROHOME(), RESERVED_AMC));
1219
1220    TEST_EXPECT_DIFFERENT(GB_unlink(test_amc), -1);
1221    TEST_EXPECT_DIFFERENT(GB_unlink(res_amc), -1);
1222
1223    // check if it finds macros in ARBMACRO = ../lib/macros
1224    TEST_FULLMACRO_EQUALS(TEST, GB_path_in_ARBLIB("macros/" TEST_AMC), TEST_AMC);
1225
1226    // searching reserved4ut.amc should fail now (macro should not exists)
1227    TEST_FULLMACRO_FAILS(RESERVED, "Failed to detect");
1228
1229    // --------------------------------
1230    // create 2 macros in ARBMACROHOME
1231    TEST_EXPECT_NO_ERROR(GBK_system(GBS_global_string("touch '%s'; touch '%s'", test_amc, res_amc)));
1232
1233    // searching test.amc should fail now (macro exists in ARBMACROHOME and ARBMACRO)
1234    TEST_FULLMACRO_FAILS(TEST, "ambiguous macro name");
1235
1236    // check if it finds macros in ARBMACROHOME = ../UNIT_TESTER/run/homefake/.arb_prop/macros
1237    TEST_FULLMACRO_EQUALS(RESERVED, res_amc, RESERVED_AMC);
1238
1239    free(res_amc);
1240    free(test_amc);
1241
1242    TEST_EXPECT_EQUAL((void*)arb_test::fakeenv, (void*)GB_install_getenv_hook(old));
1243}
1244
1245
1246#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.