source: trunk/SL/MACROS/dbserver.cxx

Last change on this file was 18159, checked in by westram, 5 years ago
  • full update from child 'fix' into 'trunk'
    • fix item name accessors (GBT_get_name + GBT_get_name_or_description)
    • add null2empty
  • adds: log:branches/fix@18140:18158
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.3 KB
Line 
1// ============================================================= //
2//                                                               //
3//   File      : dbserver.cxx                                    //
4//   Purpose   :                                                 //
5//                                                               //
6//   Coded by Ralf Westram (coder@reallysoft.de) in April 2013   //
7//   Institute of Microbiology (Technical University Munich)     //
8//   http://www.arb-home.de/                                     //
9//                                                               //
10// ============================================================= //
11
12#include "trackers.hxx"
13#include "dbserver.hxx"
14#include "macros.hxx"
15
16#include <arbdbt.h>
17#include <ad_remote.h>
18
19#include <aw_awar.hxx>
20#include <aw_msg.hxx>
21#include <aw_window.hxx>
22
23#include <unistd.h>
24
25#if defined(DEBUG)
26// # define DUMP_REMOTE_ACTIONS
27// # define DUMP_AUTHORIZATION // see also ../../ARBDB/adtools.cxx@DUMP_AUTH_HANDSHAKE
28#endif
29
30#if defined(DUMP_REMOTE_ACTIONS)
31# define IF_DUMP_ACTION(cmd) cmd
32#else
33# define IF_DUMP_ACTION(cmd)
34#endif
35
36#if defined(DUMP_AUTHORIZATION)
37# define IF_DUMP_AUTH(cmd) cmd
38#else
39# define IF_DUMP_AUTH(cmd)
40#endif
41
42
43
44#define ARB_SERVE_DB_TIMER 50  // ms
45#define ARB_CHECK_DB_TIMER 200 // ms
46
47struct db_interrupt_data : virtual Noncopyable {
48    remote_awars  remote;
49    GBDATA       *gb_main;
50
51    db_interrupt_data(GBDATA *gb_main_, const char *application_id)
52        : remote(application_id),
53          gb_main(gb_main_)
54    {}
55
56
57    GB_ERROR reconfigure(GBDATA *gb_main_, const char *application_id) {
58        GB_ERROR error = NULp;
59        if (gb_main_ != gb_main) {
60            error = "Attempt to reconfigure database interrupt with changed database";
61        }
62        else {
63            remote = remote_awars(application_id);
64        }
65        return error;
66    }
67};
68
69__ATTR__USERESULT static GB_ERROR check_for_remote_command(AW_root *aw_root, const db_interrupt_data& dib) { // @@@ split into several functions
70    arb_assert(!GB_have_error());
71
72    GB_ERROR  error   = NULp;
73    GBDATA   *gb_main = dib.gb_main;
74
75    GB_push_transaction(gb_main); // @@@ begin required/possible here?
76
77    if (GB_is_server(gb_main)) {
78        char *client_action = GBT_readOrCreate_string(gb_main, MACRO_TRIGGER_TRACKED, "");
79        if (client_action && client_action[0]) {
80            UserActionTracker *tracker       = aw_root->get_tracker();
81            MacroRecorder     *macroRecorder = dynamic_cast<MacroRecorder*>(tracker);
82
83            error               = macroRecorder->handle_tracked_client_action(client_action);
84            GB_ERROR trig_error = GBT_write_string(gb_main, MACRO_TRIGGER_TRACKED, ""); // tell client that action has been recorded
85            if (!error) error   = trig_error;
86        }
87        free(client_action);
88
89        if (!error) {
90            GB_ERROR macro_error = GB_get_macro_error(gb_main);
91            if (macro_error) {
92                UserActionTracker *tracker       = aw_root->get_tracker();
93                MacroRecorder     *macroRecorder = dynamic_cast<MacroRecorder*>(tracker);
94
95                if (macroRecorder->is_tracking()) { // only handle recording error here
96                    aw_message(GBS_global_string("%s\n"
97                                                 "macro recording aborted", macro_error));
98                    error = macroRecorder->stop_recording();
99
100                    GB_ERROR clr_error = GB_clear_macro_error(gb_main);
101                    if (!error) error  = clr_error;
102                }
103            }
104        }
105    }
106
107    if (error) {
108        GB_pop_transaction(gb_main);
109    }
110    else {
111        const remote_awars& remote = dib.remote;
112
113        long  *granted    = GBT_readOrCreate_int(gb_main, remote.granted(), 0);
114        pid_t  pid        = getpid();
115        bool   authorized = granted && *granted == pid;
116
117        if (!authorized) {
118            if (!granted) error = GBS_global_string("Failed to access '%s'", remote.granted());
119            else {
120                arb_assert(*granted != pid);
121
122                // @@@ differ between *granted == 0 and *granted != 0?
123
124                long *authReq       = GBT_readOrCreate_int(gb_main, remote.authReq(), 0);
125                if (!authReq) error = GBS_global_string("Failed to access '%s'", remote.authReq());
126                else if (*authReq) {
127                    GBDATA *gb_authAck     = GB_searchOrCreate_int(gb_main, remote.authAck(), 0);
128                    if (!gb_authAck) error = GBS_global_string("Failed to access '%s'", remote.authAck());
129                    else {
130                        pid_t authAck = GB_read_int(gb_authAck);
131                        if (authAck == 0) {
132                            // ack this process can execute remote commands
133                            error = GB_write_int(gb_authAck, pid);
134                            IF_DUMP_AUTH(fprintf(stderr, "acknowledging '%s' with pid %i\n", remote.authAck(), pid));
135                        }
136                        else if (authAck == pid) {
137                            // already acknowledged -- wait
138                        }
139                        else { // another process with same app-id acknowledged faster
140                            IF_DUMP_AUTH(fprintf(stderr, "did not acknowledge '%s' with pid %i (pid %i was faster)\n", remote.authAck(), pid, authAck));
141                            const char *merr = GBS_global_string("Detected two clients with id '%s'", remote.appID());
142                            error            = GB_set_macro_error(gb_main, merr);
143                        }
144                    }
145                }
146            }
147            error = GB_end_transaction(gb_main, error);
148        }
149        else {
150            char *action   = GBT_readOrCreate_string(gb_main, remote.action(), "");
151            char *value    = GBT_readOrCreate_string(gb_main, remote.value(), "");
152            char *tmp_awar = GBT_readOrCreate_string(gb_main, remote.awar(), "");
153
154            if (tmp_awar[0]) {
155                AW_awar *found_awar = aw_root->awar_no_error(tmp_awar);
156                if (!found_awar) {
157                    error = GBS_global_string("Unknown variable '%s'", tmp_awar);
158                }
159                else {
160                    if (strcmp(action, "AWAR_REMOTE_READ") == 0) {
161                        char *read_value = aw_root->awar(tmp_awar)->read_as_string();
162                        GBT_write_string(gb_main, remote.value(), read_value);
163                        IF_DUMP_ACTION(printf("remote command 'AWAR_REMOTE_READ' awar='%s' value='%s'\n", tmp_awar, read_value));
164                        free(read_value);
165                        action[0] = 0; // clear action (AWAR_REMOTE_READ is just a pseudo-action) :
166                        GBT_write_string(gb_main, remote.action(), "");
167                    }
168                    else if (strcmp(action, "AWAR_REMOTE_TOUCH") == 0) {
169                        GB_set_remote_action(gb_main, true);
170                        aw_root->awar(tmp_awar)->touch();
171                        GB_set_remote_action(gb_main, false);
172                        IF_DUMP_ACTION(printf("remote command 'AWAR_REMOTE_TOUCH' awar='%s'\n", tmp_awar));
173                        action[0] = 0; // clear action (AWAR_REMOTE_TOUCH is just a pseudo-action) :
174                        GBT_write_string(gb_main, remote.action(), "");
175                    }
176                    else {
177                        IF_DUMP_ACTION(printf("remote command (write awar) awar='%s' value='%s'\n", tmp_awar, value));
178                        GB_set_remote_action(gb_main, true);
179                        error = aw_root->awar(tmp_awar)->write_as_string(value);
180                        GB_set_remote_action(gb_main, false);
181                    }
182                }
183                GBT_write_string(gb_main, remote.result(), null2empty(error));
184                GBT_write_string(gb_main, remote.awar(), ""); // tell perl-client call has completed (BIO::remote_awar and BIO:remote_read_awar)
185
186                aw_message_if(error);
187            }
188            GB_pop_transaction(gb_main); // @@@ end required/possible here?
189            arb_assert(!GB_have_error()); // error exported by some callback? (unwanted)
190
191            if (action[0]) {
192                AW_cb *act = aw_root->search_remote_command(action);
193
194                if (act) {
195                    IF_DUMP_ACTION(printf("remote command (%s) found, running callback\n", action));
196                    arb_assert(!GB_have_error());
197                    GB_set_remote_action(gb_main, true);
198                    act->run_callbacks();
199                    GB_set_remote_action(gb_main, false);
200                    arb_assert(!GB_have_error()); // error exported by callback (unwanted)
201                    GBT_write_string(gb_main, remote.result(), "");
202                }
203                else {
204                    IF_DUMP_ACTION(printf("remote command (%s) is unknown\n", action));
205                    error = GBS_global_string("Unknown action '%s' in macro", action);
206                    GBT_write_string(gb_main, remote.result(), error);
207                }
208                GBT_write_string(gb_main, remote.action(), ""); // tell perl-client call has completed (remote_action)
209            }
210
211            free(tmp_awar);
212            free(value);
213            free(action);
214        }
215    }
216
217    arb_assert(!GB_have_error());
218    if (error) fprintf(stderr, "Error in check_for_remote_command: %s\n", error);
219    return error;
220}
221
222inline bool remote_command_handler(AW_root *awr, const db_interrupt_data& dib) {
223    // returns false in case of errors
224    arb_assert(!GB_have_error());
225
226    bool     ok    = true;
227    GB_ERROR error = check_for_remote_command(awr, dib);
228    if (error) {
229        aw_message(error);
230        ok = false;
231    }
232    return ok;
233}
234
235static unsigned serve_db_interrupt(AW_root *awr, db_interrupt_data *dib) { // server
236    bool success = GBCMS_accept_calls(dib->gb_main, false);
237    while (success) { // @@@ maybe abort this loop after some time? (otherwise: if a client polls to fast, the GUI of the server locks)
238        success = remote_command_handler(awr, *dib) && GBCMS_accept_calls(dib->gb_main, true);
239    }
240    return ARB_SERVE_DB_TIMER;
241}
242
243static unsigned check_db_interrupt(AW_root *awr, db_interrupt_data *dib) { // client
244    remote_command_handler(awr, *dib);
245    return ARB_CHECK_DB_TIMER;
246}
247
248// --------------------------------------------------------------------------------
249
250static db_interrupt_data *idle_interrupt = NULp;
251
252GB_ERROR startup_dbserver(AW_root *aw_root, const char *application_id, GBDATA *gb_main) {
253#if defined(DEBUG)
254    static bool initialized = false;
255    arb_assert(!initialized); // called twice - not able (yet)
256    initialized = true;
257#endif
258
259    arb_assert(got_macro_ability(aw_root));
260
261    GB_ERROR error = NULp;
262    if (GB_read_clients(gb_main) == 0) { // server
263        error = GBCMS_open(":", 0, gb_main);
264        if (error) {
265            error = GBS_global_string("THIS PROGRAM HAS PROBLEMS TO OPEN INTERCLIENT COMMUNICATION:\n"
266                                      "Reason: %s\n"
267                                      "(maybe there is already another server running)\n"
268                                      "You cannot use any EDITOR or other external SOFTWARE from here.\n"
269                                      "Advice: Close ARB again, open a console, type 'arb_clean' and restart arb.\n"
270                                      "Caution: Any unsaved data in an eventually running ARB will be lost.\n",
271                                      error);
272        }
273        else {
274            idle_interrupt = new db_interrupt_data(gb_main, application_id);
275            aw_root->add_timed_callback(ARB_SERVE_DB_TIMER, makeTimedCallback(serve_db_interrupt, idle_interrupt));
276        }
277    }
278    else { // client
279        idle_interrupt = new db_interrupt_data(gb_main, application_id);
280        aw_root->add_timed_callback(ARB_CHECK_DB_TIMER, makeTimedCallback(check_db_interrupt, idle_interrupt));
281    }
282
283    if (!error) {
284        // handle remote commands once (to create DB-entries; w/o they are created after startup of first DB-client)
285        arb_assert(idle_interrupt);
286        if (idle_interrupt) remote_command_handler(aw_root, *idle_interrupt);
287    }
288
289    return error;
290}
291
292GB_ERROR reconfigure_dbserver(const char *application_id, GBDATA *gb_main) {
293    // reconfigures a running dbserver (started with startup_dbserver)
294    arb_assert(idle_interrupt); // not started yet
295    return idle_interrupt->reconfigure(gb_main, application_id);
296}
297
Note: See TracBrowser for help on using the repository browser.