root/trunk/DBSERVER/db_server.cxx

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