source: tags/ms_ra2q2/ARBDB/adtools.cxx

Last change on this file was 17396, checked in by westram, 6 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.7 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 = NULp; // 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 ARB_strdup("HELIX"); }
57char *GBT_get_default_helix_nr(GBDATA *) { return ARB_strdup("HELIX_NR"); }
58char *GBT_get_default_ref     (GBDATA *) { return ARB_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        ARB_alloc(buffer, 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, 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            const char *key = GB_read_key_pntr(gb2);
91            if (key[0] != '@') { // skip internal containers
92                sprintf(&prefix[len_of_prefix], "/%s", key);
93                gbt_scan_db_rek(gb2, prefix, scanner);
94            }
95        }
96    }
97    else {
98        if (prefix[0]) {
99            gb_assert(!GB_check_hkey(prefix)); // prefix has to be hierarchical key
100            LocallyModify<char> firstCharWithType(prefix[0], char(type)); // first char always is '/' -> use to store type in hash
101            GBS_incr_hash(scanner->hash_table, prefix);
102        }
103    }
104}
105
106static void gbt_scan_db_start(GBDATA *gbd, GB_DbScanner *scanner) {
107    GB_TYPES type = GB_read_type(gbd);
108    gb_assert(type == GB_DB); // has to be called with container!
109
110    if (type == GB_DB) {
111        char *prefix = scanner->buffer;
112        gb_assert(!prefix[0]); // has to be empty (do not reuse GB_DbScanner)
113        for (GBDATA *gb2 = GB_child(gbd); gb2; gb2 = GB_nextChild(gb2)) {  // find everything
114            prefix[0] = 0;
115            gbt_scan_db_rek(gb2, prefix, scanner);
116        }
117    }
118}
119
120
121struct scan_db_insert {
122    GB_DbScanner *scanner;
123    const char   *datapath;
124};
125
126static void gbs_scan_db_insert(const char *key, long /*val*/, void *cd_insert_data) {
127    scan_db_insert *insert    = (scan_db_insert *)cd_insert_data;
128    char           *to_insert = NULp;
129
130    if (!insert->datapath) {
131        to_insert = ARB_strdup(key);
132    }
133    else {
134        bool do_insert = ARB_strBeginsWith(key+1, insert->datapath);
135        gb_assert(implicated(!do_insert, !ARB_strBeginsWith(insert->datapath, key+1))); // oops - previously inserted also in this case. inspect!
136
137        if (do_insert) { // datapath matches
138            to_insert    = ARB_strdup(key+strlen(insert->datapath)); // cut off prefix
139            to_insert[0] = key[0]; // copy type
140        }
141    }
142
143    if (to_insert) {
144        GB_DbScanner *scanner = insert->scanner;
145        scanner->result.put(to_insert);
146    }
147}
148
149static int gbs_scan_db_compare(const void *left, const void *right, void *) {
150    return strcmp((GB_CSTR)left+1, (GB_CSTR)right+1);
151}
152
153
154void GBT_scan_db(StrArray& fieldNames, GBDATA *gbd, const char *datapath) {
155    /*! scan CONTAINER for existing sub-keys
156     *
157     * recurses completely downwards the DB tree
158     *
159     * @param fieldNames gets filled with result strings
160     * - each string is the path to a node beyond (any child container of) gbd
161     * - every string exists only once
162     * - the first character of a string is the type of the entry
163     * - the strings are sorted alphabetically
164     * @param gbd node where search starts
165     * @param datapath if not NULp -> only keys with prefix datapath are scanned and
166     * the prefix is removed from the resulting key_names.
167     *
168     * Note: this function is incredibly slow when called from clients
169     *
170     * Warning: avoid to add new callers for this function: the interface is really weird
171     */
172
173    {
174        GB_DbScanner scanner(fieldNames);
175        gbt_scan_db_start(gbd, &scanner);
176        scan_db_insert insert = { &scanner, datapath, };
177        GBS_hash_do_const_loop(scanner.hash_table, gbs_scan_db_insert, &insert);
178    }
179    fieldNames.sort(gbs_scan_db_compare, NULp);
180}
181
182// --------------------------
183//      message injection
184
185static void new_gbt_message_created_cb(GBDATA *gb_pending_messages) {
186    static int avoid_deadlock = 0;
187
188    if (!avoid_deadlock) {
189        GBDATA *gb_msg;
190
191        avoid_deadlock++;
192        GB_push_transaction(gb_pending_messages);
193
194        for (gb_msg = GB_entry(gb_pending_messages, "msg"); gb_msg;) {
195            {
196                const char *msg = GB_read_char_pntr(gb_msg);
197                GB_warning(msg);
198            }
199            {
200                GBDATA *gb_next_msg = GB_nextEntry(gb_msg);
201                GB_delete(gb_msg);
202                gb_msg              = gb_next_msg;
203            }
204        }
205
206        GB_pop_transaction(gb_pending_messages);
207        avoid_deadlock--;
208    }
209}
210
211inline GBDATA *find_or_create_error_container(GBDATA *gb_main) {
212    return GB_search(gb_main, ERROR_CONTAINER_PATH, GB_CREATE_CONTAINER);
213}
214
215void GBT_install_message_handler(GBDATA *gb_main) {
216    GBDATA *gb_pending_messages;
217
218    GB_push_transaction(gb_main);
219    gb_pending_messages = find_or_create_error_container(gb_main);
220    gb_assert(gb_pending_messages);
221    GB_add_callback(gb_pending_messages, GB_CB_SON_CREATED, makeDatabaseCallback(new_gbt_message_created_cb));
222    GB_pop_transaction(gb_main);
223
224#if defined(DEBUG) && 0
225    GBT_message(GB_get_root(gb_pending_messages), GBS_global_string("GBT_install_message_handler installed for gb_main=%p", gb_main));
226#endif // DEBUG
227}
228
229
230void GBT_message(GBDATA *gb_main, const char *msg) {
231    /*! When called in client(or server) this causes the DB server to show the message.
232     * Message is shown via GB_warning (which uses aw_message in GUIs)
233     *
234     * Note: The message is not shown before the transaction ends.
235     * If the transaction is aborted, the message is never shown!
236     *
237     * see also : GB_warning()
238     */
239
240    GB_ERROR error = GB_push_transaction(gb_main);
241
242    if (!error) {
243        GBDATA *gb_pending_messages = find_or_create_error_container(gb_main);
244        GBDATA *gb_msg              = gb_pending_messages ? GB_create(gb_pending_messages, "msg", GB_STRING) : NULp;
245
246        if (!gb_msg) error = GB_await_error();
247        else {
248            gb_assert(msg);
249            error = GB_write_string(gb_msg, msg);
250        }
251    }
252    error = GB_end_transaction(gb_main, error);
253
254    if (error) {
255        fprintf(stderr, "GBT_message: Failed to write message '%s'\n(Reason: %s)\n", msg, error);
256    }
257}
258
259char *GBT_read_string(GBDATA *gb_container, const char *fieldpath) {
260    /*! Read value from database field (of type GB_STRING)
261     *
262     * @param gb_container where to start search for field
263     * @param fieldpath relative path from gb_container
264     *
265     * @return
266     * NULp in case of error (use GB_await_error()) or when field does not exist.
267     * otherwise returns a heap copy.
268     *
269     * other functions return a pointer to a temporary variable (invalidated by next call)
270     */
271
272    GBDATA *gbd;
273    char   *result = NULp;
274
275    GB_push_transaction(gb_container);
276    gbd = GB_search(gb_container, fieldpath, GB_FIND);
277    if (gbd) result = GB_read_string(gbd);
278    GB_pop_transaction(gb_container);
279    return result;
280}
281
282char *GBT_read_as_string(GBDATA *gb_container, const char *fieldpath) {
283    /*! like GBT_read_string()
284     *
285     * but
286     * - field may be of any type (result gets converted to reasonable char*)
287     */
288
289    GBDATA *gbd;
290    char   *result = NULp;
291
292    GB_push_transaction(gb_container);
293    gbd = GB_search(gb_container, fieldpath, GB_FIND);
294    if (gbd) result = GB_read_as_string(gbd);
295    GB_pop_transaction(gb_container);
296    return result;
297}
298
299const char *GBT_read_char_pntr(GBDATA *gb_container, const char *fieldpath) {
300    /*! like GBT_read_string()
301     *
302     * but
303     * - result is not a heap-copy and may be invalidated by further DB access
304     *   (especially by overwriting that fields)
305     *
306     * Note: Under no circumstances you may modify the result!
307     */
308
309    GBDATA     *gbd;
310    const char *result = NULp;
311
312    GB_push_transaction(gb_container);
313    gbd = GB_search(gb_container, fieldpath, GB_FIND);
314    if (gbd) result = GB_read_char_pntr(gbd);
315    GB_pop_transaction(gb_container);
316    return result;
317}
318
319NOT4PERL long *GBT_read_int(GBDATA *gb_container, const char *fieldpath) {
320    /*! similar to GBT_read_string()
321     *
322     * but
323     * - for fields of type GB_INT
324     * - result gets invalidated by next call
325     */
326
327    GBDATA *gbd;
328    long   *result = NULp;
329
330    GB_push_transaction(gb_container);
331    gbd = GB_search(gb_container, fieldpath, GB_FIND);
332    if (gbd) {
333        static long result_var;
334        result_var = GB_read_int(gbd);
335        result     = &result_var;
336    }
337    GB_pop_transaction(gb_container);
338    return result;
339}
340
341NOT4PERL float *GBT_read_float(GBDATA *gb_container, const char *fieldpath) {
342    /*! similar to GBT_read_string()
343     *
344     * but
345     * - for fields of type GB_FLOAT
346     * - result gets invalidated by next call
347     */
348
349    GBDATA *gbd;
350    float  *result = NULp;
351
352    GB_push_transaction(gb_container);
353    gbd = GB_search(gb_container, fieldpath, GB_FIND);
354    if (gbd) {
355        static float result_var;
356        result_var = GB_read_float(gbd);
357        result     = &result_var;
358    }
359    GB_pop_transaction(gb_container);
360    return result;
361}
362
363char *GBT_readOrCreate_string(GBDATA *gb_container, const char *fieldpath, const char *default_value) {
364    /*! like GBT_read_string(),
365     *
366     * but if field does not exist, it will be created and initialized with 'default_value'
367     */
368    GBDATA *gb_string;
369    char   *result = NULp;
370
371    GB_push_transaction(gb_container);
372    gb_string             = GB_searchOrCreate_string(gb_container, fieldpath, default_value);
373    if (gb_string) result = GB_read_string(gb_string);
374    GB_pop_transaction(gb_container);
375    return result;
376}
377
378const char *GBT_readOrCreate_char_pntr(GBDATA *gb_container, const char *fieldpath, const char *default_value) {
379    /*! like GBT_read_char_pntr(),
380     *
381     * but if field does not exist, it will be created and initialized with 'default_value'
382     */
383
384    GBDATA     *gb_string;
385    const char *result = NULp;
386
387    GB_push_transaction(gb_container);
388    gb_string             = GB_searchOrCreate_string(gb_container, fieldpath, default_value);
389    if (gb_string) result = GB_read_char_pntr(gb_string);
390    GB_pop_transaction(gb_container);
391    return result;
392}
393
394NOT4PERL long *GBT_readOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value) {
395    /*! like GBT_read_int(),
396     *
397     * but if field does not exist, it will be created and initialized with 'default_value'
398     */
399
400    GBDATA *gb_int;
401    long   *result = NULp;
402
403    GB_push_transaction(gb_container);
404    gb_int = GB_searchOrCreate_int(gb_container, fieldpath, default_value);
405    if (gb_int) {
406        static long result_var;
407        result_var = GB_read_int(gb_int);
408        result     = &result_var;
409    }
410    GB_pop_transaction(gb_container);
411    return result;
412}
413
414NOT4PERL float *GBT_readOrCreate_float(GBDATA *gb_container, const char *fieldpath, float default_value) {
415    /*! like GBT_read_float(),
416     *
417     * but if field does not exist, it will be created and initialized with 'default_value'
418     */
419
420    gb_assert(default_value == default_value); // !nan
421
422    GBDATA *gb_float;
423    float  *result = NULp;
424
425    GB_push_transaction(gb_container);
426    gb_float = GB_searchOrCreate_float(gb_container, fieldpath, default_value);
427    if (gb_float) {
428        static float result_var;
429        result_var = GB_read_float(gb_float);
430        result     = &result_var;
431    }
432    else {
433        gb_assert(0);
434    }
435    GB_pop_transaction(gb_container);
436    return result;
437}
438
439// -------------------------------------------------------------------
440//      overwrite existing or create new database field
441//      (field must not exist twice or more - it has to be unique!!)
442
443GB_ERROR GBT_write_string(GBDATA *gb_container, const char *fieldpath, const char *content) {
444    /*! Write content to database field of type GB_STRING
445     *
446     * if field exists, it will be overwritten.
447     * if field does not exist, it will be created.
448     *
449     * The field should be unique, i.e. it should not exist twice or more in it's parent container
450     *
451     * @return GB_ERROR on failure
452     */
453    GB_ERROR  error = GB_push_transaction(gb_container); // @@@ result unused
454    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_STRING);
455    if (!gbd) error = GB_await_error();
456    else {
457        error = GB_write_string(gbd, content);
458        gb_assert(!GB_nextEntry(gbd)); // only one entry should exist (sure you want to use this function?)
459    }
460    return GB_end_transaction(gb_container, error);
461}
462
463GB_ERROR GBT_write_int(GBDATA *gb_container, const char *fieldpath, long content) {
464    /*! like GBT_write_string(),
465     *
466     * but for fields of type GB_INT
467     */
468    GB_ERROR  error = GB_push_transaction(gb_container); // @@@ result unused
469    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_INT);
470    if (!gbd) error = GB_await_error();
471    else {
472        error = GB_write_int(gbd, content);
473        gb_assert(!GB_nextEntry(gbd)); // only one entry should exist (sure you want to use this function?)
474    }
475    return GB_end_transaction(gb_container, error);
476}
477
478GB_ERROR GBT_write_byte(GBDATA *gb_container, const char *fieldpath, unsigned char content) {
479    /*! like GBT_write_string(),
480     *
481     * but for fields of type GB_BYTE
482     */
483    GB_ERROR  error = GB_push_transaction(gb_container); // @@@ result unused
484    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_BYTE);
485    if (!gbd) error = GB_await_error();
486    else {
487        error = GB_write_byte(gbd, content);
488        gb_assert(!GB_nextEntry(gbd)); // only one entry should exist (sure you want to use this function?)
489    }
490    return GB_end_transaction(gb_container, error);
491}
492
493
494GB_ERROR GBT_write_float(GBDATA *gb_container, const char *fieldpath, float content) {
495    /*! like GBT_write_string(),
496     *
497     * but for fields of type GB_FLOAT
498     */
499
500    gb_assert(content == content); // !nan
501
502    GB_ERROR  error = GB_push_transaction(gb_container); // @@@ result unused
503    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_FLOAT);
504    if (!gbd) error = GB_await_error();
505    else {
506        error = GB_write_float(gbd, content);
507        gb_assert(!GB_nextEntry(gbd)); // only one entry should exist (sure you want to use this function?)
508    }
509    return GB_end_transaction(gb_container, error);
510}
511
512
513// --------------------
514//      save & load
515
516GBDATA *GBT_open(const char *path, const char *opent) {
517    /*! Open a database (as GB_open does)
518     *  Additionally:
519     *  - disable saving in the PT_SERVER directory,
520     *  - create an index for species and extended names (server only!),
521     *
522     * @param path filename of the DB
523     * @param opent see GB_login()
524     * @return database handle (or NULp in which case an error is exported)
525     * @see GB_open()
526     */
527
528    GBDATA *gbd = GB_open(path, opent);
529    if (gbd) {
530        GB_disable_path(gbd, GB_path_in_ARBLIB("pts/*"));
531        GB_ERROR error = NULp;
532        {
533            GB_transaction ta(gbd);
534
535            if (!strchr(path, ':')) {
536                GBDATA *species_data = GB_search(gbd, "species_data", GB_FIND); // do NOT create species_data here!
537                if (species_data) {
538                    long hash_size = max(GB_number_of_subentries(species_data), GBT_SPECIES_INDEX_SIZE);
539                    error          = GB_create_index(species_data, "name", GB_IGNORE_CASE, hash_size);
540
541                    if (!error) {
542                        GBDATA *extended_data = GBT_get_SAI_data(gbd);
543                        hash_size             = max(GB_number_of_subentries(extended_data), GBT_SAI_INDEX_SIZE);
544                        error                 = GB_create_index(extended_data, "name", GB_IGNORE_CASE, hash_size);
545                    }
546                }
547            }
548        }
549        if (error) {
550            GB_close(gbd);
551            gbd = NULp;
552            GB_export_error(error);
553        }
554    }
555    gb_assert(contradicted(gbd, GB_have_error()));
556    return gbd;
557}
558
559/* --------------------------------------------------------------------------------
560 * Remote commands
561 *
562 * Note: These commands may seem to be unused from inside ARB.
563 * They get used, but only indirectly via the macro-function.
564 *
565 * Search for
566 * - BIO::remote_action         (use of GBT_remote_action)
567 * - BIO::remote_awar           (use of GBT_remote_awar)
568 * - BIO::remote_read_awar      (use of GBT_remote_read_awar)
569 */
570
571static GBDATA *wait_for_dbentry(GBDATA *gb_main, const char *entry, const ARB_timeout *timeout, bool verbose) {
572    // if 'timeout' not NULp -> abort and return NULp if timeout has passed (otherwise wait forever)
573
574    if (verbose) GB_warningf("[waiting for DBENTRY '%s']", entry);
575    GBDATA *gbd;
576    {
577        ARB_timestamp  start;
578        MacroTalkSleep increasing;
579
580        while (1) {
581            GB_begin_transaction(gb_main);
582            gbd = GB_search(gb_main, entry, GB_FIND);
583            GB_commit_transaction(gb_main);
584            if (gbd) break;
585            if (timeout && timeout->passed()) break;
586            increasing.sleep();
587        }
588    }
589    if (gbd && verbose) GB_warningf("[found DBENTRY '%s']", entry);
590    return gbd;
591}
592
593static GB_ERROR gbt_wait_for_remote_action(GBDATA *gb_main, GBDATA *gb_action, const char *awar_read) {
594    // waits until remote action has finished
595
596    MacroTalkSleep increasing;
597    GB_ERROR       error = NULp;
598    while (!error) {
599        increasing.sleep();
600        error = GB_begin_transaction(gb_main);
601        if (!error) {
602            char *ac = GB_read_string(gb_action);
603            if (ac[0] == 0) { // action has been cleared from remote side
604                GBDATA *gb_result = GB_search(gb_main, awar_read, GB_STRING);
605                error             = GB_read_char_pntr(gb_result); // check for errors
606            }
607            free(ac);
608        }
609        error = GB_end_transaction(gb_main, error);
610    }
611
612    if (error && !error[0]) return NULp; // empty error means ok
613    return error; // may be error or result
614}
615
616static void mark_as_macro_executor(GBDATA *gb_main, bool mark) {
617    // call this with 'mark' = true to mark yourself as "client running a macro"
618    // -> GB_close will automatically announce termination of this client to main DB (MACRO_TRIGGER_TERMINATED)
619    // do NOT call with 'mark' = false
620
621    static bool client_is_macro_executor = false;
622    if (mark) {
623        if (!client_is_macro_executor) {
624            GB_atclose_callback(gb_main, makeDatabaseCallback(mark_as_macro_executor, false));
625            client_is_macro_executor = true;
626        }
627    }
628    else {
629        // called via GB_atclose_callback (i.e. at end of currently executed macro file)
630        GB_ERROR error = NULp;
631        gb_assert(client_is_macro_executor);
632        if (client_is_macro_executor) {
633            GB_transaction ta(gb_main);
634
635            GBDATA *gb_terminated = GB_search(gb_main, MACRO_TRIGGER_TERMINATED, GB_FIND);
636            gb_assert(gb_terminated); // should have been created by macro caller
637            if (gb_terminated) {
638                error = GB_write_int(gb_terminated, GB_read_int(gb_terminated)+1); // notify macro caller
639            }
640
641            error = ta.close(error);
642        }
643        if (error) GBT_message(gb_main, error);
644    }
645}
646
647inline GB_ERROR set_intEntry_to(GBDATA *gb_main, const char *path, int value) {
648    GBDATA *gbd = GB_searchOrCreate_int(gb_main, path, value);
649    return gbd ? GB_write_int(gbd, value) : GB_await_error();
650}
651
652#if defined(DEBUG)
653// # define DUMP_AUTH_HANDSHAKE // see also ../SL/MACROS/dbserver.cxx@DUMP_AUTHORIZATION
654#endif
655
656#if defined(DUMP_AUTH_HANDSHAKE)
657# define IF_DUMP_HANDSHAKE(cmd) cmd
658#else
659# define IF_DUMP_HANDSHAKE(cmd)
660#endif
661
662GB_ERROR GB_set_macro_error(GBDATA *gb_main, const char *curr_error) {
663    GB_ERROR        error          = NULp;
664    GB_transaction  ta(gb_main);
665    GBDATA         *gb_macro_error = GB_searchOrCreate_string(gb_main, MACRO_TRIGGER_ERROR, curr_error);
666    if (gb_macro_error) {
667        const char *prev_error = GB_read_char_pntr(gb_macro_error);
668        if (prev_error && prev_error[0]) { // already have an error
669            if (!strstr(prev_error, curr_error)) { // do not add message twice
670                error = GB_write_string(gb_macro_error, GBS_global_string("%s\n%s", prev_error, curr_error));
671            }
672        }
673        else {
674            error = GB_write_string(gb_macro_error, curr_error);
675        }
676    }
677    return error;
678}
679GB_ERROR GB_get_macro_error(GBDATA *gb_main) {
680    GB_ERROR error = NULp;
681
682    GB_transaction  ta(gb_main);
683    GBDATA         *gb_macro_error = GB_search(gb_main, MACRO_TRIGGER_ERROR, GB_FIND);
684    if (gb_macro_error) {
685        const char *macro_error       = GB_read_char_pntr(gb_macro_error);
686        if (!macro_error) macro_error = GBS_global_string("failed to retrieve error message (Reason: %s)", GB_await_error());
687        if (macro_error[0]) error     = GBS_global_string("macro-error: %s", macro_error);
688    }
689    return error;
690}
691GB_ERROR GB_clear_macro_error(GBDATA *gb_main) {
692    GB_transaction  ta(gb_main);
693    GB_ERROR        error          = NULp;
694    GBDATA         *gb_macro_error = GB_search(gb_main, MACRO_TRIGGER_ERROR, GB_FIND);
695    if (gb_macro_error) error      = GB_write_string(gb_macro_error, "");
696    return error;
697}
698
699static GB_ERROR start_remote_command_for_application(GBDATA *gb_main, const remote_awars& remote, const ARB_timeout *timeout, bool verbose) {
700    // Called before any remote command will be written to DB.
701    // Application specific initialization is done here.
702    //
703    // if 'timeout' not NULp -> abort with error if timeout has passed (otherwise wait forever)
704
705    ARB_timestamp start;
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(gb_main, remote.authAck(), timeout, verbose);
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            // happens after timeout (handled by next if-clause)
769        }
770
771        if (timeout && timeout->passed()) {
772            wait_for_app = false;
773            error        = "remote application did not answer (within timeout)";
774        }
775
776        if (wait_for_app) {
777            increasing.sleep();
778        }
779    }
780
781    return error;
782}
783
784NOT4PERL GB_ERROR GBT_remote_action_with_timeout(GBDATA *gb_main, const char *application, const char *action_name, const class ARB_timeout *timeout, bool verbose) {
785    // if timeout_ms > 0 -> abort with error if application does not answer (e.g. is not running)
786    // Note: opposed to GBT_remote_action, this function may NOT be called directly by perl-macros
787
788    remote_awars remote(application);
789    GB_ERROR     error = start_remote_command_for_application(gb_main, remote, timeout, verbose);
790
791    if (!error) {
792        GBDATA *gb_action = wait_for_dbentry(gb_main, remote.action(), NULp, false);
793        error             = GB_begin_transaction(gb_main);
794        if (!error) error = GB_write_string(gb_action, action_name); // write command
795        error             = GB_end_transaction(gb_main, error);
796        if (!error) error = gbt_wait_for_remote_action(gb_main, gb_action, remote.result());
797    }
798    return error;
799}
800
801GB_ERROR GBT_remote_action(GBDATA *gb_main, const char *application, const char *action_name) {
802    // needs to be public (used exclusively(!) by perl-macros)
803    mark_as_macro_executor(gb_main, true);
804    return GBT_remote_action_with_timeout(gb_main, application, action_name, NULp, true);
805}
806
807GB_ERROR GBT_remote_awar(GBDATA *gb_main, const char *application, const char *awar_name, const char *value) {
808    // needs to be public (used exclusively(!) by perl-macros)
809
810    mark_as_macro_executor(gb_main, true);
811
812    remote_awars remote(application);
813    GB_ERROR     error = start_remote_command_for_application(gb_main, remote, NULp, true);
814
815    if (!error) {
816        GBDATA *gb_awar   = wait_for_dbentry(gb_main, remote.awar(), NULp, false);
817        error             = GB_begin_transaction(gb_main);
818        if (!error) error = GB_write_string(gb_awar, awar_name);
819        if (!error) error = GBT_write_string(gb_main, remote.value(), value);
820        error             = GB_end_transaction(gb_main, error);
821        if (!error) error = gbt_wait_for_remote_action(gb_main, gb_awar, remote.result());
822    }
823    return error;
824}
825
826GB_ERROR GBT_remote_read_awar(GBDATA *gb_main, const char *application, const char *awar_name) {
827    // needs to be public (used exclusively(!) by perl-macros)
828
829    mark_as_macro_executor(gb_main, true);
830
831    remote_awars remote(application);
832    GB_ERROR     error = start_remote_command_for_application(gb_main, remote, NULp, true);
833
834    if (!error) {
835        GBDATA *gb_awar   = wait_for_dbentry(gb_main, remote.awar(), NULp, false);
836        error             = GB_begin_transaction(gb_main);
837        if (!error) error = GB_write_string(gb_awar, awar_name);
838        if (!error) error = GBT_write_string(gb_main, remote.action(), "AWAR_REMOTE_READ");
839        error             = GB_end_transaction(gb_main, error);
840        if (!error) error = gbt_wait_for_remote_action(gb_main, gb_awar, remote.value());
841    }
842    return error;
843}
844
845inline char *find_macro_in(const char *dir, const char *macroname) {
846    char *full = GBS_global_string_copy("%s/%s", dir, macroname);
847    if (!GB_is_readablefile(full)) {
848        freeset(full, GBS_global_string_copy("%s.amc", full));
849        if (!GB_is_readablefile(full)) freenull(full);
850    }
851    return full;
852}
853
854static char *fullMacroname(const char *macro_name) {
855    /*! detect full path of 'macro_name'
856     * @param macro_name full path or path relative to ARBMACRO or ARBMACROHOME
857     * @return full path or NULp (in which case an error is exported)
858     */
859
860    gb_assert(!GB_have_error());
861
862    if (GB_is_readablefile(macro_name)) return ARB_strdup(macro_name);
863
864    char *in_ARBMACROHOME = find_macro_in(GB_getenvARBMACROHOME(), macro_name);
865    char *in_ARBMACRO     = find_macro_in(GB_getenvARBMACRO(),     macro_name);
866    char *result          = NULp;
867
868    if (in_ARBMACROHOME) {
869        if (in_ARBMACRO) {
870            GB_export_errorf("ambiguous macro name '%s'\n"
871                             "('%s' and\n"
872                             " '%s' exist both.\n"
873                             " You have to rename or delete one of them!)",
874                             macro_name, in_ARBMACROHOME, in_ARBMACRO);
875        }
876        else reassign(result, in_ARBMACROHOME);
877    }
878    else {
879        if (in_ARBMACRO) reassign(result, in_ARBMACRO);
880        else GB_export_errorf("Failed to detect macro '%s'", macro_name);
881    }
882
883    free(in_ARBMACRO);
884    free(in_ARBMACROHOME);
885
886    gb_assert(contradicted(result, GB_have_error()));
887
888    return result;
889}
890
891inline const char *relative_inside(const char *dir, const char *fullpath) {
892    if (ARB_strBeginsWith(fullpath, dir)) {
893        const char *result = fullpath+strlen(dir);
894        if (result[0] == '/') return result+1;
895    }
896    return NULp;
897}
898
899const char *GBT_relativeMacroname(const char *macro_name) {
900    /*! make macro_name relative if it is located in or below ARBMACROHOME or ARBMACRO.
901     * Inverse function of fullMacroname()
902     * @return pointer into macro_name (relative part) or macro_name itself (if macro is located somewhere else)
903     */
904
905    const char *result  = relative_inside(GB_getenvARBMACROHOME(), macro_name);
906    if (!result) result = relative_inside(GB_getenvARBMACRO(), macro_name);
907    if (!result) result = macro_name;
908    return result;
909}
910
911GB_ERROR GBT_macro_execute(const char *macro_name, bool loop_marked, bool run_async) {
912    /*! simply execute a macro
913     *
914     * In doubt: do not use this function, instead use ../SL/MACROS/macros.hxx@execute_macro
915     *
916     * @param macro_name contains the macro filename (either with full path or relative to directories pointed to by ARBMACRO or ARBMACROHOME)
917     * @param loop_marked if true -> call macro once for each marked species
918     * @param run_async if true -> run perl asynchronously (will not detect perl failure in that case!)
919     */
920
921    GB_ERROR  error     = NULp;
922    char     *fullMacro = fullMacroname(macro_name);
923    if (!fullMacro) {
924        error = GB_await_error();
925    }
926    else {
927        char *perl_args = NULp;
928        freeset(fullMacro, GBK_singlequote(fullMacro));
929        if (loop_marked) {
930            const char *with_all_marked = GB_path_in_ARBHOME("PERL_SCRIPTS/MACROS/with_all_marked.pl");
931            perl_args = GBS_global_string_copy("'%s' %s", with_all_marked, fullMacro);
932        }
933        else {
934            perl_args = GBS_global_string_copy("%s", fullMacro);
935        }
936
937        char *cmd = GBS_global_string_copy("perl %s %s", perl_args, run_async ? "&" : "");
938
939        error = GBK_system(cmd);
940
941        free(cmd);
942        free(perl_args);
943        free(fullMacro);
944    }
945    return error;
946}
947
948// --------------------------------------------------------------------------------
949
950#ifdef UNIT_TESTS
951#include <test_unit.h>
952
953#define TEST_EXPECT_SCANNED_EQUALS(path,expected) do {  \
954        GB_transaction ta(gb_main);                     \
955        StrArray fields;                                \
956        GBT_scan_db(fields, gb_main, path);             \
957        char  *joined = GBT_join_strings(fields, ',');  \
958        TEST_EXPECT_EQUAL(joined, expected);            \
959        free(joined);                                   \
960    } while (0)
961
962void TEST_scan_db() {
963    GB_shell  shell;
964    GBDATA   *gb_main = GB_open("TEST_loadsave.arb", "r");
965
966    TEST_EXPECT_SCANNED_EQUALS(NULp,
967                               "\x03""alignment/aligned,\x03""alignment/alignment_len,\falignment/alignment_name,\falignment/alignment_rem,\falignment/alignment_type,\x03""alignment/alignment_write_security,\x03""alignment/auto_format,"
968                               "\x02""byte,"
969                               "\bbytes,"
970                               "\fextended/acc,\fextended/ali_16s/_TYPE,\x06""extended/ali_16s/bits,\fextended/ali_16s/data,\fextended/aligned,\x03""extended/errors,\fextended/full_name,\fextended/name,"
971                               "\nfloats,\tints,\tints_empty,"
972                               "\x03key_data/key/key_hidden,\fkey_data/key/key_name,\x03key_data/key/key_type,"
973                               "\fspecies/AL,\x03species/ARB_color,\fspecies/acc,\fspecies/ali_16s/data,\x06species/bits_test,\x04species/float_test,\fspecies/full_name,\fspecies/name,\fspecies/seqcheck,\fspecies/tax,"
974                               "\fstr,\fstr_percent,\fuse");
975
976    TEST_EXPECT_SCANNED_EQUALS("species", "\f/AL,\x03/ARB_color,\f/acc,\f/ali_16s/data,\x06/bits_test,\x04/float_test,\f/full_name,\f/name,\f/seqcheck,\f/tax");
977
978    TEST_REJECT(GB_have_error());
979   
980    GB_close(gb_main);
981}
982
983static arb_test::match_expectation macroFoundAs(const char *shortName, const char *expectedFullName_tmp, const char *expectedRelName, const char *partOfError) {
984    char *expectedFullName = nulldup(expectedFullName_tmp);
985
986    using namespace   arb_test;
987    expectation_group expected;
988
989    {
990        char *found = fullMacroname(shortName);
991        expected.add(that(found).is_equal_to(expectedFullName));
992        if (found) {
993            expected.add(that(GBT_relativeMacroname(found)).is_equal_to(expectedRelName));
994        }
995        free(found);
996    }
997
998    GB_ERROR error = GB_incur_error();
999    if (partOfError) {
1000        if (error) {
1001            expected.add(that(error).does_contain(partOfError));
1002        }
1003        else {
1004            expected.add(that(error).does_differ_from_NULL());
1005        }
1006    }
1007    else {
1008        expected.add(that(error).is_equal_to_NULL());
1009    }
1010
1011    free(expectedFullName);
1012    return all().ofgroup(expected);
1013}
1014
1015#define TEST_FULLMACRO_EQUALS(shortName,fullName,relName) TEST_EXPECTATION(macroFoundAs(shortName, fullName, relName, NULp))
1016#define TEST_FULLMACRO_FAILS(shortName,expectedErrorPart) TEST_EXPECTATION(macroFoundAs(shortName, NULp, NULp, expectedErrorPart))
1017
1018void TEST_find_macros() {
1019    gb_getenv_hook old = GB_install_getenv_hook(arb_test::fakeenv);
1020
1021#define TEST         "_dolog"       // name of a macro existing in ../lib/macros
1022#define RESERVED     "reserved4ut"  // name of a macro NOT existing in ../lib/macros
1023#define TEST_AMC     TEST ".amc"
1024#define RESERVED_AMC RESERVED ".amc"
1025
1026    // unlink test.amc in fake-ARBMACROHOME (from previous run)
1027    // ../UNIT_TESTER/run/homefake/.arb_prop/macros
1028    char *test_amc = ARB_strdup(GB_concat_path(GB_getenvARBMACROHOME(), TEST_AMC));
1029    char *res_amc  = ARB_strdup(GB_concat_path(GB_getenvARBMACROHOME(), RESERVED_AMC));
1030
1031    TEST_EXPECT_DIFFERENT(GB_unlink(test_amc), -1);
1032    TEST_EXPECT_DIFFERENT(GB_unlink(res_amc), -1);
1033
1034    // check if it finds macros in ARBMACRO = ../lib/macros
1035    TEST_FULLMACRO_EQUALS(TEST, GB_path_in_ARBLIB("macros/" TEST_AMC), TEST_AMC);
1036
1037    // searching reserved4ut.amc should fail now (macro should not exists)
1038    TEST_FULLMACRO_FAILS(RESERVED, "Failed to detect");
1039
1040    // ------------------------------------
1041    // create 2 macros in fake-ARBMACROHOME
1042    TEST_EXPECT_NO_ERROR(GBK_system(GBS_global_string("touch '%s'; touch '%s'", test_amc, res_amc)));
1043
1044    // searching test.amc should fail now (macro exists in ARBMACROHOME and ARBMACRO)
1045    TEST_FULLMACRO_FAILS(TEST, "ambiguous macro name");
1046
1047    // check if it finds macros in ARBMACROHOME = ../UNIT_TESTER/run/homefake/.arb_prop/macros
1048    TEST_FULLMACRO_EQUALS(RESERVED, res_amc, RESERVED_AMC);
1049
1050    free(res_amc);
1051    free(test_amc);
1052
1053    TEST_EXPECT_EQUAL((void*)arb_test::fakeenv, (void*)GB_install_getenv_hook(old));
1054}
1055TEST_PUBLISH(TEST_find_macros);
1056
1057#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.