source: tags/arb-6.0.4/SL/MACROS/dbserver.cxx

Last change on this file was 12267, checked in by westram, 11 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.0 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 = 0;
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   = 0;
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                        aw_root->awar(tmp_awar)->touch();
170                        IF_DUMP_ACTION(printf("remote command 'AWAR_REMOTE_TOUCH' awar='%s'\n", tmp_awar));
171                        action[0] = 0; // clear action (AWAR_REMOTE_TOUCH is just a pseudo-action) :
172                        GBT_write_string(gb_main, remote.action(), "");
173                    }
174                    else {
175                        IF_DUMP_ACTION(printf("remote command (write awar) awar='%s' value='%s'\n", tmp_awar, value));
176                        error = aw_root->awar(tmp_awar)->write_as_string(value);
177                    }
178                }
179                GBT_write_string(gb_main, remote.result(), error ? error : "");
180                GBT_write_string(gb_main, remote.awar(), ""); // tell perl-client call has completed (BIO::remote_awar and BIO:remote_read_awar)
181
182                aw_message_if(error);
183            }
184            GB_pop_transaction(gb_main); // @@@ end required/possible here?
185            arb_assert(!GB_have_error()); // error exported by some callback? (unwanted)
186
187            if (action[0]) {
188                AW_cb *cbs = aw_root->search_remote_command(action);
189
190                if (cbs) {
191                    IF_DUMP_ACTION(printf("remote command (%s) found, running callback\n", action));
192                    arb_assert(!GB_have_error());
193                    cbs->run_callbacks();
194                    arb_assert(!GB_have_error()); // error exported by callback (unwanted)
195                    GBT_write_string(gb_main, remote.result(), "");
196                }
197                else {
198                    IF_DUMP_ACTION(printf("remote command (%s) is unknown\n", action));
199                    aw_message(GBS_global_string("Unknown action '%s' in macro", action));
200                    GBT_write_string(gb_main, remote.result(), GB_await_error());
201                }
202                GBT_write_string(gb_main, remote.action(), ""); // tell perl-client call has completed (remote_action)
203            }
204
205            free(tmp_awar);
206            free(value);
207            free(action);
208        }
209    }
210
211    arb_assert(!GB_have_error());
212    if (error) fprintf(stderr, "Error in check_for_remote_command: %s\n", error);
213    return error;
214}
215
216inline bool remote_command_handler(AW_root *awr, const db_interrupt_data& dib) {
217    // returns false in case of errors
218    arb_assert(!GB_have_error());
219
220    bool     ok    = true;
221    GB_ERROR error = check_for_remote_command(awr, dib);
222    if (error) {
223        aw_message(error);
224        ok = false;
225    }
226    return ok;
227}
228
229static unsigned serve_db_interrupt(AW_root *awr, db_interrupt_data *dib) { // server
230    bool success = GBCMS_accept_calls(dib->gb_main, false);
231    while (success) { // @@@ maybe abort this loop after some time? (otherwise: if a client polls to fast, the GUI of the server locks)
232        success = remote_command_handler(awr, *dib) && GBCMS_accept_calls(dib->gb_main, true);
233    }
234    return ARB_SERVE_DB_TIMER;
235}
236
237static unsigned check_db_interrupt(AW_root *awr, db_interrupt_data *dib) { // client
238    remote_command_handler(awr, *dib);
239    return ARB_CHECK_DB_TIMER;
240}
241
242// --------------------------------------------------------------------------------
243
244static db_interrupt_data *idle_interrupt = NULL;
245
246GB_ERROR startup_dbserver(AW_root *aw_root, const char *application_id, GBDATA *gb_main) {
247#if defined(DEBUG)
248    static bool initialized = false;
249    arb_assert(!initialized); // called twice - not able (yet)
250    initialized = true;
251#endif
252
253    arb_assert(got_macro_ability(aw_root));
254
255    GB_ERROR error = NULL;
256    if (GB_read_clients(gb_main) == 0) { // server
257        error = GBCMS_open(":", 0, gb_main);
258        if (error) {
259            error = GBS_global_string("THIS PROGRAM HAS PROBLEMS TO OPEN INTERCLIENT COMMUNICATION:\n"
260                                      "Reason: %s\n"
261                                      "(maybe there is already another server running)\n"
262                                      "You cannot use any EDITOR or other external SOFTWARE from here.\n"
263                                      "Advice: Close ARB again, open a console, type 'arb_clean' and restart arb.\n"
264                                      "Caution: Any unsaved data in an eventually running ARB will be lost.\n",
265                                      error);
266        }
267        else {
268            idle_interrupt = new db_interrupt_data(gb_main, application_id);
269            aw_root->add_timed_callback(ARB_SERVE_DB_TIMER, makeTimedCallback(serve_db_interrupt, idle_interrupt));
270        }
271    }
272    else { // client
273        idle_interrupt = new db_interrupt_data(gb_main, application_id);
274        aw_root->add_timed_callback(ARB_CHECK_DB_TIMER, makeTimedCallback(check_db_interrupt, idle_interrupt));
275    }
276
277    if (!error) {
278        // handle remote commands once (to create DB-entries; w/o they are created after startup of first DB-client)
279        arb_assert(idle_interrupt);
280        if (idle_interrupt) remote_command_handler(aw_root, *idle_interrupt);
281    }
282
283    return error;
284}
285
286GB_ERROR reconfigure_dbserver(const char *application_id, GBDATA *gb_main) {
287    // reconfigures a running dbserver (started with startup_dbserver)
288    arb_assert(idle_interrupt); // not started yet
289    return idle_interrupt->reconfigure(gb_main, application_id);
290}
291
Note: See TracBrowser for help on using the repository browser.