source: tags/ms_r16q2/SERVERCNTRL/servercntrl.cxx

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