ARB
db_server.cxx
Go to the documentation of this file.
1 // ============================================================= //
2 // //
3 // File : db_server.cxx //
4 // Purpose : CL ARB database server //
5 // //
6 // Institute of Microbiology (Technical University Munich) //
7 // http://www.arb-home.de/ //
8 // //
9 // ============================================================= //
10 
11 #include <arbdb.h>
12 #include <ad_cb.h>
13 #include <servercntrl.h>
14 #include <ut_valgrinded.h>
15 #include <arb_file.h>
16 #include <arb_sleep.h>
17 #include <arb_diff.h>
18 
19 #define TIMEOUT 1000*60*2 // save every 2 minutes
20 #define LOOPS 30 // wait 30*TIMEOUT (1 hour) till shutdown
21 
23  return GB_search(gb_main, "/tmp/dbserver", GB_CREATE_CONTAINER);
24 }
25 inline GBDATA *dbserver_entry(GBDATA *gb_main, const char *entry) {
26  GBDATA *gb_entry = GB_searchOrCreate_string(dbserver_container(gb_main), entry, "<undefined>");
27 #if defined(DEBUG)
28  if (gb_entry) {
29  GBDATA *gb_brother = GB_nextEntry(gb_entry);
30  arb_assert(!gb_brother);
31  }
32 #endif
33  return gb_entry;
34 }
35 
36 inline GBDATA *get_command_entry(GBDATA *gb_main) { return dbserver_entry(gb_main, "cmd"); }
37 inline GBDATA *get_param_entry(GBDATA *gb_main) { return dbserver_entry(gb_main, "param"); }
38 inline GBDATA *get_result_entry(GBDATA *gb_main) { return dbserver_entry(gb_main, "result"); }
39 
42 
43  if (!get_command_entry(gb_main) || !get_param_entry(gb_main) || !get_result_entry(gb_main)) {
44  error = GB_await_error();
45  }
46 
47  return error;
48 }
49 
50 inline bool served(GBDATA *gb_main) {
51  GB_begin_transaction(gb_main);
52  GB_commit_transaction(gb_main);
53  return GBCMS_accept_calls(gb_main, false);
54 }
55 
56 static bool do_shutdown = false;
57 static bool command_triggered = false;
58 
59 static const char *savemode = "b";
60 
61 static void command_cb() {
62  command_triggered = true;
63 }
64 
66  static bool in_reaction = false;
67 
68  if (!in_reaction) {
69  LocallyModify<bool> flag(in_reaction, true);
70 
71  command_triggered = false;
72 
74 
75  GB_begin_transaction(gb_main);
76  GBDATA *gb_cmd_entry = get_command_entry(gb_main);
77  GB_commit_transaction(gb_main);
78 
79  char *command;
80  {
81  GB_transaction ta(gb_main);
82 
83  command = GB_read_string(gb_cmd_entry);
84  if (command[0]) {
85  error = GB_write_string(gb_cmd_entry, "");
86  if (!error) error = GB_write_string(get_result_entry(gb_main), "busy");
87  }
88  }
89 
90  if (command[0]) {
91  if (strcmp(command, "shutdown") == 0) do_shutdown = true;
92  else if (strcmp(command, "ping") == 0) {} // ping is a noop
93  else if (strcmp(command, "save") == 0) {
94  fprintf(stdout, "arb_db_server: save requested (savemode is \"%s\")\n", savemode);
95 
96  char *param = NULp;
97  {
98  GB_transaction ta(gb_main);
99  GBDATA *gb_param = get_param_entry(gb_main);
100  if (!gb_param) error = GB_await_error();
101  else {
102  param = GB_read_string(gb_param);
103  if (!param) error = GB_await_error();
104  else error = GB_write_string(gb_param, "");
105  }
106  }
107 
108  if (!error) {
109  if (param[0]) error = GB_save(gb_main, param, savemode); // @@@ support compression here?
110  else error = "No savename specified";
111  }
112 
113  printf("arb_db_server: save returns '%s'\n", error);
114 
115  free(param);
116  }
117  else {
118  error = GBS_global_string("Unknown command '%s'", command);
119  }
120  }
121  {
122  GB_transaction ta(gb_main);
123 
124  GB_ERROR err2 = GB_write_string(get_result_entry(gb_main), error ? error : "ok");
125  if (!error && err2) error = GBS_global_string("could not write result (reason: %s)", err2);
126  if (error) fprintf(stderr, "arb_db_server: failed to react to command '%s' (reason: %s)\n", command, error);
127  }
128  free(command);
129  }
130 }
131 
133  GB_ERROR error = NULp;
134  {
135  GB_transaction ta(gb_main);
136 
137  GBDATA *cmd_entry = get_command_entry(gb_main);
138  error = GB_add_callback(cmd_entry, GB_CB_CHANGED, makeDatabaseCallback(command_cb));
139  }
140 
141  while (!error) {
142  served(gb_main);
143  if (command_triggered) {
144  react_to_command(gb_main);
145  }
146  if (do_shutdown) {
147  int clients;
148  do {
149  clients = GB_read_clients(gb_main);
150  if (clients>0) {
151  fprintf(stdout, "arb_db_server: shutdown requested (waiting for %i clients)\n", clients);
152  served(gb_main);
153  }
154  }
155  while (clients>0);
156  fputs("arb_db_server: all clients left, performing shutdown\n", stdout);
157  break;
158  }
159  }
160  return error;
161 }
162 
164  GBDATA *gb_extern = GB_open(params.tcp, "rwc");
165  if (gb_extern) {
166  GB_close(gb_extern);
167  return GBS_global_string("socket '%s' already in use", params.tcp);
168  }
169  GB_clear_error();
170  return NULp;
171 }
172 
173 static GB_ERROR run_server(const arb_params& params) {
174  GB_shell shell;
176  if (!error) {
177  printf("Loading '%s'...\n", params.default_file);
178  GBDATA *gb_main = GB_open(params.default_file, "rw");
179  if (!gb_main) error = GBS_global_string("Can't open DB '%s' (reason: %s)", params.default_file, GB_await_error());
180  else {
181  error = GBCMS_open(params.tcp, TIMEOUT, gb_main);
182  if (error) error = GBS_global_string("Error starting server: %s", error);
183  }
184 
185  if (!error) {
186  GB_transaction ta(gb_main);
187  error = init_data(gb_main);
188  }
189  if (!error) {
190  error = server_main_loop(gb_main);
191  fprintf(stderr, "server_main_loop returns with '%s'\n", error);
192  }
193 
194  if (gb_main) {
195  GBCMS_shutdown(gb_main);
196  GB_close(gb_main);
197  }
198  }
199  return error;
200 }
201 
202 static GB_ERROR run_command(const arb_params& params, const char *command) {
203  GB_ERROR error = NULp;
204  GB_shell shell;
205  GBDATA *gb_main = GB_open(params.tcp, "rw");
206  if (!gb_main) error = GB_await_error();
207  else {
208  {
209  GB_transaction ta(gb_main);
210 
211  GBDATA *gb_cmd_entry = get_command_entry(gb_main);
212  error = gb_cmd_entry ? GB_write_string(gb_cmd_entry, command) : GB_await_error();
213 
214  if (!error) {
215  if (strcmp(command, "save") == 0) {
216  GBDATA *gb_param = get_param_entry(gb_main);
217  if (!gb_param) error = GB_await_error();
218  else error = GB_write_string(gb_param, params.default_file); // send save-name
219  }
220  }
221  }
222 
223  if (!error) {
224  GB_transaction ta(gb_main);
225 
226  GBDATA *gb_result_entry = get_result_entry(gb_main);
227  if (!gb_result_entry) error = GB_await_error();
228  else {
229  const char *result = GB_read_char_pntr(gb_result_entry);
230 
231  if (strcmp(result, "ok") != 0) {
232  error = GBS_global_string("Error from server: %s", result);
233  }
234  }
235  }
236 
237  GB_close(gb_main);
238  }
239  return error;
240 }
241 
242 static void show_help() {
243  fputs("arb_db_server 2.0 -- ARB-database server\n", stdout);
244  fputs("options:\n", stdout);
245  fputs(" -h show this help\n", stdout);
246  fputs(" -A use ASCII-DB-version\n", stdout);
247  fputs(" -Ccmd execute command 'cmd' on running server\n", stdout);
248  fputs(" known command are:\n", stdout);
249  fputs(" ping test if server is up (crash or failure if not)\n", stdout);
250  fputs(" save save the database (use -d to change name)\n", stdout);
251  fputs(" shutdown shutdown running arb_db_server\n", stdout);
253 }
254 
255 int ARB_main(int argc, char *argv[]) {
256  arb_params *params = arb_trace_argv(&argc, (const char **)argv);
257 
258  bool help = false;
259  const char *cmd = NULp; // run server command
260 
261  GB_ERROR error = NULp;
262  while (argc>1 && !error) {
263  const char *arg = argv[1];
264  if (arg[0] == '-') {
265  char sw_char = arg[1];
266  switch (sw_char) {
267  case 'h': help = true; break;
268  case 'C': cmd = arg+2; break;
269  case 'A': savemode = "a"; break;
270 
271  default:
272  error = GBS_global_string("Unknown switch '-%c'", sw_char);
273  break;
274  }
275  }
276  else {
277  error = GBS_global_string("Unknown argument '%s'", arg);
278  }
279  argc--; argv++;
280  }
281 
282  if (!error) {
283  if (help) show_help();
284  else if (cmd) error = run_command(*params, cmd);
285  else error = run_server(*params);
286  }
287 
288  free_arb_params(params);
289  if (error) {
290  fprintf(stderr, "Error in arb_db_server: %s\n", error);
291  return EXIT_FAILURE;
292  }
293  return EXIT_SUCCESS;
294 }
295 
296 // --------------------------------------------------------------------------------
297 
298 #ifdef UNIT_TESTS
299 #ifndef TEST_UNIT_H
300 #include <test_unit.h>
301 #endif
302 #include <test_runtool.h>
303 
304 #include <unistd.h>
305 #include <sys/wait.h>
306 
307 
308 inline bool server_is_down(const char *tcp) {
309  char *ping_cmd = ARB_strdup(GBS_global_string("arb_db_server -T%s -Cping", tcp));
310  GB_ERROR error = GBK_system(ping_cmd); // causes a crash in called command (as long as server is not up)
311  free(ping_cmd);
312  return error;
313 }
314 
315 static int entry_changed_cb_called = 0;
316 static void entry_changed_cb() {
317  entry_changed_cb_called++;
318 }
319 
320 void TEST_SLOW_dbserver() {
321  // MISSING_TEST(TEST_dbserver);
322  TEST_RUN_TOOL("arb_db_server -h");
323  TEST_RUN_TOOL_FAILS("arb_db_server -X");
324  TEST_RUN_TOOL_FAILS("arb_db_server brzl");
325 
326  char *sock = ARB_strdup(GB_path_in_ARBHOME("UNIT_TESTER/sok/dbserver.socket"));
327  char *tcp = GBS_global_string_copy(":%s", sock);
328  char *db = ARB_strdup(GB_path_in_ARBHOME("UNIT_TESTER/run/TEST_loadsave.arb"));
329 
330  char *shutdown_cmd = ARB_strdup(GBS_global_string("arb_db_server -T%s -Cshutdown", tcp));
331 
332 // #define DEBUG_SERVER // uncomment when debugging server manually
333 
334 #if !defined(DEBUG_SERVER)
335  TEST_EXPECT(server_is_down(tcp));
336 #endif
337 
338  pid_t child_pid = fork();
339  if (child_pid) { // parent ("the client")
340  bool down = true;
341  int max_wait = (60*1000)/25; // set timeout to ~60 seconds
342  while (down) {
343  ARB_sleep(25, MS);
344  down = server_is_down(tcp);
345  TEST_EXPECT(max_wait-->0);
346  }
347  // ok - server is up
348 
349  {
350  char *bad_cmd = ARB_strdup(GBS_global_string("arb_db_server -T%s -Cbad", tcp));
351  TEST_RUN_TOOL_FAILS(bad_cmd);
352  free(bad_cmd);
353  }
354 
355  { // now directly connect to server
356  GB_shell shell;
357  GB_ERROR error = NULp;
358  GBDATA *gb_main1 = GB_open(tcp, "rw"); // first client
359  if (!gb_main1) error = GB_await_error();
360  else {
361  GBDATA *gb_main2 = GB_open(tcp, "rw"); // second client
362  if (!gb_main2) error = GB_await_error();
363  else {
364  // test existing entries
365  {
366  GB_transaction ta(gb_main1);
367  GBDATA *gb_ecoli = GB_search(gb_main1, "/extended_data/extended/ali_16s/data", GB_FIND);
368 
369  TEST_REJECT_NULL(gb_ecoli);
370 
371  const char *ecoli = GB_read_char_pntr(gb_ecoli);
372  if (!ecoli) error = GB_await_error();
373  else TEST_EXPECT_EQUAL(GBS_checksum(ecoli, 0, NULp), 0x3558760cU);
374  }
375  {
376  GB_transaction ta(gb_main2);
377  GBDATA *gb_alitype = GB_search(gb_main2, "/presets/alignment/alignment_type", GB_FIND);
378 
379  TEST_REJECT_NULL(gb_alitype);
380 
381  const char *alitype = GB_read_char_pntr(gb_alitype);
382  if (!alitype) error = GB_await_error();
383  else TEST_EXPECT_EQUAL(alitype, "rna");
384  }
385 
386  // test value written in client1 is changed in client2
387  const char *test_entry = "/tmp/TEST_SLOW_dbserver";
388  const char *test_content = "hello world!";
389 
390  GBDATA *gb_entry1;
391  GBDATA *gb_entry2;
392 
393  if (!error) {
394  GB_transaction ta(gb_main1);
395 
396  gb_entry1 = GB_search(gb_main1, test_entry, GB_STRING);
397  if (!gb_entry1) error = GB_await_error();
398  else error = GB_write_string(gb_entry1, test_content);
399  }
400 
401  if (!error) { // test value arrived in other client
402  GB_transaction ta(gb_main2);
403 
404  gb_entry2 = GB_search(gb_main2, test_entry, GB_STRING);
405  if (!gb_entry2) error = GB_await_error();
406  else {
407  const char *read_content = GB_read_char_pntr(gb_entry2);
408  if (!read_content) {
409  error = GB_await_error();
410  }
411  else {
412  TEST_EXPECT_EQUAL(read_content, test_content);
413  }
414  }
415  }
416 
417  // test change-callback gets triggered
418  if (!error) {
419 
420  TEST_EXPECT_EQUAL(entry_changed_cb_called, 0);
421  {
422  GB_transaction ta(gb_entry1);
423  error = GB_add_callback(gb_entry1, GB_CB_CHANGED, makeDatabaseCallback(entry_changed_cb));
424  }
425  TEST_EXPECT_EQUAL(entry_changed_cb_called, 0);
426  {
427  GB_transaction ta(gb_entry2);
428  GB_touch(gb_entry2);
429  }
430  TEST_EXPECT_EQUAL(entry_changed_cb_called, 0); // client1 did not transact yet
431  delete new GB_transaction(gb_main1);
432  TEST_EXPECT_EQUAL(entry_changed_cb_called, 1);
433  }
434 
435  GB_close(gb_main2);
436  }
437  GB_close(gb_main1);
438  }
439 
440  TEST_EXPECT_NO_ERROR(error);
441  }
442 
443  // test remote save
444  {
445  char *savename = ARB_strdup(GB_path_in_ARBHOME("UNIT_TESTER/run/TEST_arbdbserver_save.arb"));
446  char *expected = ARB_strdup(GB_path_in_ARBHOME("UNIT_TESTER/run/TEST_arbdbserver_save_expected.arb"));
447  char *save_cmd = ARB_strdup(GBS_global_string("arb_db_server -T%s -Csave -d%s", tcp, savename));
448  char *bad_savecmd = ARB_strdup(GBS_global_string("arb_db_server -T%s -Csave", tcp));
449 
450  TEST_RUN_TOOL(save_cmd);
451  TEST_EXPECT(GB_is_regularfile(savename));
452 
453 // #define TEST_AUTO_UPDATE
454 #if defined(TEST_AUTO_UPDATE)
455  TEST_COPY_FILE(savename, expected);
456 #else // !defined(TEST_AUTO_UPDATE)
457  TEST_EXPECT_FILES_EQUAL(savename, expected);
458 #endif
460 
461  TEST_RUN_TOOL_FAILS(bad_savecmd);
462 
463  free(bad_savecmd);
464  free(save_cmd);
465  free(expected);
466  free(savename);
467  }
468 
469  // stop the server
470  TEST_RUN_TOOL(shutdown_cmd);
471  while (child_pid != wait(NULp)) {} // wait for child to finish = wait for server to terminate
472  }
473  else { // child ("the server")
474 #if !defined(DEBUG_SERVER)
475  ARB_sleep(100, MS);
476  TEST_RUN_TOOL(GBS_global_string("arb_db_server -T%s -d%s -A", tcp, db)); // start the server (in ASCII-mode)
477 #endif
478  exit(EXIT_SUCCESS);
479  }
480 
481  TEST_EXPECT(server_is_down(tcp));
482 
483 #define socket_disappeared(sock) (GB_time_of_file(sock) == 0)
484 
485  TEST_EXPECT(socket_disappeared(sock));
486 
487  free(shutdown_cmd);
488  free(db);
489  free(tcp);
490  free(sock);
491 }
492 
493 #endif // UNIT_TESTS
494 
495 // --------------------------------------------------------------------------------
GB_ERROR GB_begin_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2528
GB_ERROR GBK_system(const char *system_command)
Definition: arb_msg.cxx:571
#define arb_assert(cond)
Definition: arb_assert.h:245
const char * GB_ERROR
Definition: arb_core.h:25
string result
GBDATA * GB_open(const char *path, const char *opent)
Definition: ad_load.cxx:1363
GB_ERROR GB_commit_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2551
static void show_help()
Definition: db_server.cxx:242
GB_ERROR GB_save(GBDATA *gb, const char *path, const char *savetype)
GB_ERROR GB_write_string(GBDATA *gbd, const char *s)
Definition: arbdb.cxx:1387
#define TIMEOUT
Definition: db_server.cxx:19
GBDATA * GB_searchOrCreate_string(GBDATA *gb_container, const char *fieldpath, const char *default_value)
Definition: adquery.cxx:546
GB_ERROR GB_add_callback(GBDATA *gbd, GB_CB_TYPE type, const DatabaseCallback &dbcb)
Definition: ad_cb.cxx:356
GBDATA * GB_nextEntry(GBDATA *entry)
Definition: adquery.cxx:339
bool GBCMS_accept_calls(GBDATA *gbd, bool wait_extra_time)
Definition: adcomm.cxx:1188
static void help()
GBDATA * get_result_entry(GBDATA *gb_main)
Definition: db_server.cxx:38
static GB_ERROR run_command(const arb_params &params, const char *command)
Definition: db_server.cxx:202
GBDATA * get_command_entry(GBDATA *gb_main)
Definition: db_server.cxx:36
static GB_ERROR check_socket_available(const arb_params &params)
Definition: db_server.cxx:163
char * ARB_strdup(const char *str)
Definition: arb_string.h:27
GBDATA * dbserver_container(GBDATA *gb_main)
Definition: db_server.cxx:22
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:203
static const char * savemode
Definition: db_server.cxx:59
#define EXIT_SUCCESS
Definition: arb_a2ps.c:154
int GB_unlink(const char *path)
Definition: arb_file.cxx:188
void GBCMS_shutdown(GBDATA *gbd)
Definition: adcomm.cxx:247
arb_params * arb_trace_argv(int *argc, const char **argv)
static void command_cb()
Definition: db_server.cxx:61
void free_arb_params(arb_params *params)
GB_ERROR GB_await_error()
Definition: arb_msg.cxx:342
int ARB_main(int argc, char *argv[])
Definition: db_server.cxx:255
#define TEST_EXPECT(cond)
Definition: test_unit.h:1328
void arb_print_server_params()
static void react_to_command(GBDATA *gb_main)
Definition: db_server.cxx:65
static bool command_triggered
Definition: db_server.cxx:57
#define TEST_RUN_TOOL(cmdline)
Definition: test_runtool.h:109
void GB_clear_error()
Definition: arb_msg.cxx:354
static GB_ERROR run_server(const arb_params &params)
Definition: db_server.cxx:173
#define TEST_REJECT_NULL(n)
Definition: test_unit.h:1325
static void error(const char *msg)
Definition: mkptypes.cxx:96
char * tcp
Definition: servercntrl.h:30
GB_CSTR GB_path_in_ARBHOME(const char *relative_path)
Definition: adsocket.cxx:1149
static GB_ERROR server_main_loop(GBDATA *gb_main)
Definition: db_server.cxx:132
long GB_read_clients(GBDATA *gbd)
Definition: adcomm.cxx:1682
#define TEST_EXPECT_ZERO_OR_SHOW_ERRNO(iocond)
Definition: test_unit.h:1090
GB_ERROR GBCMS_open(const char *path, long timeout, GBDATA *gb_main)
Definition: adcomm.cxx:198
void ARB_sleep(int amount, TimeUnit tu)
Definition: arb_sleep.h:32
Definition: arbdb.h:86
#define EXIT_FAILURE
Definition: arb_a2ps.c:157
long int flag
Definition: f2c.h:39
fputs(TRACE_PREFIX, stderr)
GBDATA * get_param_entry(GBDATA *gb_main)
Definition: db_server.cxx:37
Definition: arb_sleep.h:30
void GB_touch(GBDATA *gbd)
Definition: arbdb.cxx:2802
#define TEST_EXPECT_FILES_EQUAL(f1, f2)
Definition: test_unit.h:1422
char * GB_read_string(GBDATA *gbd)
Definition: arbdb.cxx:909
GBDATA * dbserver_entry(GBDATA *gb_main, const char *entry)
Definition: db_server.cxx:25
uint32_t GBS_checksum(const char *seq, int ignore_case, const char *exclude)
Definition: adstring.cxx:352
#define TEST_EXPECT_NO_ERROR(call)
Definition: test_unit.h:1118
#define NULp
Definition: cxxforward.h:116
bool GB_is_regularfile(const char *path)
Definition: arb_file.cxx:76
static bool do_shutdown
Definition: db_server.cxx:56
GB_ERROR init_data(GBDATA *gb_main)
Definition: db_server.cxx:40
#define TEST_RUN_TOOL_FAILS(cmdline)
Definition: test_runtool.h:111
static char * command
Definition: arb_a2ps.c:319
char * default_file
Definition: servercntrl.h:19
GB_transaction ta(gb_var)
GB_CSTR GB_read_char_pntr(GBDATA *gbd)
Definition: arbdb.cxx:904
GBDATA * gb_main
Definition: adname.cxx:32
GBDATA * GB_search(GBDATA *gbd, const char *fieldpath, GB_TYPES create)
Definition: adquery.cxx:531
#define TEST_EXPECT_EQUAL(expr, want)
Definition: test_unit.h:1294
bool served(GBDATA *gb_main)
Definition: db_server.cxx:50
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:194
void GB_close(GBDATA *gbd)
Definition: arbdb.cxx:655