source: branches/help/ARBDB/adtcp.cxx

Last change on this file was 18730, checked in by westram, 3 years ago
  • remove trailing whitespace from c source.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 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 >= 0);
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            const char *user = GB_getenvUSER();
369            if (!user) {
370                error = "Environment variable 'USER' not defined";
371            }
372            else {
373                char *envuser = GBS_global_string_copy("%s:%s", user, env);
374                result        = arb_tcp_dat.get_entry(envuser);
375
376                if (!result) { // no user-specific entry found
377                    result = arb_tcp_dat.get_entry(env);
378                    if (!result) {
379                        error = GBS_global_string("No such entry '%s' (or '%s') in '%s'",
380                                                  env, envuser, arb_tcp_dat.get_filename());
381                    }
382                }
383                free(envuser);
384            }
385        }
386    }
387
388    gb_assert(result||error);
389    if (error) {
390        GB_export_error(error);
391        result = NULp;
392    }
393    return result;
394}
395
396const char * const *GBS_get_arb_tcp_entries(const char *matching) {
397    /*! returns a list of all matching non-user-specific entries found in arb_tcp.dat
398     * match is performed by GBS_string_matches (e.g. use "ARB_PT_SERVER*")
399     */
400    static const char **matchingEntries     = NULp;
401    static int          matchingEntriesSize = 0;
402
403    GB_ERROR error = arb_tcp_dat.update();
404    if (!error) {
405        int count = arb_tcp_dat.get_server_count();
406
407        if (matchingEntriesSize != count) {
408            freeset(matchingEntries, ARB_alloc<const char*>(count+1));
409            matchingEntriesSize = count;
410        }
411
412        int matched = 0;
413        for (int c = 0; c<count; c++) {
414            const char *id = arb_tcp_dat.get_serverID(c);
415
416            if (!strchr(id, ':')) { // not user-specific
417                if (GBS_string_matches(id, matching, GB_MIND_CASE)) { // matches
418                    matchingEntries[matched++] = id;
419                }
420            }
421        }
422        matchingEntries[matched] = NULp; // end of array
423    }
424    if (error) GB_export_error(error);
425    return error ? NULp : matchingEntries;
426}
427
428// ---------------------------
429//      pt server related
430
431const char *GBS_ptserver_logname() {
432    RETURN_ONETIME_ALLOC(nulldup(GB_path_in_ARBLIB("pts/ptserver.log")));
433}
434
435void GBS_add_ptserver_logentry(const char *entry) {
436    FILE *log = fopen(GBS_ptserver_logname(), "at");
437    if (log) {
438        chmod(GBS_ptserver_logname(), 0666);
439
440        char    atime[256];
441        time_t  t   = time(NULp);
442        tm     *tms = localtime(&t);
443
444        strftime(atime, 255, "%Y/%m/%d %k:%M:%S", tms);
445        fprintf(log, "%s %s\n", atime, entry);
446        fclose(log);
447    }
448    else {
449        fprintf(stderr, "Failed to write to '%s'\n", GBS_ptserver_logname());
450    }
451}
452
453char *GBS_ptserver_id_to_choice(int i, int showBuild) {
454    /* Return a readable name for PTserver number 'i'
455     * If 'showBuild' then show build info as well.
456     *
457     * Return NULp in case of error (which was exported then)
458     */
459    const char *ipPort = GBS_read_arb_tcp(GBS_ptserver_tag(i));
460    char       *result = NULp;
461
462    if (ipPort) {
463        const char *file     = GBS_scan_arb_tcp_param(ipPort, "-d");
464        const char *nameOnly = strrchr(file, '/');
465
466        if (nameOnly) nameOnly++;   // position behind '/'
467        else nameOnly = file;       // otherwise show complete file
468
469        {
470            char *remote      = ARB_strdup(ipPort);
471            char *colon       = strchr(remote, ':');
472            if (colon) *colon = 0; // hide port
473
474            if (strcmp(remote, "localhost") == 0) { // hide localhost
475                result = nulldup(nameOnly);
476            }
477            else {
478                result = GBS_global_string_copy("%s: %s", remote, nameOnly);
479            }
480            free(remote);
481        }
482
483
484        if (showBuild) {
485            struct stat  st;
486
487            if (stat(file, &st) == 0) { // xxx.arb present
488                time_t  fileMod   = st.st_mtime;
489                char   *serverDB  = GBS_global_string_copy("%s.pt", file);
490                char   *newResult = NULp;
491
492                if (stat(serverDB, &st) == 0) { // pt-database present
493                    if (st.st_mtime < fileMod) { // DB is newer than pt-database
494                        newResult = GBS_global_string_copy("%s [starting or failed update]", result);
495                    }
496                    else {
497                        char  atime[256];
498                        tm   *tms = localtime(&st.st_mtime);
499
500                        strftime(atime, 255, "%Y/%m/%d %k:%M", tms);
501                        newResult = GBS_global_string_copy("%s [%s]", result, atime);
502                    }
503                }
504                else { // check for running build
505                    char *serverDB_duringBuild = GBS_global_string_copy("%s%%", serverDB);
506                    if (stat(serverDB_duringBuild, &st) == 0) {
507                        newResult = GBS_global_string_copy("%s [building..]", result);
508                    }
509                    free(serverDB_duringBuild);
510                }
511
512                if (newResult) freeset(result, newResult);
513                free(serverDB);
514            }
515        }
516    }
517
518    return result;
519}
520
521// --------------------------------------------------------------------------------
522
523#ifdef UNIT_TESTS
524
525#include <test_unit.h>
526
527void TEST_GBS_servertags() {
528    TEST_EXPECT_EQUAL(GBS_ptserver_tag(0), "ARB_PT_SERVER0");
529    TEST_EXPECT_EQUAL(GBS_ptserver_tag(7), "ARB_PT_SERVER7");
530
531    TEST_EXPECT_EQUAL(GBS_nameserver_tag(NULp),   "ARB_NAME_SERVER");
532    TEST_EXPECT_EQUAL(GBS_nameserver_tag(""),     "ARB_NAME_SERVER");
533    TEST_EXPECT_EQUAL(GBS_nameserver_tag("test"), "ARB_NAME_SERVER_TEST");
534}
535
536#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.