source: trunk/SERVERCNTRL/servercntrl.cxx

Last change on this file was 19297, checked in by westram, 2 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1// ============================================================= //
2//                                                               //
3//   File      : servercntrl.cxx                                 //
4//   Purpose   :                                                 //
5//                                                               //
6//   Institute of Microbiology (Technical University Munich)     //
7//   http://www.arb-home.de/                                     //
8//                                                               //
9// ============================================================= //
10
11#include <servercntrl.h>
12
13#include <client_privat.h>
14#include <client.h>
15
16#include <arbdb.h>
17#include <arb_file.h>
18#include <arb_sleep.h>
19#include <ut_valgrinded.h>
20
21/* The following lines go to servercntrl.h
22 * edit here, not there!!
23 * call 'make proto' to update
24 */
25
26// AISC_MKPT_PROMOTE:#ifndef ARBDB_BASE_H
27// AISC_MKPT_PROMOTE:#include <arbdb_base.h>
28// AISC_MKPT_PROMOTE:#endif
29// AISC_MKPT_PROMOTE:
30// AISC_MKPT_PROMOTE:struct arb_params {
31// AISC_MKPT_PROMOTE:    char *species_name;
32// AISC_MKPT_PROMOTE:    char *extended_name;
33// AISC_MKPT_PROMOTE:    char *alignment;
34// AISC_MKPT_PROMOTE:    char *default_file;
35// AISC_MKPT_PROMOTE:    char *field;
36// AISC_MKPT_PROMOTE:    const char *field_default;
37// AISC_MKPT_PROMOTE:
38// AISC_MKPT_PROMOTE:    int  read_only;
39// AISC_MKPT_PROMOTE:
40// AISC_MKPT_PROMOTE:    char *job_server;
41// AISC_MKPT_PROMOTE:    char *db_server;
42// AISC_MKPT_PROMOTE:    char *mgr_server;
43// AISC_MKPT_PROMOTE:    char *pt_server;
44// AISC_MKPT_PROMOTE:
45// AISC_MKPT_PROMOTE:    char *tcp;
46// AISC_MKPT_PROMOTE:};
47// AISC_MKPT_PROMOTE:
48// AISC_MKPT_PROMOTE:enum SpawnMode {
49// AISC_MKPT_PROMOTE:    WAIT_FOR_TERMINATION,
50// AISC_MKPT_PROMOTE:    SPAWN_ASYNCHRONOUS,
51// AISC_MKPT_PROMOTE:    SPAWN_DAEMONIZED,
52// AISC_MKPT_PROMOTE:};
53
54#if defined(AISC_MAGIC_NUMBER)
55# undef AISC_MAGIC_NUMBER
56#endif
57
58
59#define TRIES 1
60
61static struct gl_struct {
62    aisc_com   *link;
63    AISC_Object  com;
64
65    gl_struct()
66        : link(NULp), com(0x10000*1) // faked type id (matches main object, e.g. AN_MAIN or PT_MAIN)
67    { }
68} glservercntrl;
69
70
71inline void make_async_call(char*& command) {
72    freeset(command, GBS_global_string_copy("( %s ) &", command));
73}
74
75char *createCallOnSocketHost(const char *host, const char *remotePrefix, const char *command, SpawnMode spawnmode, const char *logfile) {
76    /*! transforms a shell-command
77     * - use ssh if host is not local
78     * - add wrappers for detached execution
79     * @param host          socket specification (may be 'host:port', 'host' or ':portOrSocketfile')
80     * @param remotePrefix  prefixed to command if it is executed on remote host (e.g. "$ARBHOME/bin/" -> uses value of environment variable ARBHOME on remote host!)
81     * @param command       the shell command to execute
82     * @param spawnmode     how to spawn:
83     *      WAIT_FOR_TERMINATION = run "command"
84     *      SPAWN_ASYNCHRONOUS = run "( command ) &"
85     *      SPAWN_DAEMONIZED   = do not forward kill signals, remove from joblist and redirect output to logfile
86     * @param logfile       use with SPAWN_DAEMONIZED (mandatory; NULp otherwise)
87     * @return a SSH system call for remote hosts and direct system calls for the local machine
88     */
89
90    arb_assert((remotePrefix[0] == 0) || (strchr(remotePrefix, 0)[-1] == '/'));
91    arb_assert(correlated(spawnmode == SPAWN_DAEMONIZED, logfile));
92
93    char *call = NULp;
94    if (host && host[0]) {
95        const char *hostPort = strchr(host, ':');
96        char       *hostOnly = ARB_strpartdup(host, hostPort ? hostPort-1 : NULp);
97
98        if (hostOnly[0] && !GB_host_is_local(hostOnly)) {
99            char *quotedRemoteCommand = GBK_singlequote(GBS_global_string("%s%s", remotePrefix, command));
100            call                      = GBS_global_string_copy("ssh %s -n %s", hostOnly, quotedRemoteCommand);
101            free(quotedRemoteCommand);
102        }
103        free(hostOnly);
104    }
105
106    if (!call) {
107        call = ARB_strdup(command);
108        make_valgrinded_call(call); // only on local host
109    }
110
111    switch (spawnmode) {
112        case WAIT_FOR_TERMINATION:
113            break;
114
115        case SPAWN_ASYNCHRONOUS:
116            make_async_call(call);
117            break;
118
119        case SPAWN_DAEMONIZED: {
120            char *quotedLogfile         = GBK_singlequote(logfile);
121            char *quotedDaemonizingCall = GBK_singlequote(GBS_global_string("nohup %s >> %s 2>&1 & disown", call, quotedLogfile));
122
123            freeset(call, GBS_global_string_copy("bash -c %s", quotedDaemonizingCall));
124
125            free(quotedDaemonizingCall);
126            free(quotedLogfile);
127            break;
128        }
129    }
130
131    return call;
132}
133
134GB_ERROR arb_start_server(const char *arb_tcp_env, int do_sleep) {
135    const char *tcp_id;
136    GB_ERROR    error = NULp;
137
138    if (!(tcp_id = GBS_read_arb_tcp(arb_tcp_env))) {
139        error = GB_await_error();
140    }
141    else {
142        const char *server       = strchr(tcp_id, 0) + 1;
143        char       *serverparams = NULp;
144
145        /* concatenate all params behind server
146           Note :  changed behavior on 2007/Mar/09 -- ralf
147           serverparams now is one space if nothing defined in arb_tcp.dat
148           (previously was same as 'server' - most likely a bug)
149        */
150        {
151            const char *param  = strchr(server, 0)+1;
152            size_t      plen   = strlen(param);
153            size_t      alllen = 0;
154
155            while (plen) {
156                param  += plen+1;
157                alllen += plen+1;
158                plen    = strlen(param);
159            }
160
161            ARB_alloc(serverparams, alllen+1);
162            {
163                char *sp = serverparams;
164
165                param = strchr(server, 0)+1;
166                plen  = strlen(param);
167                if (!plen) sp++;
168                else do {
169                    memcpy(sp, param, plen);
170                    sp[plen]  = ' ';
171                    sp       += plen+1;
172                    param    += plen+1;
173                    plen      = strlen(param);
174                } while (plen);
175                sp[-1] = 0;
176            }
177        }
178
179        {
180            char       *command = NULp;
181            const char *port    = strchr(tcp_id, ':');
182
183            if (!port) {
184                error = GB_export_errorf("Error: Missing ':' in socket definition of '%s' in file $(ARBHOME)/lib/arb_tcp.dat", arb_tcp_env);
185            }
186            else {
187                // When arb is called from arb_launcher, ARB_SERVER_LOG gets set to the name of a logfile.
188                // If ARB_SERVER_LOG is set here -> start servers daemonized here (see #492 for motivation)
189
190                const char *serverlog    = GB_getenv("ARB_SERVER_LOG");
191                SpawnMode   spawnmode    = serverlog ? SPAWN_DAEMONIZED : SPAWN_ASYNCHRONOUS;
192                char       *plainCommand = GBS_global_string_copy("%s %s '-T%s'", server, serverparams, port); // Note: quotes around -T added for testing only (remove@will)
193                command                  = createCallOnSocketHost(tcp_id, "$ARBHOME/bin/", plainCommand, spawnmode, serverlog);
194                free(plainCommand);
195            }
196
197            if (!error) {
198                error = GBK_system(command);
199                if (do_sleep) ARB_sleep(5, SEC);
200            }
201            free(command);
202        }
203        free(serverparams);
204    }
205    return error;
206}
207
208static GB_ERROR arb_wait_for_server(const char *arb_tcp_env, const char *tcp_id, int magic_number, struct gl_struct *serverctrl, int wait) {
209    GB_ERROR error   = NULp;
210    serverctrl->link = aisc_open(tcp_id, serverctrl->com, magic_number, &error);
211
212    if (!error && !serverctrl->link) { // no server running -> start one
213        error = arb_start_server(arb_tcp_env, 0);
214        while (!error && !serverctrl->link && wait) {
215            ARB_sleep(1, SEC);
216            wait--;
217            if ((wait%10) == 0 && wait>0) {
218                printf("Waiting for server '%s' to come up (%i seconds left)\n", arb_tcp_env, wait);
219            }
220            serverctrl->link  = aisc_open(tcp_id, serverctrl->com, magic_number, &error);
221        }
222    }
223
224    return error;
225}
226
227static GB_ERROR check_server_selected(const char *arb_tcp_env) {
228    // check whether arb_tcp_env (normally created by GBS_ptserver_tag for pt-servers)
229    // indicates that no pt-server has been selected by user.
230    //
231    // if so -> provide simple error to reduce confusion.
232
233    if (strcmp(arb_tcp_env, "ARB_PT_SERVER-1") == 0) {
234        return "No pt-server has been selected.";
235    }
236    return NULp;
237}
238
239GB_ERROR arb_look_and_start_server(long magic_number, const char *arb_tcp_env) {
240    arb_assert(!GB_have_error());
241
242    GB_ERROR error = check_server_selected(arb_tcp_env);
243    if (!error) {
244        const char *tcp_id      = GBS_read_arb_tcp(arb_tcp_env);
245        const char *arb_tcp_dat = "$(ARBHOME)/lib/arb_tcp.dat";
246
247        if (!tcp_id) {
248            error = GBS_global_string("Entry '%s' not found in %s (%s)", arb_tcp_env, arb_tcp_dat, GB_await_error());
249        }
250        else {
251            const char *file = GBS_scan_arb_tcp_param(tcp_id, "-d"); // find parameter behind '-d'
252
253            if (!file) {
254                error = GBS_global_string("Parameter -d missing for entry '%s' in %s", arb_tcp_env, arb_tcp_dat);
255            }
256            else {
257                if (strcmp(file, "!ASSUME_RUNNING") == 0) {
258                    // assume pt-server is running on a host,  w/o access to common network drive
259                    // i.e. we cannot check for the existence of the database file
260                }
261                else if (GB_size_of_file(file) <= 0) {
262                    if (strncmp(arb_tcp_env, "ARB_NAME_SERVER", 15) == 0) {
263                        char *dir       = ARB_strdup(file);
264                        char *lastSlash = strrchr(dir, '/');
265
266                        if (lastSlash) {
267                            lastSlash[0] = 0; // cut off file
268                            {
269                                const char *copy_cmd = GBS_global_string("cp %s/names.dat.template %s", dir, file);
270                                error                = GBK_system(copy_cmd);
271                            }
272                            if (!error && GB_size_of_file(file) <= 0) {
273                                error = GBS_global_string("Cannot copy nameserver template (%s/names.dat.template missing?)", dir);
274                            }
275                        }
276                        else {
277                            error = GBS_global_string("Can't determine directory from '%s'", dir);
278                        }
279                        free(dir);
280                    }
281                    else if (strncmp(arb_tcp_env, "ARB_PT_SERVER", 13) == 0) {
282                        const char *nameOnly    = strrchr(file, '/');
283                        if (!nameOnly) nameOnly = file;
284
285                        error = GBS_global_string("PT_server '%s' has not been created yet.\n"
286                                                  " To create it follow these steps:\n"
287                                                  " 1. Start ARB on the whole database you want to use for probe match/design\n"
288                                                  " 2. Go to ARB_NTREE/Probes/PT_SERVER Admin\n"
289                                                  " 3. Select '%s' and press BUILD SERVER\n"
290                                                  " 4. Wait (up to hours, depending on your DB size)\n"
291                                                  " 5. Meanwhile read the help file: PT_SERVER: What Why and How",
292                                                  file, nameOnly);
293                    }
294                    else {
295                        error = GBS_global_string("The file '%s' is missing. \nUnable to start %s", file, arb_tcp_env);
296                    }
297                }
298            }
299
300            if (!error) {
301                error = arb_wait_for_server(arb_tcp_env, tcp_id, magic_number, &glservercntrl, 20);
302
303                if (!error) {
304                    if (!glservercntrl.link) { // couldn't start server
305                        error =                                                            // |
306                            "ARB has problems to start a server! Possible reasons may be one\n"
307                            "or several of the following list:\n"
308                            "- the tcp_id (socket number) is already used by another program\n"
309                            "  (doesnt apply to user-specific PTSERVERs; check $ARBHOME/lib/arb_tcp.dat versus /etc/services)\n"
310                            "- the server exited with error or has crashed.\n"
311                            "  In case of PTSERVER, the failure might be caused by:\n"
312                            "  - missing database in $ARBHOME/lib/pts/* (solution: update ptserver database)\n"
313                            "  - wrong permissions of $ARBHOME/lib/pts/* (no read access)\n"
314                            "  If you recently installed a new arb version, arb will continue\n"
315                            "  to use your previous 'arb_tcp.dat', which might be out-of-date.\n"
316                            "  Backup and remove it, then restart ARB. If it works now,\n"
317                            "  compare your old 'arb_tcp.dat' with the new one for changes.\n"
318                            "- When using remote servers: login or network problems\n"
319                            ;
320                    }
321                    else {
322                        aisc_close(glservercntrl.link, glservercntrl.com);
323                        glservercntrl.link = NULp;
324                    }
325                }
326            }
327        }
328    }
329
330    arb_assert(!GB_have_error());
331    return error;
332}
333
334GB_ERROR arb_look_and_kill_server(int magic_number, const char *arb_tcp_env) {
335    GB_ERROR error = check_server_selected(arb_tcp_env);
336    if (!error) {
337        const char *tcp_id = GBS_read_arb_tcp(arb_tcp_env);
338        if (!tcp_id) {
339            error = GB_await_error();
340        }
341        else {
342            const char *server = strchr(tcp_id, 0)+1;
343
344            glservercntrl.link = aisc_open(tcp_id, glservercntrl.com, magic_number, &error);
345            if (glservercntrl.link) {
346                aisc_close(glservercntrl.link, glservercntrl.com);
347                glservercntrl.link = NULp;
348
349                error = GBK_system(GBS_global_string("%s -kill -T%s &", server, tcp_id));
350            }
351            else {
352                error = "Server is not running";
353            }
354        }
355    }
356    return error;
357}
358
359const char *arb_look_and_start_ptserver(int magic_number, int ptserver_id, GB_ERROR& error) {
360    // return PT server info string (see GBS_read_arb_tcp for details) // @@@ fix doc
361    // or NULp (in this case 'error' is set)
362
363    const char *result     = NULp;
364    const char *server_tag = GBS_ptserver_tag(ptserver_id);
365
366    error = arb_look_and_start_server(magic_number, server_tag);
367    if (!error) {
368        result             = GBS_read_arb_tcp(server_tag);
369        if (!result) error = GB_await_error();
370    }
371    arb_assert(contradicted(result, error));
372    return result;
373}
374
375
376
377void arb_print_server_params() {
378    printf("General server parameters (some maybe unused by this server):\n"
379           "    -s<name>        sets species name to '<name>'\n"
380           "    -e<name>        sets extended name to '<name>'\n"
381           "    -a<ali>         sets alignment to '<ali>'\n"
382           "    -d<file>        sets default file to '<file>'\n"
383           "    -f<field>=<def> sets DB field to '<field>' (using <def> as default)\n"
384           "    -r              read-only mode\n"
385           "    -D<server>      sets DB-server to '<server>'  [default = ':']\n"
386           "    -J<server>      sets job-server to '<server>' [default = 'ARB_JOB_SERVER']\n"
387           "    -M<server>      sets MGR-server to '<server>' [default = 'ARB_MGR_SERVER']\n"
388           "    -P<server>      sets PT-server to '<server>'  [default = 'ARB_PT_SERVER']\n"
389           "    -T<[host]:port>   sets TCP connection to '<[host]:port>'\n"
390           );
391}
392
393arb_params *arb_trace_argv(int *argc, const char **argv) {
394    arb_params *erg = ARB_calloc<arb_params>(1);
395
396    erg->db_server  = ARB_strdup(":");
397    erg->job_server = ARB_strdup("ARB_JOB_SERVER");
398    erg->mgr_server = ARB_strdup("ARB_MGR_SERVER");
399    erg->pt_server  = ARB_strdup("ARB_PT_SERVER");
400
401    int s, d;
402    for (s=d=0; s<*argc; s++) {
403        if (argv[s][0] == '-') {
404            switch (argv[s][1]) {
405                case 's': erg->species_name  = ARB_strdup(argv[s]+2); break;
406                case 'e': erg->extended_name = ARB_strdup(argv[s]+2); break;
407                case 'a': erg->alignment     = ARB_strdup(argv[s]+2); break;
408                case 'd': erg->default_file  = ARB_strdup(argv[s]+2); break;
409                case 'f': {
410                    char *eq;
411                    erg->field = ARB_strdup(argv[s]+2);
412
413                    eq = strchr(erg->field, '=');
414                    if (eq) {
415                        erg->field_default = eq+1;
416                        eq[0]              = 0;
417                    }
418                    else {
419                        erg->field_default = NULp; // this is illegal - error handling done in caller
420                    }
421                    break;
422                }
423                case 'r': erg->read_only     = 1; break;
424                case 'J': freedup(erg->job_server, argv[s]+2); break;
425                case 'D': freedup(erg->db_server, argv[s]+2); break;
426                case 'M': freedup(erg->mgr_server, argv[s]+2); break;
427                case 'P': freedup(erg->pt_server, argv[s]+2); break;
428                case 'T': {
429                    const char *ipport = argv[s]+2;
430                    if (ipport[0] == ':' &&
431                        ipport[1] >= '0' && ipport[1] <= '9') { // port only -> assume localhost
432                        erg->tcp = GBS_global_string_copy("localhost%s", ipport);
433                    }
434                    else {
435                        erg->tcp = ARB_strdup(ipport);
436                    }
437                    break;
438                }
439                default:
440                    argv[d++] = argv[s];
441                    break;
442            }
443        }
444        else {
445            argv[d++] = argv[s];
446        }
447    }
448    *argc = d;
449    return erg;
450}
451
452void free_arb_params(arb_params *params) {
453    free(params->species_name);
454    free(params->extended_name);
455    free(params->alignment);
456    free(params->default_file);
457    free(params->field);
458    free(params->job_server);
459    free(params->db_server);
460    free(params->mgr_server);
461    free(params->pt_server);
462    free(params->tcp);
463
464    free(params);
465}
466
467// --------------------------------------------------------------------------------
468
469#if defined(UNIT_TESTS) && 0
470
471// If you need tests in AISC_COM/C/client.c, put them here instead.
472
473#include <test_unit.h>
474
475void TEST_servercntrl() {
476    TEST_REJECT(true); // would fail if test gets activated
477}
478
479#endif // UNIT_TESTS
480
Note: See TracBrowser for help on using the repository browser.