source: tags/svn.1.5.4/DBSERVER/db_server.cxx

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