source: branches/profile/ARBDB/adtcp.cxx

Last change on this file was 12398, checked in by westram, 10 years ago
  • GBS_read_arb_tcp()
    • handle exported errors at callers
    • changed error message to 'No such entry'
    • related to [12396]
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.8 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(NULL) , content(NULL), 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 = 0;
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    printf("filename='%s'\n", filename);
86    printf("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            printf("Entry #%i:\n", c);
93
94            while (tok[0]) {
95                printf("- '%s'\n", tok);
96                tok = strchr(tok, 0)+1;
97            }
98        }
99    }
100    else {
101        printf("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 = 0;
112    FILE     *in    = fopen(filename, "rt");
113
114    *versionFound = 1; // default to version 1 (old format)
115
116#if defined(DUMP_ATD_ACCESS)
117    printf("(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     = (char**)malloc(MAXTOKENS*sizeof(*tokens));
130
131        int    entries_allocated = 30;
132        int    entries           = 0;
133        char **entry             = (char**)malloc(entries_allocated*sizeof(*entry));
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) : strdup(tok);
151                if (!tokens[tokCount]) { error = GB_await_error(); break; }
152                tokCount++;
153                lp = 0;
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 = (char*)malloc(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                        char **entry2     = (char**)realloc(entry, entries_allocated*sizeof(*entry));
186
187                        if (!entry2) error = "Out of memory";
188                        else entry = entry2;
189                    }
190                    if (!error) {
191                        entry[entries++] = data;
192                        data             = 0;
193                    }
194                    free(data);
195                }
196            }
197
198            if (error) error = GBS_global_string("%s (in line %i of '%s')", error, lineNumber, filename);
199            for (t = 0; t<tokCount; t++) freenull(tokens[t]);
200        }
201
202        content = (char**)realloc(entry, (entries+1)*sizeof(*entry));
203
204        if (!content) {
205            error       = "Out of memory";
206            serverCount = 0;
207            free(entry);
208        }
209        else {
210            content[entries] = 0;
211            serverCount      = entries;
212        }
213
214        free(tokens);
215        fclose(in);
216    }
217
218#if defined(DUMP_ATD_ACCESS)
219    dump();
220#endif // DUMP_ATD_ACCESS
221    return error;
222}
223
224char *GB_arbtcpdat_path() {
225    return GB_lib_file(true, "", "arb_tcp.dat");
226}
227
228GB_ERROR ArbTcpDat::update() {
229    // read arb_tcp.dat (once or if changed on disk)
230    GB_ERROR error = 0;
231
232    if (!filename) filename = GB_arbtcpdat_path();
233
234    if (!filename) {
235        error = "File $ARBHOME/lib/arb_tcp.dat missing or unreadable";
236    }
237    else {
238        struct stat st;
239        if (stat(filename, &st) == 0) {
240            GB_ULONG mtime = st.st_mtime;
241            if (modtime != mtime) { // read once or when changed
242                int arb_tcp_version;
243                error = read(&arb_tcp_version);
244
245                if (!error) {
246                    int expected_version = 2;
247                    if (arb_tcp_version != expected_version) {
248                        error = GBS_global_string("Expected arb_tcp.dat version %i\n"
249                                                  "Your '%s' has version %i\n"
250                                                  "To solve the problem\n"
251                                                  "- either reinstall ARB and do not select\n"
252                                                  "  'Use information of already installed ARB'\n"
253                                                  "  (any changes to arb_tcp.dat will be lost)\n"
254                                                  "- or backup your changed %s,\n"
255                                                  "  replace it by the contents from $ARBHOME/lib/arb_tcp_org.dat\n"
256                                                  "  and edit it to fit your needs.",
257                                                  expected_version,
258                                                  filename, arb_tcp_version,
259                                                  filename);
260                    }
261                }
262                modtime = error ? -1 : mtime;
263            }
264        }
265        else {
266            error = GBS_global_string("Can't stat '%s'", filename);
267        }
268    }
269   
270    if (error) {
271        freenull(filename);
272#if defined(DUMP_ATD_ACCESS)
273        printf("error=%s\n", error);
274#endif // DUMP_ATD_ACCESS
275    }
276
277    return error;
278}
279
280static ArbTcpDat arb_tcp_dat;
281
282// --------------------------------------------------------------------------------
283
284const char *GBS_scan_arb_tcp_param(const char *ipPort, const char *wantedParam) {
285    /* search a specific server parameter in result from GBS_read_arb_tcp()
286     * wantedParam may be sth like '-d' (case is ignored!)
287     */
288    const char *result = 0;
289    if (ipPort) {
290        const char *exe   = strchr(ipPort, 0)+1;
291        const char *param = strchr(exe, 0)+1;
292        size_t      plen  = strlen(param);
293        size_t      wplen = strlen(wantedParam);
294
295        while (plen) {
296            if (strncasecmp(param, wantedParam, wplen) == 0) { // starts with wantedParam
297                result = param+wplen;
298                break;
299            }
300            param += plen+1;    // position behind 0-delimiter
301            plen   = strlen(param);
302        }
303    }
304    return result;
305}
306
307// AISC_MKPT_PROMOTE:#ifdef UNIT_TESTS // UT_DIFF
308// AISC_MKPT_PROMOTE:#define TEST_SERVER_ID (-666)
309// AISC_MKPT_PROMOTE:#define TEST_GENESERVER_ID (-667)
310// AISC_MKPT_PROMOTE:#endif
311
312const char *GBS_nameserver_tag(const char *add_field) {
313    if (add_field && add_field[0]) {
314        char *tag = GBS_global_string_copy("ARB_NAME_SERVER_%s", add_field);
315        ARB_strupper(tag);
316        RETURN_LOCAL_ALLOC(tag);
317    }
318    return "ARB_NAME_SERVER";
319}
320
321const char *GBS_ptserver_tag(int id) {
322#ifdef UNIT_TESTS // UT_DIFF
323    if (id == TEST_SERVER_ID) return "ARB_TEST_PT_SERVER";
324    if (id == TEST_GENESERVER_ID) return "ARB_TEST_PT_SERVER_GENE";
325#endif // UNIT_TESTS
326    gb_assert(id >= 0);
327    const int   MAXIDSIZE = 30;
328    static char server_tag[MAXIDSIZE];
329    // ASSERT_RESULT_BELOW(int, sprintf(server_tag, "ARB_PT_SERVER%i", id), MAXIDSIZE);
330    ASSERT_RESULT_PREDICATE(isBelow<int>(MAXIDSIZE), sprintf(server_tag, "ARB_PT_SERVER%i", id));
331    return server_tag;
332}
333
334const char *GBS_read_arb_tcp(const char *env) {
335    /*! Find an entry in the $ARBHOME/lib/arb_tcp.dat file
336     *
337     * Be aware: GBS_read_arb_tcp returns a quite unusual string containing several string delimiters (0-characters).
338     * It contains all words (separated by space or tab in arb_tcp.dat) of the corresponding line in arb_tcp.dat.
339     * These words get concatenated (separated by 0 characters).
340     *
341     * The first word (which matches the parameter env) is skipped.
342     * The second word (the socket info = "host:port") is returned directly as result.
343     * The third word is the server executable name.
344     * The fourth and following words are parameters to the executable.
345     *
346     * To access these words follow this example:
347     *
348     * const char *ipPort = GBS_read_arb_tcp(GBS_nameserver_tag(NULL));
349     * if (ipPort) {
350     *     const char *serverExe = strchr(ipPort, 0)+1;
351     *     const char *para1     = strchr(serverExe, 0)+1; // always exists!
352     *     const char *para2     = strchr(para1, 0)+1;
353     *     if (para2[0]) {
354     *         // para2 exists
355     *     }
356     * }
357     * else {
358     * // handle error!
359     * }
360     *
361     * see also GBS_read_arb_tcp_param() above
362     *
363     * Returns NULL on error (which is exported in that case)
364     */
365
366    const char *result = 0;
367    GB_ERROR    error  = 0;
368
369    if (strchr(env, ':')) {
370        static char *resBuf = 0;
371        freedup(resBuf, env);
372        result = resBuf;
373    }
374    else {
375        error = arb_tcp_dat.update(); // load once or reload after change on disk
376        if (!error) {
377            const char *user = GB_getenvUSER();
378            if (!user) {
379                error = "Environment variable 'USER' not defined";
380            }
381            else {
382                char *envuser = GBS_global_string_copy("%s:%s", user, env);
383                result        = arb_tcp_dat.get_entry(envuser);
384
385                if (!result) { // no user-specific entry found
386                    result = arb_tcp_dat.get_entry(env);
387                    if (!result) {
388                        error = GBS_global_string("No such entry '%s' (or '%s') in '%s'",
389                                                  env, envuser, arb_tcp_dat.get_filename());
390                    }
391                }
392                free(envuser);
393            }
394        }
395    }
396
397    gb_assert(result||error);
398    if (error) {
399        GB_export_error(error);
400        result = 0;
401    }
402    return result;
403}
404
405const char * const *GBS_get_arb_tcp_entries(const char *matching) {
406    /*! returns a list of all matching non-user-specific entries found in arb_tcp.dat
407     * match is performed by GBS_string_matches (e.g. use "ARB_PT_SERVER*")
408     */
409    static const char **matchingEntries     = 0;
410    static int          matchingEntriesSize = 0;
411
412    GB_ERROR error = arb_tcp_dat.update();
413    if (!error) {
414        int count = arb_tcp_dat.get_server_count();
415
416        if (matchingEntriesSize != count) {
417            freeset(matchingEntries, (const char **)malloc((count+1)*sizeof(*matchingEntries)));
418            matchingEntriesSize = count;
419        }
420
421        int matched = 0;
422        for (int c = 0; c<count; c++) {
423            const char *id = arb_tcp_dat.get_serverID(c);
424
425            if (strchr(id, ':') == 0) { // not user-specific
426                if (GBS_string_matches(id, matching, GB_MIND_CASE)) { // matches
427                    matchingEntries[matched++] = id;
428                }
429            }
430        }
431        matchingEntries[matched] = 0; // end of array
432    }
433    if (error) GB_export_error(error);
434    return error ? 0 : matchingEntries;
435}
436
437// ---------------------------
438//      pt server related
439
440const char *GBS_ptserver_logname() {
441    RETURN_ONETIME_ALLOC(nulldup(GB_path_in_ARBLIB("pts/ptserver.log")));
442}
443
444void GBS_add_ptserver_logentry(const char *entry) {
445    FILE *log = fopen(GBS_ptserver_logname(), "at");
446    if (log) {
447        chmod(GBS_ptserver_logname(), 0666);
448
449        char    atime[256];
450        time_t  t   = time(0);
451        tm     *tms = localtime(&t);
452
453        strftime(atime, 255, "%Y/%m/%d %k:%M:%S", tms);
454        fprintf(log, "%s %s\n", atime, entry);
455        fclose(log);
456    }
457    else {
458        fprintf(stderr, "Failed to write to '%s'\n", GBS_ptserver_logname());
459    }
460}
461
462char *GBS_ptserver_id_to_choice(int i, int showBuild) {
463    /* Return a readable name for PTserver number 'i'
464     * If 'showBuild' then show build info as well.
465     *
466     * Return NULL in case of error (which was exported then)
467     */
468    const char *ipPort = GBS_read_arb_tcp(GBS_ptserver_tag(i));
469    char       *result = 0;
470
471    if (ipPort) {
472        const char *file     = GBS_scan_arb_tcp_param(ipPort, "-d");
473        const char *nameOnly = strrchr(file, '/');
474
475        if (nameOnly) nameOnly++;   // position behind '/'
476        else nameOnly = file;       // otherwise show complete file
477
478        {
479            char *remote      = strdup(ipPort);
480            char *colon       = strchr(remote, ':');
481            if (colon) *colon = 0; // hide port
482
483            if (strcmp(remote, "localhost") == 0) { // hide localhost
484                result = nulldup(nameOnly);
485            }
486            else {
487                result = GBS_global_string_copy("%s: %s", remote, nameOnly);
488            }
489            free(remote);
490        }
491
492
493        if (showBuild) {
494            struct stat  st;
495
496            if (stat(file, &st) == 0) { // xxx.arb present
497                time_t  fileMod   = st.st_mtime;
498                char   *serverDB  = GBS_global_string_copy("%s.pt", file);
499                char   *newResult = 0;
500
501                if (stat(serverDB, &st) == 0) { // pt-database present
502                    if (st.st_mtime < fileMod) { // DB is newer than pt-database
503                        newResult = GBS_global_string_copy("%s [starting or failed update]", result);
504                    }
505                    else {
506                        char  atime[256];
507                        tm   *tms = localtime(&st.st_mtime);
508
509                        strftime(atime, 255, "%Y/%m/%d %k:%M", tms);
510                        newResult = GBS_global_string_copy("%s [%s]", result, atime);
511                    }
512                }
513                else { // check for running build
514                    char *serverDB_duringBuild = GBS_global_string_copy("%s%%", serverDB);
515                    if (stat(serverDB_duringBuild, &st) == 0) {
516                        newResult = GBS_global_string_copy("%s [building..]", result);
517                    }
518                    free(serverDB_duringBuild);
519                }
520
521                if (newResult) freeset(result, newResult);
522                free(serverDB);
523            }
524        }
525    }
526
527    return result;
528}
529
530// --------------------------------------------------------------------------------
531
532#ifdef UNIT_TESTS
533
534#include <test_unit.h>
535
536void TEST_GBS_servertags() {
537    TEST_EXPECT_EQUAL(GBS_ptserver_tag(0), "ARB_PT_SERVER0");
538    TEST_EXPECT_EQUAL(GBS_ptserver_tag(7), "ARB_PT_SERVER7");
539   
540    TEST_EXPECT_EQUAL(GBS_nameserver_tag(NULL),   "ARB_NAME_SERVER");
541    TEST_EXPECT_EQUAL(GBS_nameserver_tag(""),     "ARB_NAME_SERVER");
542    TEST_EXPECT_EQUAL(GBS_nameserver_tag("test"), "ARB_NAME_SERVER_TEST");
543}
544
545#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.