source: tags/ms_r18q1/SERVERCNTRL/servercntrl.cxx

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