ARB
dbserver.cxx
Go to the documentation of this file.
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 
47 struct db_interrupt_data : virtual Noncopyable {
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) {
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
71 
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
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));
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 
218  if (error) fprintf(stderr, "Error in check_for_remote_command: %s\n", error);
219  return error;
220 }
221 
222 inline bool remote_command_handler(AW_root *awr, const db_interrupt_data& dib) {
223  // returns false in case of errors
225 
226  bool ok = true;
228  if (error) {
229  aw_message(error);
230  ok = false;
231  }
232  return ok;
233 }
234 
235 static 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 
243 static 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 
251 
252 GB_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 
292 GB_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 
#define arb_assert(cond)
Definition: arb_assert.h:245
static unsigned serve_db_interrupt(AW_root *awr, db_interrupt_data *dib)
Definition: dbserver.cxx:235
const char * GB_ERROR
Definition: arb_core.h:25
#define MACRO_TRIGGER_TRACKED
Definition: arbdbt.h:27
#define ARB_CHECK_DB_TIMER
Definition: dbserver.cxx:45
GB_ERROR GB_set_macro_error(GBDATA *gb_main, const char *curr_error)
Definition: adtools.cxx:670
long GB_read_int(GBDATA *gbd)
Definition: arbdb.cxx:729
bool is_tracking() const
Definition: aw_root.hxx:70
bool GB_is_server(GBDATA *gbd)
Definition: adcomm.cxx:1697
const char * granted() const
Definition: ad_remote.h:80
const char * result() const
Definition: ad_remote.h:73
remote_awars remote
Definition: dbserver.cxx:48
static __ATTR__USERESULT GB_ERROR check_for_remote_command(AW_root *aw_root, const db_interrupt_data &dib)
Definition: dbserver.cxx:69
UserActionTracker * get_tracker()
Definition: aw_root.hxx:180
GB_ERROR GB_end_transaction(GBDATA *gbd, GB_ERROR error)
Definition: arbdb.cxx:2561
bool GBCMS_accept_calls(GBDATA *gbd, bool wait_extra_time)
Definition: adcomm.cxx:1188
GB_ERROR reconfigure(GBDATA *gb_main_, const char *application_id)
Definition: dbserver.cxx:57
const char * authAck() const
Definition: ad_remote.h:79
NOT4PERL long * GBT_readOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value)
Definition: adtools.cxx:402
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:203
bool remote_command_handler(AW_root *awr, const db_interrupt_data &dib)
Definition: dbserver.cxx:222
bool GB_have_error()
Definition: arb_msg.cxx:338
void add_timed_callback(int ms, const TimedCallback &tcb)
Definition: AW_root.cxx:538
AW_cb * search_remote_command(const char *action)
Definition: AW_root.cxx:247
const char * value() const
Definition: ad_remote.h:75
#define ARB_SERVE_DB_TIMER
Definition: dbserver.cxx:44
const char * authReq() const
Definition: ad_remote.h:78
GB_ERROR GB_push_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2494
static bool initialized
Definition: AW_advice.cxx:36
GB_ERROR reconfigure_dbserver(const char *application_id, GBDATA *gb_main)
Definition: dbserver.cxx:292
static db_interrupt_data * idle_interrupt
Definition: dbserver.cxx:250
GBDATA * gb_main
Definition: dbserver.cxx:49
void touch()
Definition: AW_awar.cxx:207
static void error(const char *msg)
Definition: mkptypes.cxx:96
char * GBT_readOrCreate_string(GBDATA *gb_container, const char *fieldpath, const char *default_value)
Definition: adtools.cxx:371
void GB_set_remote_action(GBDATA *gbd, bool in_action)
Definition: adcomm.cxx:1656
GBDATA * GB_searchOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value)
Definition: adquery.cxx:569
char * read_as_string() const
db_interrupt_data(GBDATA *gb_main_, const char *application_id)
Definition: dbserver.cxx:51
long GB_read_clients(GBDATA *gbd)
Definition: adcomm.cxx:1682
GB_ERROR GBCMS_open(const char *path, long timeout, GBDATA *gb_main)
Definition: adcomm.cxx:198
AW_awar * awar_no_error(const char *awar)
end timer stuff
Definition: AW_root.cxx:549
AW_awar * awar(const char *awar)
Definition: AW_root.cxx:554
GB_ERROR GB_pop_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2524
GB_ERROR startup_dbserver(AW_root *aw_root, const char *application_id, GBDATA *gb_main)
Definition: dbserver.cxx:252
GB_ERROR GB_write_int(GBDATA *gbd, long i)
Definition: arbdb.cxx:1250
const char * appID() const
Definition: ad_remote.h:84
bool got_macro_ability(AW_root *aw_root)
Definition: trackers.cxx:480
GB_ERROR write_as_string(const char *aw_string)
const char * awar() const
Definition: ad_remote.h:74
#define IF_DUMP_AUTH(cmd)
Definition: dbserver.cxx:39
#define IF_DUMP_ACTION(cmd)
Definition: dbserver.cxx:33
GB_ERROR GBT_write_string(GBDATA *gb_container, const char *fieldpath, const char *content)
Definition: adtools.cxx:451
GB_ERROR handle_tracked_client_action(char *&tracked)
Definition: trackers.cxx:248
#define __ATTR__USERESULT
Definition: attributes.h:58
static unsigned check_db_interrupt(AW_root *awr, db_interrupt_data *dib)
Definition: dbserver.cxx:243
void aw_message(const char *msg)
Definition: AW_status.cxx:1142
GB_ERROR GB_clear_macro_error(GBDATA *gb_main)
Definition: adtools.cxx:699
#define NULp
Definition: cxxforward.h:116
GB_ERROR stop_recording()
Definition: trackers.cxx:91
void run_callbacks()
GBDATA * gb_main
Definition: adname.cxx:32
GB_ERROR GB_get_macro_error(GBDATA *gb_main)
Definition: adtools.cxx:687
const char * action() const
Definition: ad_remote.h:72
void aw_message_if(GB_ERROR error)
Definition: aw_msg.hxx:21