source: trunk/ARBDB/adtcp.cxx

Last change on this file was 19245, 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: 17.6 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adtcp.cxx                                         //
4//   Purpose   : arb_tcp.dat handling                              //
5//                                                                 //
6//   Coded by Ralf Westram (coder@reallysoft.de) in April 2007     //
7//   Institute of Microbiology (Technical University Munich)       //
8//   http://www.arb-home.de/                                       //
9//                                                                 //
10// =============================================================== //
11
12#include <ctime>
13#include <sys/stat.h>
14
15#include <arbdbt.h>
16#include <arb_str.h>
17
18#include "gb_local.h"
19
20#if defined(DEBUG)
21// #define DUMP_ATD_ACCESS
22#endif // DEBUG
23
24// ------------------------------------------------------------
25// Data representing current content of arb_tcp.dat
26
27class ArbTcpDat : virtual Noncopyable {
28    GB_ULONG  modtime;                              // modification time of read-in arb_tcp.dat
29    char     *filename;                             // pathname of loaded arb_tcp.dat
30
31    char **content; /* zero-pointer terminated array of multi-separated strings
32                     * (strings have same format as the result of
33                     * GBS_read_arb_tcp(), but also contain the first element,
34                     * i.e. the server id)
35                     */
36    int serverCount;
37
38
39    void freeContent() {
40        if (content) {
41            for (int c = 0; content[c]; c++) free(content[c]);
42            freenull(content);
43        }
44    }
45    GB_ERROR read(int *versionFound);
46
47public:
48    ArbTcpDat() : modtime(-1) , filename(NULp) , content(NULp), serverCount(-1) { }
49
50    ~ArbTcpDat() {
51        free(filename);
52        freeContent();
53    }
54
55    GB_ERROR update();
56
57    int get_server_count() const { return serverCount; }
58    const char *get_serverID(int idx) const { gb_assert(idx >= 0 && idx<serverCount); return content[idx]; }
59    const char *get_entry(const char *serverID) const;
60    const char *get_filename() const { return filename; }
61
62#if defined(DUMP_ATD_ACCESS)
63    void dump();
64#endif
65};
66
67const char *ArbTcpDat::get_entry(const char *serverID) const {
68    const char *result = NULp;
69    if (content) {
70        int c;
71        for (c = 0; content[c]; c++) {
72            const char *id = content[c];
73
74            if (strcmp(id, serverID) == 0) {
75                result = strchr(id, 0)+1; // return pointer to first parameter
76                break;
77            }
78        }
79    }
80    return result;
81}
82
83#if defined(DUMP_ATD_ACCESS)
84void ArbTcpDat::dump() {
85    fprintf(stderr, "filename='%s'\n", filename);
86    fprintf(stderr, "modtime='%lu'\n", modtime);
87    if (content) {
88        int c;
89        for (c = 0; content[c]; c++) {
90            char *data = content[c];
91            char *tok  = data;
92            fprintf(stderr, "Entry #%i:\n", c);
93
94            while (tok[0]) {
95                fprintf(stderr, "- '%s'\n", tok);
96                tok = strchr(tok, 0)+1;
97            }
98        }
99    }
100    else {
101        fprintf(stderr, "No content\n");
102    }
103}
104#endif // DUMP_ATD_ACCESS
105
106#define MAXLINELEN 512
107#define MAXTOKENS  10
108
109GB_ERROR ArbTcpDat::read(int *versionFound) {
110    // used to read arb_tcp.dat or arb_tcp_org.dat
111    GB_ERROR  error = NULp;
112    FILE     *in    = fopen(filename, "rt");
113
114    *versionFound = 1; // default to version 1 (old format)
115
116#if defined(DUMP_ATD_ACCESS)
117    fprintf(stderr, "(re)reading %s\n", filename);
118#endif // DUMP_ATD_ACCESS
119
120    freeContent();
121
122    if (!in) {
123        error = GBS_global_string("Can't read '%s'", filename);
124    }
125    else {
126        char   buffer[MAXLINELEN+1];
127        char  *lp;
128        int    lineNumber = 0;
129        char **tokens     = ARB_alloc<char*>(MAXTOKENS);
130
131        int    entries_allocated = 30;
132        int    entries           = 0;
133        char **entry             = ARB_alloc<char*>(entries_allocated);
134
135        if (!tokens || !entry) error = "Out of memory";
136
137        for (lp = fgets(buffer, MAXLINELEN, in);
138             lp && !error;
139             lp = fgets(buffer, MAXLINELEN, in))
140        {
141            char *tok;
142            int   tokCount = 0;
143            int   t;
144
145            lineNumber++;
146
147            while ((tok = strtok(lp, " \t\n"))) {
148                if (tok[0] == '#') break; // EOL comment -> stop
149                if (tokCount >= MAXTOKENS) { error = "Too many tokens"; break; }
150                tokens[tokCount] = tokCount ? GBS_eval_env(tok) : ARB_strdup(tok);
151                if (!tokens[tokCount]) { error = GB_await_error(); break; }
152                tokCount++;
153                lp = NULp;
154            }
155
156            if (!error && tokCount>0) {
157                if (strcmp(tokens[0], "ARB_TCP_DAT_VERSION") == 0) {
158                    if (tokCount>1) *versionFound = atoi(tokens[1]);
159                }
160                else {
161                    char *data;
162                    {
163                        int allsize = 0;
164                        int size[MAXTOKENS];
165
166                        for (t = 0; t<tokCount; t++) {
167                            size[t]  = strlen(tokens[t])+1;
168                            allsize += size[t];
169                        }
170                        allsize++;      // additional zero byte
171
172                        data = ARB_alloc<char>(allsize);
173                        {
174                            char *d = data;
175                            for (t = 0; t<tokCount; t++) {
176                                memmove(d, tokens[t], size[t]);
177                                d += size[t];
178                            }
179                            d[0] = 0;
180                        }
181                    }
182
183                    if (entries == entries_allocated) {
184                        entries_allocated = (int)(entries_allocated*1.5);
185                        ARB_realloc(entry, entries_allocated);
186                    }
187                    if (!error) {
188                        entry[entries++] = data;
189                        data             = NULp;
190                    }
191                    free(data);
192                }
193            }
194
195            if (error) error = GBS_global_string("%s (in line %i of '%s')", error, lineNumber, filename);
196            for (t = 0; t<tokCount; t++) freenull(tokens[t]);
197        }
198
199        ARB_realloc(entry, entries+1);
200
201        content          = entry;
202        content[entries] = NULp;
203        serverCount      = entries;
204
205        free(tokens);
206        fclose(in);
207    }
208
209#if defined(DUMP_ATD_ACCESS)
210    dump();
211#endif // DUMP_ATD_ACCESS
212    return error;
213}
214
215char *GB_arbtcpdat_path() {
216    return GB_lib_file(true, "", "arb_tcp.dat");
217}
218
219GB_ERROR ArbTcpDat::update() {
220    // read arb_tcp.dat (once or if changed on disk)
221    GB_ERROR error = NULp;
222
223    if (!filename) filename = GB_arbtcpdat_path();
224
225    if (!filename) {
226        error = "File $ARBHOME/lib/arb_tcp.dat missing or unreadable";
227    }
228    else {
229        struct stat st;
230        if (stat(filename, &st) == 0) {
231            GB_ULONG mtime = st.st_mtime;
232            if (modtime != mtime) { // read once or when changed
233                int arb_tcp_version;
234                error = read(&arb_tcp_version);
235
236                if (!error) {
237                    int expected_version = 2;
238                    if (arb_tcp_version != expected_version) {
239                        error = GBS_global_string("Expected arb_tcp.dat version %i\n"
240                                                  "Your '%s' has version %i\n"
241                                                  "To solve the problem\n"
242                                                  "- either reinstall ARB and do not select\n"
243                                                  "  'Use information of already installed ARB'\n"
244                                                  "  (any changes to arb_tcp.dat will be lost)\n"
245                                                  "- or backup your changed %s,\n"
246                                                  "  replace it by the contents from $ARBHOME/lib/arb_tcp_org.dat\n"
247                                                  "  and edit it to fit your needs.",
248                                                  expected_version,
249                                                  filename, arb_tcp_version,
250                                                  filename);
251                    }
252                }
253                modtime = error ? -1 : mtime;
254            }
255        }
256        else {
257            error = GBS_global_string("Can't stat '%s'", filename);
258        }
259    }
260
261    if (error) {
262        freenull(filename);
263#if defined(DUMP_ATD_ACCESS)
264        fprintf(stderr, "error=%s\n", error);
265#endif // DUMP_ATD_ACCESS
266    }
267
268    return error;
269}
270
271static ArbTcpDat arb_tcp_dat;
272
273// --------------------------------------------------------------------------------
274
275const char *GBS_scan_arb_tcp_param(const char *ipPort, const char *wantedParam) {
276    /* search a specific server parameter in result from GBS_read_arb_tcp()
277     * wantedParam may be sth like '-d' (case is ignored!)
278     */
279    const char *result = NULp;
280    if (ipPort) {
281        const char *exe   = strchr(ipPort, 0)+1;
282        const char *param = strchr(exe, 0)+1;
283        size_t      plen  = strlen(param);
284        size_t      wplen = strlen(wantedParam);
285
286        while (plen) {
287            if (strncasecmp(param, wantedParam, wplen) == 0) { // starts with wantedParam
288                result = param+wplen;
289                break;
290            }
291            param += plen+1;    // position behind 0-delimiter
292            plen   = strlen(param);
293        }
294    }
295    return result;
296}
297
298// AISC_MKPT_PROMOTE:#ifdef UNIT_TESTS // UT_DIFF
299// AISC_MKPT_PROMOTE:#define TEST_SERVER_ID (-666)
300// AISC_MKPT_PROMOTE:#define TEST_GENESERVER_ID (-667)
301// AISC_MKPT_PROMOTE:#endif
302
303const char *GBS_nameserver_tag(const char *add_field) {
304    if (add_field && add_field[0]) {
305        char *tag = GBS_global_string_copy("ARB_NAME_SERVER_%s", add_field);
306        ARB_strupper(tag);
307        RETURN_LOCAL_ALLOC(tag);
308    }
309    return "ARB_NAME_SERVER";
310}
311
312const char *GBS_ptserver_tag(int id) {
313#ifdef UNIT_TESTS // UT_DIFF
314    if (id == TEST_SERVER_ID) return "ARB_TEST_PT_SERVER";
315    if (id == TEST_GENESERVER_ID) return "ARB_TEST_PT_SERVER_GENE";
316#endif // UNIT_TESTS
317    gb_assert(id >= -1); // accept id==-1 => server_tag will be "ARB_PT_SERVER-1" which means "no ptserver has been selected"
318    const int   MAXIDSIZE = 30;
319    static char server_tag[MAXIDSIZE];
320    // ASSERT_RESULT_BELOW(int, sprintf(server_tag, "ARB_PT_SERVER%i", id), MAXIDSIZE);
321    ASSERT_RESULT_PREDICATE(isBelow<int>(MAXIDSIZE), sprintf(server_tag, "ARB_PT_SERVER%i", id));
322    return server_tag;
323}
324
325const char *GBS_read_arb_tcp(const char *env) {
326    /*! Find an entry in the $ARBHOME/lib/arb_tcp.dat file
327     *
328     * Be aware: GBS_read_arb_tcp returns a quite unusual string containing several string delimiters (0-characters).
329     * It contains all words (separated by space or tab in arb_tcp.dat) of the corresponding line in arb_tcp.dat.
330     * These words get concatenated (separated by 0 characters).
331     *
332     * The first word (which matches the parameter env) is skipped.
333     * The second word (the socket info = "host:port") is returned directly as result.
334     * The third word is the server executable name.
335     * The fourth and following words are parameters to the executable.
336     *
337     * To access these words follow this example:
338     *
339     * const char *ipPort = GBS_read_arb_tcp(GBS_nameserver_tag(NULp));
340     * if (ipPort) {
341     *     const char *serverExe = strchr(ipPort, 0)+1;
342     *     const char *para1     = strchr(serverExe, 0)+1; // always exists!
343     *     const char *para2     = strchr(para1, 0)+1;
344     *     if (para2[0]) {
345     *         // para2 exists
346     *     }
347     * }
348     * else {
349     * // handle error!
350     * }
351     *
352     * see also GBS_read_arb_tcp_param() above
353     *
354     * Returns NULp on error (which is exported in that case)
355     */
356
357    const char *result = NULp;
358    GB_ERROR    error  = NULp;
359
360    if (strchr(env, ':')) {
361        static char *resBuf = NULp;
362        freedup(resBuf, env);
363        result = resBuf;
364    }
365    else {
366        error = arb_tcp_dat.update(); // load once or reload after change on disk
367        if (!error) {
368            char *envuser = GBS_global_string_copy("%s:%s", GB_getenvUSER(), env);
369            result        = arb_tcp_dat.get_entry(envuser);
370
371            if (!result) { // no user-specific entry found
372                result = arb_tcp_dat.get_entry(env);
373                if (!result) {
374                    error = GBS_global_string("No such entry '%s' (or '%s') in '%s'",
375                                              env, envuser, arb_tcp_dat.get_filename());
376                }
377            }
378            free(envuser);
379        }
380    }
381
382    gb_assert(result||error);
383    if (error) {
384        GB_export_error(error);
385        result = NULp;
386    }
387    return result;
388}
389
390const char * const *GBS_get_arb_tcp_entries(const char *matching) {
391    /*! returns a list of all matching non-user-specific entries found in arb_tcp.dat
392     * match is performed by GBS_string_matches (e.g. use "ARB_PT_SERVER*")
393     */
394    static const char **matchingEntries     = NULp;
395    static int          matchingEntriesSize = 0;
396
397    GB_ERROR error = arb_tcp_dat.update();
398    if (!error) {
399        int count = arb_tcp_dat.get_server_count();
400
401        if (matchingEntriesSize != count) {
402            freeset(matchingEntries, ARB_alloc<const char*>(count+1));
403            matchingEntriesSize = count;
404        }
405
406        int matched = 0;
407        for (int c = 0; c<count; c++) {
408            const char *id = arb_tcp_dat.get_serverID(c);
409
410            if (!strchr(id, ':')) { // not user-specific
411                if (GBS_string_matches(id, matching, GB_MIND_CASE)) { // matches
412                    matchingEntries[matched++] = id;
413                }
414            }
415        }
416        matchingEntries[matched] = NULp; // end of array
417    }
418    if (error) GB_export_error(error);
419    return error ? NULp : matchingEntries;
420}
421
422// ---------------------------
423//      pt server related
424
425const char *GBS_ptserver_logname() {
426    RETURN_ONETIME_ALLOC(nulldup(GB_path_in_ARBLIB("pts/ptserver.log")));
427}
428
429void GBS_add_ptserver_logentry(const char *entry) {
430    FILE *log = fopen(GBS_ptserver_logname(), "at");
431    if (log) {
432        chmod(GBS_ptserver_logname(), 0666);
433
434        char    atime[256];
435        time_t  t   = time(NULp);
436        tm     *tms = localtime(&t);
437
438        strftime(atime, 255, "%Y/%m/%d %k:%M:%S", tms);
439        fprintf(log, "%s %s\n", atime, entry);
440        fclose(log);
441    }
442    else {
443        fprintf(stderr, "Failed to write to '%s'\n", GBS_ptserver_logname());
444    }
445}
446
447char *GBS_ptserver_id_to_choice(int i, int showBuild) {
448    /* Return a readable name for PTserver number 'i'
449     * If 'showBuild' then show build info as well.
450     *
451     * Return NULp in case of error (which was exported then)
452     */
453    const char *ipPort = GBS_read_arb_tcp(GBS_ptserver_tag(i));
454    char       *result = NULp;
455
456    if (ipPort) {
457        const char *file     = GBS_scan_arb_tcp_param(ipPort, "-d");
458        const char *nameOnly = strrchr(file, '/');
459
460        if (nameOnly) nameOnly++;   // position behind '/'
461        else nameOnly = file;       // otherwise show complete file
462
463        {
464            char *remote      = ARB_strdup(ipPort);
465            char *colon       = strchr(remote, ':');
466            if (colon) *colon = 0; // hide port
467
468            if (strcmp(remote, "localhost") == 0) { // hide localhost
469                result = nulldup(nameOnly);
470            }
471            else {
472                result = GBS_global_string_copy("%s: %s", remote, nameOnly);
473            }
474            free(remote);
475        }
476
477
478        if (showBuild) {
479            struct stat  st;
480
481            if (stat(file, &st) == 0) { // xxx.arb present
482                time_t  fileMod   = st.st_mtime;
483                char   *serverDB  = GBS_global_string_copy("%s.pt", file);
484                char   *newResult = NULp;
485
486                if (stat(serverDB, &st) == 0) { // pt-database present
487                    if (st.st_mtime < fileMod) { // DB is newer than pt-database
488                        newResult = GBS_global_string_copy("%s [starting or failed update]", result);
489                    }
490                    else {
491                        char  atime[256];
492                        tm   *tms = localtime(&st.st_mtime);
493
494                        strftime(atime, 255, "%Y/%m/%d %k:%M", tms);
495                        newResult = GBS_global_string_copy("%s [%s]", result, atime);
496                    }
497                }
498                else { // check for running build
499                    char *serverDB_duringBuild = GBS_global_string_copy("%s%%", serverDB);
500                    if (stat(serverDB_duringBuild, &st) == 0) {
501                        newResult = GBS_global_string_copy("%s [building..]", result);
502                    }
503                    free(serverDB_duringBuild);
504                }
505
506                if (newResult) freeset(result, newResult);
507                free(serverDB);
508            }
509        }
510    }
511
512    return result;
513}
514
515// --------------------------------------------------------------------------------
516
517#ifdef UNIT_TESTS
518
519#include <test_unit.h>
520
521void TEST_GBS_servertags() {
522    TEST_EXPECT_EQUAL(GBS_ptserver_tag(0), "ARB_PT_SERVER0");
523    TEST_EXPECT_EQUAL(GBS_ptserver_tag(7), "ARB_PT_SERVER7");
524
525    TEST_EXPECT_EQUAL(GBS_ptserver_tag(-1), "ARB_PT_SERVER-1"); // this should be interpreted as "user has not selected a server"
526
527    TEST_EXPECT_EQUAL(GBS_nameserver_tag(NULp),   "ARB_NAME_SERVER");
528    TEST_EXPECT_EQUAL(GBS_nameserver_tag(""),     "ARB_NAME_SERVER");
529    TEST_EXPECT_EQUAL(GBS_nameserver_tag("test"), "ARB_NAME_SERVER_TEST");
530}
531
532#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.