history.c

Go to the documentation of this file.
00001 /********************************************************************\
00002 
00003   Name:         HISTORY.C
00004   Created by:   Stefan Ritt
00005 
00006   Contents:     MIDAS history functions
00007 
00008   $Id: history.c 4636 2009-12-01 15:58:50Z ritt $
00009 
00010 \********************************************************************/
00011 
00012 #include "midas.h"
00013 #include "msystem.h"
00014 #include "strlcpy.h"
00015 #include <assert.h>
00016 
00017 /** @defgroup hsfunctioncode Midas History Functions (hs_xxx)
00018  */
00019 
00020 /**dox***************************************************************/
00021 /** @addtogroup hsfunctioncode
00022  *
00023  *  @{  */
00024 
00025 #if !defined(OS_VXWORKS)
00026 /********************************************************************\
00027 *                                                                    *
00028 *                 History functions                                  *
00029 *                                                                    *
00030 \********************************************************************/
00031 
00032 /**dox***************************************************************/
00033 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00034 
00035 static HISTORY *_history;
00036 static INT _history_entries = 0;
00037 static char _hs_path_name[MAX_STRING_LENGTH];
00038 
00039 /**dox***************************************************************/
00040 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
00041 
00042 /********************************************************************/
00043 /**
00044 Sets the path for future history file accesses. Should
00045 be called before any other history function is called.
00046 @param path             Directory where history files reside
00047 @return HS_SUCCESS
00048 */
00049 INT hs_set_path(char *path)
00050 {
00051    /* set path locally and remotely */
00052    if (rpc_is_remote())
00053       rpc_call(RPC_HS_SET_PATH, path);
00054 
00055    strcpy(_hs_path_name, path);
00056 
00057    /* check for trailing directory seperator */
00058    if (strlen(_hs_path_name) > 0 && _hs_path_name[strlen(_hs_path_name) - 1] != DIR_SEPARATOR)
00059       strcat(_hs_path_name, DIR_SEPARATOR_STR);
00060 
00061    return HS_SUCCESS;
00062 }
00063 
00064 /**dox***************************************************************/
00065 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00066 
00067 /********************************************************************/
00068 
00069 /**
00070 Open history file belonging to certain date. Internal use
00071            only.
00072 @param ltime          Date for which a history file should be opened.
00073 @param suffix         File name suffix like "hst", "idx", "idf"
00074 @param mode           R/W access mode
00075 @param fh             File handle
00076 @return HS_SUCCESS
00077 */
00078 INT hs_open_file(time_t ltime, char *suffix, INT mode, int *fh)
00079 {
00080    struct tm *tms;
00081    char file_name[256];
00082    time_t ttime;
00083 
00084    /* generate new file name YYMMDD.xxx */
00085 #if !defined(OS_VXWORKS)
00086 #if !defined(OS_VMS)
00087    tzset();
00088 #endif
00089 #endif
00090    ttime = (time_t) ltime;
00091    tms = localtime(&ttime);
00092 
00093    sprintf(file_name, "%s%02d%02d%02d.%s", _hs_path_name, tms->tm_year % 100, tms->tm_mon + 1, tms->tm_mday, suffix);
00094 
00095    /* open file, add O_BINARY flag for Windows NT */
00096    *fh = open(file_name, mode | O_BINARY, 0644);
00097 
00098    //printf("hs_open_file: time %d, file \'%s\', fh %d\n", (int)ltime, file_name, *fh);
00099 
00100    return HS_SUCCESS;
00101 }
00102 
00103 /********************************************************************/
00104 INT hs_gen_index(DWORD ltime)
00105 /********************************************************************\
00106 
00107   Routine: hs_gen_index
00108 
00109   Purpose: Regenerate index files ("idx" and "idf" files) for a given
00110            history file ("hst"). Interal use only.
00111 
00112   Input:
00113     time_t ltime            Date for which a history file should
00114                             be analyzed.
00115 
00116   Output:
00117     none
00118 
00119   Function value:
00120     HS_SUCCESS              Successful completion
00121     HS_FILE_ERROR           Index files cannot be created
00122 
00123 \********************************************************************/
00124 {
00125    char event_name[NAME_LENGTH];
00126    int fh, fhd, fhi;
00127    INT n;
00128    HIST_RECORD rec;
00129    INDEX_RECORD irec;
00130    DEF_RECORD def_rec;
00131    int recovering = 0;
00132    //time_t now = time(NULL);
00133 
00134    cm_msg(MINFO, "hs_gen_index", "generating index files for time %d", (int) ltime);
00135    printf("Recovering index files...\n");
00136 
00137    if (ltime == 0)
00138       ltime = (DWORD) time(NULL);
00139 
00140    /* open new index file */
00141    hs_open_file(ltime, "idx", O_RDWR | O_CREAT | O_TRUNC, &fhi);
00142    hs_open_file(ltime, "idf", O_RDWR | O_CREAT | O_TRUNC, &fhd);
00143 
00144    if (fhd < 0 || fhi < 0) {
00145       cm_msg(MERROR, "hs_gen_index", "cannot create index file");
00146       return HS_FILE_ERROR;
00147    }
00148 
00149    /* open history file */
00150    hs_open_file(ltime, "hst", O_RDONLY, &fh);
00151    if (fh < 0)
00152       return HS_FILE_ERROR;
00153    lseek(fh, 0, SEEK_SET);
00154 
00155    /* loop over file records in .hst file */
00156    do {
00157       n = read(fh, (char *) &rec, sizeof(rec));
00158       //printf("read %d\n", n);
00159       if (n < sizeof(rec))
00160          break;
00161 
00162       /* check if record type is definition */
00163       if (rec.record_type == RT_DEF) {
00164          /* read name */
00165          read(fh, event_name, sizeof(event_name));
00166 
00167          printf("Event definition %s, ID %d\n", event_name, rec.event_id);
00168 
00169          /* write definition index record */
00170          def_rec.event_id = rec.event_id;
00171          memcpy(def_rec.event_name, event_name, sizeof(event_name));
00172          def_rec.def_offset = TELL(fh) - sizeof(event_name) - sizeof(rec);
00173          write(fhd, (char *) &def_rec, sizeof(def_rec));
00174 
00175          //printf("data def at %d (age %d)\n", rec.time, now-rec.time);
00176 
00177          /* skip tags */
00178          lseek(fh, rec.data_size, SEEK_CUR);
00179       } else if (rec.record_type == RT_DATA && rec.data_size > 1 && rec.data_size < 1 * 1024 * 1024) {
00180          /* write index record */
00181          irec.event_id = rec.event_id;
00182          irec.time = rec.time;
00183          irec.offset = TELL(fh) - sizeof(rec);
00184          write(fhi, (char *) &irec, sizeof(irec));
00185 
00186          //printf("data rec at %d (age %d)\n", rec.time, now-rec.time);
00187 
00188          /* skip data */
00189          lseek(fh, rec.data_size, SEEK_CUR);
00190       } else {
00191          if (!recovering)
00192             cm_msg(MERROR, "hs_gen_index", "broken history file for time %d, trying to recover", (int) ltime);
00193 
00194          recovering = 1;
00195          lseek(fh, 1 - sizeof(rec), SEEK_CUR);
00196 
00197          continue;
00198       }
00199 
00200    } while (TRUE);
00201 
00202    close(fh);
00203    close(fhi);
00204    close(fhd);
00205 
00206    printf("...done.\n");
00207 
00208    return HS_SUCCESS;
00209 }
00210 
00211 
00212 /********************************************************************/
00213 INT hs_search_file(DWORD * ltime, INT direction)
00214 /********************************************************************\
00215 
00216   Routine: hs_search_file
00217 
00218   Purpose: Search an history file for a given date. If not found,
00219            look for files after date (direction==1) or before date
00220            (direction==-1) up to one year.
00221 
00222   Input:
00223     DWORD  *ltime           Date of history file
00224     INT    direction        Search direction
00225 
00226   Output:
00227     DWORD  *ltime           Date of history file found
00228 
00229   Function value:
00230     HS_SUCCESS              Successful completion
00231     HS_FILE_ERROR           No file found
00232 
00233 \********************************************************************/
00234 {
00235    time_t lt;
00236    int fh, fhd, fhi;
00237    struct tm *tms;
00238 
00239    if (*ltime == 0)
00240       *ltime = ss_time();
00241 
00242    lt = (time_t) * ltime;
00243    do {
00244       /* try to open history file for date "lt" */
00245       hs_open_file(lt, "hst", O_RDONLY, &fh);
00246 
00247       /* if not found, look for next day */
00248       if (fh < 0)
00249          lt += direction * 3600 * 24;
00250 
00251       /* stop if more than a year before starting point or in the future */
00252    } while (fh < 0 && (INT) * ltime - (INT) lt < 3600 * 24 * 365 && lt <= (time_t) ss_time());
00253 
00254    if (fh < 0)
00255       return HS_FILE_ERROR;
00256 
00257    if (lt != *ltime) {
00258       /* if switched to new day, set start_time to 0:00 */
00259       tms = localtime(&lt);
00260       tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
00261       *ltime = (DWORD) mktime(tms);
00262    }
00263 
00264    /* check if index files are there */
00265    hs_open_file(*ltime, "idf", O_RDONLY, &fhd);
00266    hs_open_file(*ltime, "idx", O_RDONLY, &fhi);
00267 
00268    close(fh);
00269    if (fhd > 0)
00270       close(fhd);
00271    if (fhi > 0)
00272       close(fhi);
00273 
00274    /* generate them if not */
00275    if (fhd < 0 || fhi < 0)
00276       hs_gen_index(*ltime);
00277 
00278    return HS_SUCCESS;
00279 }
00280 
00281 
00282 /********************************************************************/
00283 INT hs_define_event(DWORD event_id, char *name, TAG * tag, DWORD size)
00284 /********************************************************************\
00285 
00286   Routine: hs_define_event
00287 
00288   Purpose: Define a new event for which a history should be recorded.
00289            This routine must be called before any call to
00290            hs_write_event. It also should be called if the definition
00291            of the event has changed.
00292 
00293            The event definition is written directly to the history
00294            file. If the definition is identical to a previous
00295            definition, it is not written to the file.
00296 
00297 
00298   Input:
00299     DWORD  event_id         ID for this event. Must be unique.
00300     char   name             Name of this event
00301     TAG    tag              Tag list containing names and types of
00302                             variables in this event.
00303     DWORD  size             Size of tag array
00304 
00305   Output:
00306     <none>
00307 
00308   Function value:
00309     HS_SUCCESS              Successful completion
00310     HS_NO_MEMEORY           Out of memory
00311     HS_FILE_ERROR           Cannot open history file
00312 
00313 \********************************************************************/
00314 {
00315 /* History events are only written locally (?)
00316 
00317   if (rpc_is_remote())
00318     return rpc_call(RPC_HS_DEFINE_EVENT, event_id, name, tag, size);
00319 */
00320    {
00321       HIST_RECORD rec, prev_rec;
00322       DEF_RECORD def_rec;
00323       time_t ltime;
00324       char str[256], event_name[NAME_LENGTH], *buffer;
00325       int fh, fhi, fhd;
00326       INT i, n, len, index, status, semaphore;
00327       struct tm *tmb;
00328 
00329       /* request semaphore */
00330       cm_get_experiment_semaphore(NULL, NULL, &semaphore, NULL);
00331       status = ss_semaphore_wait_for(semaphore, 5 * 1000);
00332       if (status != SS_SUCCESS)
00333          return SUCCESS;        /* someone else blocked the history system */
00334 
00335       /* allocate new space for the new history descriptor */
00336       if (_history_entries == 0) {
00337          _history = (HISTORY *) M_MALLOC(sizeof(HISTORY));
00338          memset(_history, 0, sizeof(HISTORY));
00339          if (_history == NULL) {
00340             ss_semaphore_release(semaphore);
00341             return HS_NO_MEMORY;
00342          }
00343 
00344          _history_entries = 1;
00345          index = 0;
00346       } else {
00347          /* check if history already open */
00348          for (i = 0; i < _history_entries; i++)
00349             if (_history[i].event_id == event_id)
00350                break;
00351 
00352          /* if not found, create new one */
00353          if (i == _history_entries) {
00354             _history = (HISTORY *) realloc(_history, sizeof(HISTORY) * (_history_entries + 1));
00355             memset(&_history[_history_entries], 0, sizeof(HISTORY));
00356 
00357             _history_entries++;
00358             if (_history == NULL) {
00359                _history_entries--;
00360                ss_semaphore_release(semaphore);
00361                return HS_NO_MEMORY;
00362             }
00363          }
00364          index = i;
00365       }
00366 
00367       /* assemble definition record header */
00368       rec.record_type = RT_DEF;
00369       rec.event_id = event_id;
00370       rec.time = (DWORD) time(NULL);
00371       rec.data_size = size;
00372       strncpy(event_name, name, NAME_LENGTH);
00373 
00374       /* pad tag names with zeos */
00375       for (i = 0; (DWORD) i < size / sizeof(TAG); i++) {
00376          len = strlen(tag[i].name);
00377          memset(tag[i].name + len, 0, NAME_LENGTH - len);
00378       }
00379 
00380       /* if history structure not set up, do so now */
00381       if (!_history[index].hist_fh) {
00382          /* open history file */
00383          hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
00384          if (fh < 0) {
00385             ss_semaphore_release(semaphore);
00386             return HS_FILE_ERROR;
00387          }
00388 
00389          /* open index files */
00390          hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
00391          hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
00392          lseek(fh, 0, SEEK_END);
00393          lseek(fhi, 0, SEEK_END);
00394          lseek(fhd, 0, SEEK_END);
00395 
00396          /* regenerate index if missing */
00397          if (TELL(fh) > 0 && TELL(fhd) == 0) {
00398             close(fh);
00399             close(fhi);
00400             close(fhd);
00401             hs_gen_index(rec.time);
00402             hs_open_file(rec.time, "hst", O_RDWR, &fh);
00403             hs_open_file(rec.time, "idx", O_RDWR, &fhi);
00404             hs_open_file(rec.time, "idf", O_RDWR, &fhd);
00405             lseek(fh, 0, SEEK_END);
00406             lseek(fhi, 0, SEEK_END);
00407             lseek(fhd, 0, SEEK_END);
00408          }
00409 
00410          ltime = (time_t) rec.time;
00411          tmb = localtime(&ltime);
00412          tmb->tm_hour = tmb->tm_min = tmb->tm_sec = 0;
00413 
00414          /* setup history structure */
00415          _history[index].hist_fh = fh;
00416          _history[index].index_fh = fhi;
00417          _history[index].def_fh = fhd;
00418          _history[index].def_offset = TELL(fh);
00419          _history[index].event_id = event_id;
00420          strcpy(_history[index].event_name, event_name);
00421          _history[index].base_time = (DWORD) mktime(tmb);
00422          _history[index].n_tag = size / sizeof(TAG);
00423          _history[index].tag = (TAG *) M_MALLOC(size);
00424          memcpy(_history[index].tag, tag, size);
00425 
00426          /* search previous definition */
00427          n = TELL(fhd) / sizeof(def_rec);
00428          def_rec.event_id = 0;
00429          for (i = n - 1; i >= 0; i--) {
00430             lseek(fhd, i * sizeof(def_rec), SEEK_SET);
00431             read(fhd, (char *) &def_rec, sizeof(def_rec));
00432             if (def_rec.event_id == event_id)
00433                break;
00434          }
00435          lseek(fhd, 0, SEEK_END);
00436 
00437          /* if definition found, compare it with new one */
00438          if (def_rec.event_id == event_id) {
00439             buffer = (char *) M_MALLOC(size);
00440             memset(buffer, 0, size);
00441 
00442             lseek(fh, def_rec.def_offset, SEEK_SET);
00443             read(fh, (char *) &prev_rec, sizeof(prev_rec));
00444             read(fh, str, NAME_LENGTH);
00445             read(fh, buffer, size);
00446             lseek(fh, 0, SEEK_END);
00447 
00448             if (prev_rec.data_size != size || strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
00449                /* write definition to history file */
00450                write(fh, (char *) &rec, sizeof(rec));
00451                write(fh, event_name, NAME_LENGTH);
00452                write(fh, (char *) tag, size);
00453 
00454                /* write index record */
00455                def_rec.event_id = event_id;
00456                memcpy(def_rec.event_name, event_name, sizeof(event_name));
00457                def_rec.def_offset = _history[index].def_offset;
00458                write(fhd, (char *) &def_rec, sizeof(def_rec));
00459             } else
00460                /* definition identical, just remember old offset */
00461                _history[index].def_offset = def_rec.def_offset;
00462 
00463             M_FREE(buffer);
00464          } else {
00465             /* write definition to history file */
00466             write(fh, (char *) &rec, sizeof(rec));
00467             write(fh, event_name, NAME_LENGTH);
00468             write(fh, (char *) tag, size);
00469 
00470             /* write definition index record */
00471             def_rec.event_id = event_id;
00472             memcpy(def_rec.event_name, event_name, sizeof(event_name));
00473             def_rec.def_offset = _history[index].def_offset;
00474             write(fhd, (char *) &def_rec, sizeof(def_rec));
00475          }
00476       } else {
00477          fh = _history[index].hist_fh;
00478          fhd = _history[index].def_fh;
00479 
00480          /* compare definition with previous definition */
00481          buffer = (char *) M_MALLOC(size);
00482          memset(buffer, 0, size);
00483 
00484          lseek(fh, _history[index].def_offset, SEEK_SET);
00485          read(fh, (char *) &prev_rec, sizeof(prev_rec));
00486          read(fh, str, NAME_LENGTH);
00487          read(fh, buffer, size);
00488 
00489          lseek(fh, 0, SEEK_END);
00490          lseek(fhd, 0, SEEK_END);
00491 
00492          if (prev_rec.data_size != size || strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
00493             /* save new definition offset */
00494             _history[index].def_offset = TELL(fh);
00495 
00496             /* write definition to history file */
00497             write(fh, (char *) &rec, sizeof(rec));
00498             write(fh, event_name, NAME_LENGTH);
00499             write(fh, (char *) tag, size);
00500 
00501             /* write index record */
00502             def_rec.event_id = event_id;
00503             memcpy(def_rec.event_name, event_name, sizeof(event_name));
00504             def_rec.def_offset = _history[index].def_offset;
00505             write(fhd, (char *) &def_rec, sizeof(def_rec));
00506          }
00507 
00508          M_FREE(buffer);
00509       }
00510 
00511       ss_semaphore_release(semaphore);
00512    }
00513 
00514    return HS_SUCCESS;
00515 }
00516 
00517 
00518 /********************************************************************/
00519 INT hs_write_event(DWORD event_id, void *data, DWORD size)
00520 /********************************************************************\
00521 
00522   Routine: hs_write_event
00523 
00524   Purpose: Write an event to a history file.
00525 
00526   Input:
00527     DWORD  event_id         Event ID
00528     void   *data            Data buffer containing event
00529     DWORD  size             Data buffer size in bytes
00530 
00531   Output:
00532     none
00533                             future hs_write_event
00534 
00535   Function value:
00536     HS_SUCCESS              Successful completion
00537     HS_NO_MEMEORY           Out of memory
00538     HS_FILE_ERROR           Cannot write to history file
00539     HS_UNDEFINED_EVENT      Event was not defined via hs_define_event
00540 
00541 \********************************************************************/
00542 {
00543 /* history events are only written locally (?)
00544 
00545   if (rpc_is_remote())
00546     return rpc_call(RPC_HS_WRITE_EVENT, event_id, data, size);
00547 */
00548    HIST_RECORD rec, drec;
00549    DEF_RECORD def_rec;
00550    INDEX_RECORD irec;
00551    int fh, fhi, fhd, last_pos_data, last_pos_index;
00552    INT index, semaphore, status;
00553    struct tm tmb, tmr;
00554    time_t ltime;
00555 
00556    /* request semaphore */
00557    cm_get_experiment_semaphore(NULL, NULL, &semaphore, NULL);
00558    status = ss_semaphore_wait_for(semaphore, 5 * 1000);
00559    if (status != SS_SUCCESS) {
00560       cm_msg(MERROR, "hs_write_event", "semaphore timeout");
00561       return SUCCESS;           /* someone else blocked the history system */
00562    }
00563 
00564    /* find index to history structure */
00565    for (index = 0; index < _history_entries; index++)
00566       if (_history[index].event_id == event_id)
00567          break;
00568    if (index == _history_entries) {
00569       ss_semaphore_release(semaphore);
00570       return HS_UNDEFINED_EVENT;
00571    }
00572 
00573    /* assemble record header */
00574    rec.record_type = RT_DATA;
00575    rec.event_id = _history[index].event_id;
00576    rec.time = (DWORD) time(NULL);
00577    rec.def_offset = _history[index].def_offset;
00578    rec.data_size = size;
00579 
00580    irec.event_id = _history[index].event_id;
00581    irec.time = rec.time;
00582 
00583    /* check if new day */
00584    ltime = (time_t) rec.time;
00585    memcpy(&tmr, localtime(&ltime), sizeof(tmr));
00586    ltime = (time_t) _history[index].base_time;
00587    memcpy(&tmb, localtime(&ltime), sizeof(tmb));
00588 
00589    if (tmr.tm_yday != tmb.tm_yday) {
00590       /* close current history file */
00591       close(_history[index].hist_fh);
00592       close(_history[index].def_fh);
00593       close(_history[index].index_fh);
00594 
00595       /* open new history file */
00596       hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fh);
00597       if (fh < 0) {
00598          ss_semaphore_release(semaphore);
00599          return HS_FILE_ERROR;
00600       }
00601 
00602       /* open new index file */
00603       hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fhi);
00604       if (fhi < 0) {
00605          ss_semaphore_release(semaphore);
00606          return HS_FILE_ERROR;
00607       }
00608 
00609       /* open new definition index file */
00610       hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fhd);
00611       if (fhd < 0) {
00612          ss_semaphore_release(semaphore);
00613          return HS_FILE_ERROR;
00614       }
00615 
00616       lseek(fh, 0, SEEK_END);
00617       lseek(fhi, 0, SEEK_END);
00618       lseek(fhd, 0, SEEK_END);
00619 
00620       /* remember new file handles */
00621       _history[index].hist_fh = fh;
00622       _history[index].index_fh = fhi;
00623       _history[index].def_fh = fhd;
00624 
00625       _history[index].def_offset = TELL(fh);
00626       rec.def_offset = _history[index].def_offset;
00627 
00628       tmr.tm_hour = tmr.tm_min = tmr.tm_sec = 0;
00629       _history[index].base_time = (DWORD) mktime(&tmr);
00630 
00631       /* write definition from _history structure */
00632       drec.record_type = RT_DEF;
00633       drec.event_id = _history[index].event_id;
00634       drec.time = rec.time;
00635       drec.data_size = _history[index].n_tag * sizeof(TAG);
00636 
00637       write(fh, (char *) &drec, sizeof(drec));
00638       write(fh, _history[index].event_name, NAME_LENGTH);
00639       write(fh, (char *) _history[index].tag, drec.data_size);
00640 
00641       /* write definition index record */
00642       def_rec.event_id = _history[index].event_id;
00643       memcpy(def_rec.event_name, _history[index].event_name, sizeof(def_rec.event_name));
00644       def_rec.def_offset = _history[index].def_offset;
00645       write(fhd, (char *) &def_rec, sizeof(def_rec));
00646    }
00647 
00648    /* got to end of file */
00649    lseek(_history[index].hist_fh, 0, SEEK_END);
00650    last_pos_data = irec.offset = TELL(_history[index].hist_fh);
00651 
00652    /* write record header */
00653    write(_history[index].hist_fh, (char *) &rec, sizeof(rec));
00654 
00655    /* write data */
00656    if (write(_history[index].hist_fh, (char *) data, size) < (int) size) {
00657       /* disk maybe full? Do a roll-back! */
00658       lseek(_history[index].hist_fh, last_pos_data, SEEK_SET);
00659       TRUNCATE(_history[index].hist_fh);
00660       ss_semaphore_release(semaphore);
00661       return HS_FILE_ERROR;
00662    }
00663 
00664    /* write index record */
00665    lseek(_history[index].index_fh, 0, SEEK_END);
00666    last_pos_index = TELL(_history[index].index_fh);
00667    if (write(_history[index].index_fh, (char *) &irec, sizeof(irec)) < sizeof(irec)) {
00668       /* disk maybe full? Do a roll-back! */
00669       lseek(_history[index].hist_fh, last_pos_data, SEEK_SET);
00670       TRUNCATE(_history[index].hist_fh);
00671       lseek(_history[index].index_fh, last_pos_index, SEEK_SET);
00672       TRUNCATE(_history[index].index_fh);
00673       ss_semaphore_release(semaphore);
00674       return HS_FILE_ERROR;
00675    }
00676 
00677    ss_semaphore_release(semaphore);
00678    return HS_SUCCESS;
00679 }
00680 
00681 
00682 /********************************************************************/
00683 INT hs_enum_events(DWORD ltime, char *event_name, DWORD * name_size, INT event_id[], DWORD * id_size)
00684 /********************************************************************\
00685 
00686   Routine: hs_enum_events
00687 
00688   Purpose: Enumerate events for a given date
00689 
00690   Input:
00691     DWORD  ltime            Date at which events should be enumerated
00692 
00693   Output:
00694     char   *event_name      Array containing event names
00695     DWORD  *name_size       Size of name array
00696     char   *event_id        Array containing event IDs
00697     DWORD  *id_size         Size of ID array
00698 
00699   Function value:
00700     HS_SUCCESS              Successful completion
00701     HS_NO_MEMEORY           Out of memory
00702     HS_FILE_ERROR           Cannot open history file
00703 
00704 \********************************************************************/
00705 {
00706    int fh, fhd;
00707    INT status, i, j, n;
00708    DEF_RECORD def_rec;
00709 
00710    if (rpc_is_remote())
00711       return rpc_call(RPC_HS_ENUM_EVENTS, ltime, event_name, name_size, event_id, id_size);
00712 
00713    /* search latest history file */
00714    status = hs_search_file(&ltime, -1);
00715    if (status != HS_SUCCESS) {
00716       cm_msg(MERROR, "hs_enum_events", "cannot find recent history file");
00717       return HS_FILE_ERROR;
00718    }
00719 
00720    /* open history and definition files */
00721    hs_open_file(ltime, "hst", O_RDONLY, &fh);
00722    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
00723    if (fh < 0 || fhd < 0) {
00724       cm_msg(MERROR, "hs_enum_events", "cannot open index files");
00725       return HS_FILE_ERROR;
00726    }
00727    lseek(fhd, 0, SEEK_SET);
00728 
00729    /* loop over definition index file */
00730    n = 0;
00731    do {
00732       /* read event definition */
00733       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
00734       if (j < (int) sizeof(def_rec))
00735          break;
00736 
00737       /* look for existing entry for this event id */
00738       for (i = 0; i < n; i++)
00739          if (event_id[i] == (INT) def_rec.event_id) {
00740             strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
00741             break;
00742          }
00743 
00744       /* new entry found */
00745       if (i == n) {
00746          if (i * NAME_LENGTH > (INT) * name_size || i * sizeof(INT) > (INT) * id_size) {
00747             cm_msg(MERROR, "hs_enum_events", "index buffer too small");
00748             close(fh);
00749             close(fhd);
00750             return HS_NO_MEMORY;
00751          }
00752 
00753          /* copy definition record */
00754          strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
00755          event_id[i] = def_rec.event_id;
00756          n++;
00757       }
00758    } while (TRUE);
00759 
00760    close(fh);
00761    close(fhd);
00762    *name_size = n * NAME_LENGTH;
00763    *id_size = n * sizeof(INT);
00764 
00765    return HS_SUCCESS;
00766 }
00767 
00768 
00769 /********************************************************************/
00770 INT hs_count_events(DWORD ltime, DWORD * count)
00771 /********************************************************************\
00772 
00773   Routine: hs_count_events
00774 
00775   Purpose: Count number of different events for a given date
00776 
00777   Input:
00778     DWORD  ltime            Date at which events should be counted
00779 
00780   Output:
00781     DWORD  *count           Number of different events found
00782 
00783   Function value:
00784     HS_SUCCESS              Successful completion
00785     HS_FILE_ERROR           Cannot open history file
00786 
00787 \********************************************************************/
00788 {
00789    int fh, fhd;
00790    INT status, i, j, n;
00791    DWORD *id;
00792    DEF_RECORD def_rec;
00793 
00794    if (rpc_is_remote())
00795       return rpc_call(RPC_HS_COUNT_EVENTS, ltime, count);
00796 
00797    /* search latest history file */
00798    status = hs_search_file(&ltime, -1);
00799    if (status != HS_SUCCESS) {
00800       cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
00801       return HS_FILE_ERROR;
00802    }
00803 
00804    /* open history and definition files */
00805    hs_open_file(ltime, "hst", O_RDONLY, &fh);
00806    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
00807    if (fh < 0 || fhd < 0) {
00808       cm_msg(MERROR, "hs_count_events", "cannot open index files");
00809       return HS_FILE_ERROR;
00810    }
00811 
00812    /* allocate event id array */
00813    lseek(fhd, 0, SEEK_END);
00814    id = (DWORD *) M_MALLOC(TELL(fhd) / sizeof(def_rec) * sizeof(DWORD));
00815    lseek(fhd, 0, SEEK_SET);
00816 
00817    /* loop over index file */
00818    n = 0;
00819    do {
00820       /* read definition index record */
00821       j = read(fhd, (char *) &def_rec, sizeof(def_rec));
00822       if (j < (int) sizeof(def_rec))
00823          break;
00824 
00825       /* look for existing entries */
00826       for (i = 0; i < n; i++)
00827          if (id[i] == def_rec.event_id)
00828             break;
00829 
00830       /* new entry found */
00831       if (i == n) {
00832          id[i] = def_rec.event_id;
00833          n++;
00834       }
00835    } while (TRUE);
00836 
00837 
00838    M_FREE(id);
00839    close(fh);
00840    close(fhd);
00841    *count = n;
00842 
00843    return HS_SUCCESS;
00844 }
00845 
00846 
00847 /********************************************************************/
00848 INT hs_get_event_id(DWORD ltime, char *name, DWORD * id)
00849 /********************************************************************\
00850 
00851   Routine: hs_get_event_id
00852 
00853   Purpose: Return event ID for a given name. If event cannot be found
00854            in current definition file, go back in time until found
00855 
00856   Input:
00857     DWORD  ltime            Date at which event ID should be looked for
00858 
00859   Output:
00860     DWORD  *id              Event ID
00861 
00862   Function value:
00863     HS_SUCCESS              Successful completion
00864     HS_FILE_ERROR           Cannot open history file
00865     HS_UNDEFINED_EVENT      Event "name" not found
00866 
00867 \********************************************************************/
00868 {
00869    int fh, fhd;
00870    INT status, i;
00871    DWORD lt;
00872    DEF_RECORD def_rec;
00873 
00874    if (rpc_is_remote())
00875       return rpc_call(RPC_HS_GET_EVENT_ID, ltime, name, id);
00876 
00877    /* search latest history file */
00878    if (ltime == 0)
00879       ltime = (DWORD) time(NULL);
00880 
00881    lt = ltime;
00882 
00883    do {
00884       status = hs_search_file(&lt, -1);
00885       if (status != HS_SUCCESS) {
00886          cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
00887          return HS_FILE_ERROR;
00888       }
00889 
00890       /* open history and definition files */
00891       hs_open_file(lt, "hst", O_RDONLY, &fh);
00892       hs_open_file(lt, "idf", O_RDONLY, &fhd);
00893       if (fh < 0 || fhd < 0) {
00894          cm_msg(MERROR, "hs_count_events", "cannot open index files");
00895          return HS_FILE_ERROR;
00896       }
00897 
00898       /* loop over index file */
00899       *id = 0;
00900       do {
00901          /* read definition index record */
00902          i = read(fhd, (char *) &def_rec, sizeof(def_rec));
00903          if (i < (int) sizeof(def_rec))
00904             break;
00905 
00906          if (strcmp(name, def_rec.event_name) == 0) {
00907             *id = def_rec.event_id;
00908             close(fh);
00909             close(fhd);
00910             return HS_SUCCESS;
00911          }
00912       } while (TRUE);
00913 
00914       close(fh);
00915       close(fhd);
00916 
00917       /* not found -> go back one day */
00918       lt -= 3600 * 24;
00919 
00920    } while (lt > ltime - 3600 * 24 * 365 * 10); /* maximum 10 years */
00921 
00922    return HS_UNDEFINED_EVENT;
00923 }
00924 
00925 
00926 /********************************************************************/
00927 INT hs_count_vars(DWORD ltime, DWORD event_id, DWORD * count)
00928 /********************************************************************\
00929 
00930   Routine: hs_count_vars
00931 
00932   Purpose: Count number of variables for a given date and event id
00933 
00934   Input:
00935     DWORD  ltime            Date at which tags should be counted
00936 
00937   Output:
00938     DWORD  *count           Number of tags
00939 
00940   Function value:
00941     HS_SUCCESS              Successful completion
00942     HS_FILE_ERROR           Cannot open history file
00943 
00944 \********************************************************************/
00945 {
00946    int fh, fhd;
00947    INT i, n, status;
00948    DEF_RECORD def_rec;
00949    HIST_RECORD rec;
00950 
00951    if (rpc_is_remote())
00952       return rpc_call(RPC_HS_COUNT_VARS, ltime, event_id, count);
00953 
00954    /* search latest history file */
00955    status = hs_search_file(&ltime, -1);
00956    if (status != HS_SUCCESS) {
00957       cm_msg(MERROR, "hs_count_tags", "cannot find recent history file");
00958       return HS_FILE_ERROR;
00959    }
00960 
00961    /* open history and definition files */
00962    hs_open_file(ltime, "hst", O_RDONLY, &fh);
00963    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
00964    if (fh < 0 || fhd < 0) {
00965       cm_msg(MERROR, "hs_count_tags", "cannot open index files");
00966       return HS_FILE_ERROR;
00967    }
00968 
00969    /* search last definition */
00970    lseek(fhd, 0, SEEK_END);
00971    n = TELL(fhd) / sizeof(def_rec);
00972    def_rec.event_id = 0;
00973    for (i = n - 1; i >= 0; i--) {
00974       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
00975       read(fhd, (char *) &def_rec, sizeof(def_rec));
00976       if (def_rec.event_id == event_id)
00977          break;
00978    }
00979    if (def_rec.event_id != event_id) {
00980       cm_msg(MERROR, "hs_count_tags", "event %d not found in index file", event_id);
00981       return HS_FILE_ERROR;
00982    }
00983 
00984    /* read definition */
00985    lseek(fh, def_rec.def_offset, SEEK_SET);
00986    read(fh, (char *) &rec, sizeof(rec));
00987    *count = rec.data_size / sizeof(TAG);
00988 
00989    close(fh);
00990    close(fhd);
00991 
00992    return HS_SUCCESS;
00993 }
00994 
00995 
00996 /********************************************************************/
00997 INT hs_enum_vars(DWORD ltime, DWORD event_id, char *var_name, DWORD * size, DWORD * var_n, DWORD * n_size)
00998 /********************************************************************\
00999 
01000   Routine: hs_enum_vars
01001 
01002   Purpose: Enumerate variable tags for a given date and event id
01003 
01004   Input:
01005     DWORD  ltime            Date at which tags should be enumerated
01006     DWORD  event_id         Event ID
01007 
01008   Output:
01009     char   *var_name        Array containing variable names
01010     DWORD  *size            Size of name array
01011     DWORD  *var_n           Array size of variable
01012     DWORD  *n_size          Size of n array
01013 
01014   Function value:
01015     HS_SUCCESS              Successful completion
01016     HS_NO_MEMEORY           Out of memory
01017     HS_FILE_ERROR           Cannot open history file
01018 
01019 \********************************************************************/
01020 {
01021    char str[256];
01022    int fh, fhd;
01023    INT i, n, status;
01024    DEF_RECORD def_rec;
01025    HIST_RECORD rec;
01026    TAG *tag;
01027 
01028    if (rpc_is_remote())
01029       return rpc_call(RPC_HS_ENUM_VARS, ltime, event_id, var_name, size);
01030 
01031    /* search latest history file */
01032    status = hs_search_file(&ltime, -1);
01033    if (status != HS_SUCCESS) {
01034       cm_msg(MERROR, "hs_enum_vars", "cannot find recent history file");
01035       return HS_FILE_ERROR;
01036    }
01037 
01038    /* open history and definition files */
01039    hs_open_file(ltime, "hst", O_RDONLY, &fh);
01040    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
01041    if (fh < 0 || fhd < 0) {
01042       cm_msg(MERROR, "hs_enum_vars", "cannot open index files");
01043       return HS_FILE_ERROR;
01044    }
01045 
01046    /* search last definition */
01047    lseek(fhd, 0, SEEK_END);
01048    n = TELL(fhd) / sizeof(def_rec);
01049    def_rec.event_id = 0;
01050    for (i = n - 1; i >= 0; i--) {
01051       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
01052       read(fhd, (char *) &def_rec, sizeof(def_rec));
01053       if (def_rec.event_id == event_id)
01054          break;
01055    }
01056    if (def_rec.event_id != event_id) {
01057       cm_msg(MERROR, "hs_enum_vars", "event %d not found in index file", event_id);
01058       return HS_FILE_ERROR;
01059    }
01060 
01061    /* read definition header */
01062    lseek(fh, def_rec.def_offset, SEEK_SET);
01063    read(fh, (char *) &rec, sizeof(rec));
01064    read(fh, str, NAME_LENGTH);
01065 
01066    /* read event definition */
01067    n = rec.data_size / sizeof(TAG);
01068    tag = (TAG *) M_MALLOC(rec.data_size);
01069    read(fh, (char *) tag, rec.data_size);
01070 
01071    if (n * NAME_LENGTH > (INT) * size || n * sizeof(DWORD) > *n_size) {
01072 
01073       /* store partial definition */
01074       for (i = 0; i < (INT) * size / NAME_LENGTH; i++) {
01075          strcpy(var_name + i * NAME_LENGTH, tag[i].name);
01076          var_n[i] = tag[i].n_data;
01077       }
01078 
01079       cm_msg(MERROR, "hs_enum_vars", "tag buffer too small");
01080       M_FREE(tag);
01081       close(fh);
01082       close(fhd);
01083       return HS_NO_MEMORY;
01084    }
01085 
01086    /* store full definition */
01087    for (i = 0; i < n; i++) {
01088       strcpy(var_name + i * NAME_LENGTH, tag[i].name);
01089       var_n[i] = tag[i].n_data;
01090    }
01091    *size = n * NAME_LENGTH;
01092    *n_size = n * sizeof(DWORD);
01093 
01094    M_FREE(tag);
01095    close(fh);
01096    close(fhd);
01097 
01098    return HS_SUCCESS;
01099 }
01100 
01101 
01102 /********************************************************************/
01103 INT hs_get_var(DWORD ltime, DWORD event_id, char *var_name, DWORD * type, INT * n_data)
01104 /********************************************************************\
01105 
01106   Routine: hs_get_var
01107 
01108   Purpose: Get definition for certain variable
01109 
01110   Input:
01111     DWORD  ltime            Date at which variable definition should
01112                             be returned
01113     DWORD  event_id         Event ID
01114     char   *var_name        Name of variable
01115 
01116   Output:
01117     INT    *type            Type of variable
01118     INT    *n_data          Number of items in variable
01119 
01120   Function value:
01121     HS_SUCCESS              Successful completion
01122     HS_NO_MEMEORY           Out of memory
01123     HS_FILE_ERROR           Cannot open history file
01124 
01125 \********************************************************************/
01126 {
01127    char str[256];
01128    int fh, fhd;
01129    INT i, n, status;
01130    DEF_RECORD def_rec;
01131    HIST_RECORD rec;
01132    TAG *tag;
01133 
01134    if (rpc_is_remote())
01135       return rpc_call(RPC_HS_GET_VAR, ltime, event_id, var_name, type, n_data);
01136 
01137    /* search latest history file */
01138    status = hs_search_file(&ltime, -1);
01139    if (status != HS_SUCCESS) {
01140       cm_msg(MERROR, "hs_get_var", "cannot find recent history file");
01141       return HS_FILE_ERROR;
01142    }
01143 
01144    /* open history and definition files */
01145    hs_open_file(ltime, "hst", O_RDONLY, &fh);
01146    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
01147    if (fh < 0 || fhd < 0) {
01148       cm_msg(MERROR, "hs_get_var", "cannot open index files");
01149       return HS_FILE_ERROR;
01150    }
01151 
01152    /* search last definition */
01153    lseek(fhd, 0, SEEK_END);
01154    n = TELL(fhd) / sizeof(def_rec);
01155    def_rec.event_id = 0;
01156    for (i = n - 1; i >= 0; i--) {
01157       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
01158       read(fhd, (char *) &def_rec, sizeof(def_rec));
01159       if (def_rec.event_id == event_id)
01160          break;
01161    }
01162    if (def_rec.event_id != event_id) {
01163       cm_msg(MERROR, "hs_get_var", "event %d not found in index file", event_id);
01164       return HS_FILE_ERROR;
01165    }
01166 
01167    /* read definition header */
01168    lseek(fh, def_rec.def_offset, SEEK_SET);
01169    read(fh, (char *) &rec, sizeof(rec));
01170    read(fh, str, NAME_LENGTH);
01171 
01172    /* read event definition */
01173    n = rec.data_size / sizeof(TAG);
01174    tag = (TAG *) M_MALLOC(rec.data_size);
01175    read(fh, (char *) tag, rec.data_size);
01176 
01177    /* search variable */
01178    for (i = 0; i < n; i++)
01179       if (strcmp(tag[i].name, var_name) == 0)
01180          break;
01181 
01182    close(fh);
01183    close(fhd);
01184 
01185    if (i < n) {
01186       *type = tag[i].type;
01187       *n_data = tag[i].n_data;
01188    } else {
01189       *type = *n_data = 0;
01190       cm_msg(MERROR, "hs_get_var", "variable %s not found", var_name);
01191       M_FREE(tag);
01192       return HS_UNDEFINED_VAR;
01193    }
01194 
01195    M_FREE(tag);
01196    return HS_SUCCESS;
01197 }
01198 
01199 
01200 /********************************************************************/
01201 INT hs_get_tags(DWORD ltime, DWORD event_id, char event_name[NAME_LENGTH], int* n_tags, TAG** tags)
01202 /********************************************************************\
01203 
01204   Routine: hs_get_tags
01205 
01206   Purpose: Get tags for event id
01207 
01208   Input:
01209     DWORD  ltime            Date at which variable definition should
01210                             be returned
01211     DWORD  event_id         Event ID
01212 
01213   Output:
01214     char    event_name[NAME_LENGTH] Event name from history file
01215     INT    *n_tags          Number of tags
01216     TAG   **tags            Pointer to array of tags (should be free()ed by the caller)
01217 
01218   Function value:
01219     HS_SUCCESS              Successful completion
01220     HS_NO_MEMEORY           Out of memory
01221     HS_FILE_ERROR           Cannot open history file
01222 
01223 \********************************************************************/
01224 {
01225    int fh, fhd;
01226    INT i, n, status;
01227    DEF_RECORD def_rec;
01228    HIST_RECORD rec;
01229 
01230    *n_tags = 0;
01231    *tags = NULL;
01232 
01233    if (rpc_is_remote())
01234       assert(!"RPC not implemented");
01235 
01236    /* search latest history file */
01237    status = hs_search_file(&ltime, -1);
01238    if (status != HS_SUCCESS) {
01239       cm_msg(MERROR, "hs_get_tags", "cannot find recent history file");
01240       return HS_FILE_ERROR;
01241    }
01242 
01243    /* open history and definition files */
01244    hs_open_file(ltime, "hst", O_RDONLY, &fh);
01245    hs_open_file(ltime, "idf", O_RDONLY, &fhd);
01246    if (fh < 0 || fhd < 0) {
01247       cm_msg(MERROR, "hs_get_tags", "cannot open index files");
01248       if (fh>0)
01249          close(fh);
01250       if (fhd>0)
01251          close(fhd);
01252       return HS_FILE_ERROR;
01253    }
01254 
01255    /* search last definition */
01256    lseek(fhd, 0, SEEK_END);
01257    n = TELL(fhd) / sizeof(def_rec);
01258    def_rec.event_id = 0;
01259    for (i = n - 1; i >= 0; i--) {
01260       lseek(fhd, i * sizeof(def_rec), SEEK_SET);
01261       read(fhd, (char *) &def_rec, sizeof(def_rec));
01262       if (def_rec.event_id == event_id)
01263          break;
01264    }
01265 
01266    if (def_rec.event_id != event_id) {
01267       //cm_msg(MERROR, "hs_get_tags", "event %d not found in index file", event_id);
01268       close(fh);
01269       close(fhd);
01270       return HS_UNDEFINED_EVENT;
01271    }
01272 
01273    /* read definition header */
01274    lseek(fh, def_rec.def_offset, SEEK_SET);
01275    status = read(fh, (char *) &rec, sizeof(rec));
01276    assert(status == sizeof(rec));
01277 
01278    status = read(fh, event_name, NAME_LENGTH);
01279    assert(status == NAME_LENGTH);
01280 
01281    /* read event definition */
01282    *n_tags = rec.data_size / sizeof(TAG);
01283 
01284    *tags = (TAG*) malloc(rec.data_size);
01285 
01286    status = read(fh, (char *) (*tags), rec.data_size);
01287    assert(status == rec.data_size);
01288 
01289    close(fh);
01290    close(fhd);
01291 
01292    return HS_SUCCESS;
01293 }
01294 
01295 
01296 INT hs_read(DWORD event_id, DWORD start_time, DWORD end_time, DWORD interval, char *tag_name, DWORD var_index, DWORD * time_buffer, DWORD * tbsize, void *data_buffer, DWORD * dbsize, DWORD * type, DWORD * n)
01297 /********************************************************************\
01298 
01299   Routine: hs_read
01300 
01301   Purpose: Read history for a variable at a certain time interval
01302 
01303   Input:
01304     DWORD  event_id         Event ID
01305     DWORD  start_time       Starting Date/Time
01306     DWORD  end_time         End Date/Time
01307     DWORD  interval         Minimum time in seconds between reported
01308                             events. Can be used to skip events
01309     char   *tag_name        Variable name inside event
01310     DWORD  var_index        Index if variable is array
01311 
01312   Output:
01313     DWORD  *time_buffer     Buffer containing times for each value
01314     DWORD  *tbsize          Size of time buffer
01315     void   *data_buffer     Buffer containing variable values
01316     DWORD  *dbsize          Data buffer size
01317     DWORD  *type            Type of variable (one of TID_xxx)
01318     DWORD  *n               Number of time/value pairs found
01319                             in specified interval and placed into
01320                             time_buffer and data_buffer
01321 
01322 
01323   Function value:
01324     HS_SUCCESS              Successful completion
01325     HS_NO_MEMEORY           Out of memory
01326     HS_FILE_ERROR           Cannot open history file
01327     HS_WRONG_INDEX          var_index exceeds array size of variable
01328     HS_UNDEFINED_VAR        Variable "tag_name" not found in event
01329     HS_TRUNCATED            Buffer too small, data has been truncated
01330 
01331 \********************************************************************/
01332 {
01333    DWORD prev_time, last_irec_time;
01334    int fh, fhd, fhi, cp = 0;
01335    INT i, delta, index = 0, status, cache_size;
01336    INDEX_RECORD irec, *pirec;
01337    HIST_RECORD rec, drec;
01338    INT old_def_offset, var_size = 0, var_offset = 0;
01339    TAG *tag;
01340    char str[NAME_LENGTH];
01341    struct tm *tms;
01342    char *cache = NULL;
01343    time_t ltime;
01344    int rd;
01345 
01346    //printf("hs_read event %d, time %d:%d, tagname: \'%s\', varindex: %d\n", event_id, start_time, end_time, tag_name, var_index);
01347 
01348 #if 0
01349    if (rpc_is_remote())
01350       return rpc_call(RPC_HS_READ, event_id, start_time, end_time, interval, tag_name, var_index, time_buffer, tbsize, data_buffer, dbsize, type, n);
01351 #endif
01352 
01353    /* if not time given, use present to one hour in past */
01354    if (start_time == 0)
01355       start_time = (DWORD) time(NULL) - 3600;
01356    if (end_time == 0)
01357       end_time = (DWORD) time(NULL);
01358 
01359    *n = 0;
01360    prev_time = 0;
01361    last_irec_time = start_time;
01362 
01363    /* search history file for start_time */
01364    status = hs_search_file(&start_time, 1);
01365    if (status != HS_SUCCESS) {
01366       cm_msg(MERROR, "hs_read", "cannot find recent history file");
01367       *tbsize = *dbsize = *n = 0;
01368       return HS_FILE_ERROR;
01369    }
01370 
01371    /* open history and definition files */
01372    hs_open_file(start_time, "hst", O_RDONLY, &fh);
01373    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
01374    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
01375    if (fh < 0 || fhd < 0 || fhi < 0) {
01376       cm_msg(MERROR, "hs_read", "cannot open index files");
01377       *tbsize = *dbsize = *n = 0;
01378       if (fh > 0)
01379          close(fh);
01380       if (fhd > 0)
01381          close(fhd);
01382       if (fhi > 0)
01383          close(fhi);
01384       return HS_FILE_ERROR;
01385    }
01386 
01387    /* try to read index file into cache */
01388    lseek(fhi, 0, SEEK_END);
01389    cache_size = TELL(fhi);
01390 
01391    if (cache_size == 0) {
01392       goto nextday;
01393    }
01394 
01395    if (cache_size > 0) {
01396       cache = (char *) M_MALLOC(cache_size);
01397       if (cache) {
01398          lseek(fhi, 0, SEEK_SET);
01399          i = read(fhi, cache, cache_size);
01400          if (i < cache_size) {
01401             M_FREE(cache);
01402             if (fh > 0)
01403                close(fh);
01404             if (fhd > 0)
01405                close(fhd);
01406             if (fhi > 0)
01407                close(fhi);
01408             return HS_FILE_ERROR;
01409          }
01410       }
01411 
01412       /* search record closest to start time */
01413       if (cache == NULL) {
01414          lseek(fhi, 0, SEEK_END);
01415          delta = (TELL(fhi) / sizeof(irec)) / 2;
01416          lseek(fhi, delta * sizeof(irec), SEEK_SET);
01417          do {
01418             delta = (int) (abs(delta) / 2.0 + 0.5);
01419             rd = read(fhi, (char *) &irec, sizeof(irec));
01420             assert(rd == sizeof(irec));
01421             if (irec.time > start_time)
01422                delta = -delta;
01423 
01424             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
01425          } while (abs(delta) > 1 && irec.time != start_time);
01426          rd = read(fhi, (char *) &irec, sizeof(irec));
01427          assert(rd == sizeof(irec));
01428          if (irec.time > start_time)
01429             delta = -abs(delta);
01430 
01431          i = TELL(fhi) + (delta - 1) * sizeof(irec);
01432          if (i <= 0)
01433             lseek(fhi, 0, SEEK_SET);
01434          else
01435             lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
01436          rd = read(fhi, (char *) &irec, sizeof(irec));
01437          assert(rd == sizeof(irec));
01438       } else {
01439          delta = (cache_size / sizeof(irec)) / 2;
01440          cp = delta * sizeof(irec);
01441          do {
01442             delta = (int) (abs(delta) / 2.0 + 0.5);
01443             pirec = (INDEX_RECORD *) (cache + cp);
01444 
01445             //printf("pirec %p, cache %p, cp %d\n", pirec, cache, cp);
01446 
01447             if (pirec->time > start_time)
01448                delta = -delta;
01449 
01450             cp = cp + delta * sizeof(irec);
01451 
01452             if (cp < 0)
01453                cp = 0;
01454          } while (abs(delta) > 1 && pirec->time != start_time);
01455          pirec = (INDEX_RECORD *) (cache + cp);
01456          if (pirec->time > start_time)
01457             delta = -abs(delta);
01458 
01459          if (cp <= delta * (int) sizeof(irec))
01460             cp = 0;
01461          else
01462             cp = cp + delta * sizeof(irec);
01463 
01464          if (cp >= cache_size)
01465             cp = cache_size - sizeof(irec);
01466          if (cp < 0)
01467             cp = 0;
01468 
01469          memcpy(&irec, (INDEX_RECORD *) (cache + cp), sizeof(irec));
01470          cp += sizeof(irec);
01471       }
01472    } else {                     /* file size > 0 */
01473 
01474       cache = NULL;
01475       irec.time = start_time;
01476    }
01477 
01478    /* read records, skip wrong IDs */
01479    old_def_offset = -1;
01480    last_irec_time = start_time - 24 * 60 * 60;
01481    do {
01482       //printf("time %d -> %d\n", last_irec_time, irec.time);
01483 
01484       if (irec.time < last_irec_time) {
01485          cm_msg(MERROR, "hs_read", "corrupted history data: time does not increase: %d -> %d", last_irec_time, irec.time);
01486          //*tbsize = *dbsize = *n = 0;
01487          if (fh > 0)
01488             close(fh);
01489          if (fhd > 0)
01490             close(fhd);
01491          if (fhi > 0)
01492             close(fhi);
01493          hs_gen_index(last_irec_time);
01494          return HS_SUCCESS;
01495       }
01496       last_irec_time = irec.time;
01497       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
01498          /* check if record time more than "interval" seconds after previous time */
01499          if (irec.time >= prev_time + interval) {
01500             prev_time = irec.time;
01501             lseek(fh, irec.offset, SEEK_SET);
01502             rd = read(fh, (char *) &rec, sizeof(rec));
01503             if (rd != sizeof(rec)) {
01504                cm_msg(MERROR, "hs_read", "corrupted history data at time %d: read() of %d bytes returned %d, errno %d (%s)", (int) irec.time, sizeof(rec), rd, errno, strerror(errno));
01505                //*tbsize = *dbsize = *n = 0;
01506                if (fh > 0)
01507                   close(fh);
01508                if (fhd > 0)
01509                   close(fhd);
01510                if (fhi > 0)
01511                   close(fhi);
01512                hs_gen_index(last_irec_time);
01513                return HS_SUCCESS;
01514             }
01515 
01516             /* if definition changed, read new definition */
01517             if ((INT) rec.def_offset != old_def_offset) {
01518                lseek(fh, rec.def_offset, SEEK_SET);
01519                read(fh, (char *) &drec, sizeof(drec));
01520                read(fh, str, NAME_LENGTH);
01521 
01522                tag = (TAG *) M_MALLOC(drec.data_size);
01523                if (tag == NULL) {
01524                   *n = *tbsize = *dbsize = 0;
01525                   if (cache)
01526                      M_FREE(cache);
01527                   close(fh);
01528                   close(fhd);
01529                   close(fhi);
01530                   return HS_NO_MEMORY;
01531                }
01532                read(fh, (char *) tag, drec.data_size);
01533 
01534                /* find index of tag_name in new definition */
01535                index = -1;
01536                for (i = 0; (DWORD) i < drec.data_size / sizeof(TAG); i++)
01537                   if (equal_ustring(tag[i].name, tag_name)) {
01538                      index = i;
01539                      break;
01540                   }
01541 
01542                /*
01543                   if ((DWORD) i == drec.data_size/sizeof(TAG))
01544                   {
01545                   *n = *tbsize = *dbsize = 0;
01546                   if (cache)
01547                   M_FREE(cache);
01548 
01549                   return HS_UNDEFINED_VAR;
01550                   }
01551                 */
01552 
01553                if (index >= 0 && var_index >= tag[i].n_data) {
01554                   *n = *tbsize = *dbsize = 0;
01555                   if (cache)
01556                      M_FREE(cache);
01557                   M_FREE(tag);
01558                   close(fh);
01559                   close(fhd);
01560                   close(fhi);
01561                   return HS_WRONG_INDEX;
01562                }
01563 
01564                /* calculate offset for variable */
01565                if (index >= 0) {
01566                   *type = tag[i].type;
01567 
01568                   /* loop over all previous variables */
01569                   for (i = 0, var_offset = 0; i < index; i++)
01570                      var_offset += rpc_tid_size(tag[i].type) * tag[i].n_data;
01571 
01572                   /* strings have size n_data */
01573                   if (tag[index].type == TID_STRING)
01574                      var_size = tag[i].n_data;
01575                   else
01576                      var_size = rpc_tid_size(tag[index].type);
01577 
01578                   var_offset += var_size * var_index;
01579                }
01580 
01581                M_FREE(tag);
01582                old_def_offset = rec.def_offset;
01583                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
01584             }
01585 
01586             if (index >= 0) {
01587                /* check if data fits in buffers */
01588                if ((*n) * sizeof(DWORD) >= *tbsize || (*n) * var_size >= *dbsize) {
01589                   *dbsize = (*n) * var_size;
01590                   *tbsize = (*n) * sizeof(DWORD);
01591                   if (cache)
01592                      M_FREE(cache);
01593                   close(fh);
01594                   close(fhd);
01595                   close(fhi);
01596                   return HS_TRUNCATED;
01597                }
01598 
01599                /* copy time from header */
01600                time_buffer[*n] = irec.time;
01601 
01602                /* copy data from record */
01603                lseek(fh, var_offset, SEEK_CUR);
01604                read(fh, (char *) data_buffer + (*n) * var_size, var_size);
01605 
01606                /* increment counter */
01607                (*n)++;
01608             }
01609          }
01610       }
01611 
01612       /* read next index record */
01613       if (cache) {
01614          if (cp >= cache_size) {
01615             i = -1;
01616             M_FREE(cache);
01617             cache = NULL;
01618          } else {
01619 
01620           try_again:
01621 
01622             i = sizeof(irec);
01623 
01624             memcpy(&irec, cache + cp, sizeof(irec));
01625             cp += sizeof(irec);
01626 
01627             /* if history file is broken ... */
01628             if (irec.time < last_irec_time || irec.time > last_irec_time + 24 * 60 * 60) {
01629                //if (irec.time < last_irec_time) {
01630                //printf("time %d -> %d, cache_size %d, cp %d\n", last_irec_time, irec.time, cache_size, cp);
01631 
01632                //printf("Seeking next record...\n");
01633 
01634                while (cp < cache_size) {
01635                   DWORD *evidp = (DWORD *) (cache + cp);
01636                   if (*evidp == event_id) {
01637                      //printf("Found at cp %d\n", cp);
01638                      goto try_again;
01639                   }
01640 
01641                   cp++;
01642                }
01643 
01644                i = -1;
01645             }
01646          }
01647       } else
01648          i = read(fhi, (char *) &irec, sizeof(irec));
01649 
01650       /* end of file: search next history file */
01651       if (i <= 0) {
01652          close(fh);
01653          close(fhd);
01654          close(fhi);
01655          fh = fhd = fhi = 0;
01656 
01657        nextday:
01658 
01659          /* advance one day */
01660          ltime = (time_t) last_irec_time;
01661          tms = localtime(&ltime);
01662          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
01663          last_irec_time = (DWORD) mktime(tms);
01664 
01665          last_irec_time += 3600 * 24;
01666 
01667          if (last_irec_time > end_time)
01668             break;
01669 
01670          /* search next file */
01671          status = hs_search_file(&last_irec_time, 1);
01672          if (status != HS_SUCCESS)
01673             break;
01674 
01675          /* open history and definition files */
01676          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
01677          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
01678          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
01679          if (fh < 0 || fhd < 0 || fhi < 0) {
01680             cm_msg(MERROR, "hs_read", "cannot open index files");
01681             break;
01682          }
01683 
01684          /* try to read index file into cache */
01685          lseek(fhi, 0, SEEK_END);
01686          cache_size = TELL(fhi);
01687 
01688          if (cache_size == 0) {
01689             goto nextday;
01690          }
01691 
01692          lseek(fhi, 0, SEEK_SET);
01693          cache = (char *) M_MALLOC(cache_size);
01694          if (cache) {
01695             i = read(fhi, cache, cache_size);
01696             if (i < cache_size)
01697                break;
01698             /* read first record */
01699             cp = 0;
01700             memcpy(&irec, cache, sizeof(irec));
01701          } else {
01702             /* read first record */
01703             i = read(fhi, (char *) &irec, sizeof(irec));
01704             if (i <= 0)
01705                break;
01706             assert(i == sizeof(irec));
01707          }
01708 
01709          /* old definition becomes invalid */
01710          old_def_offset = -1;
01711       }
01712       //if (event_id==4 && irec.event_id == event_id)
01713       //  printf("time %d end %d\n", irec.time, end_time);
01714    } while (irec.time < end_time);
01715 
01716    if (cache)
01717       M_FREE(cache);
01718    if (fh)
01719       close(fh);
01720    if (fhd)
01721       close(fhd);
01722    if (fhi)
01723       close(fhi);
01724 
01725    *dbsize = *n * var_size;
01726    *tbsize = *n * sizeof(DWORD);
01727 
01728    return HS_SUCCESS;
01729 }
01730 
01731 /**dox***************************************************************/
01732 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
01733 
01734 /********************************************************************/
01735 /**
01736 Display history for a given event at stdout. The output
01737 can be redirected to be read by Excel for example. 
01738 @param event_id         Event ID
01739 @param start_time       Starting Date/Time
01740 @param end_time         End Date/Time
01741 @param interval         Minimum time in seconds between reported                                                                                
01742                             events. Can be used to skip events
01743 @param binary_time      Display DWORD time stamp
01744 @return HS_SUCCESS, HS_FILE_ERROR
01745 */
01746 /********************************************************************/
01747 INT hs_dump(DWORD event_id, DWORD start_time, DWORD end_time, DWORD interval, BOOL binary_time)
01748 {
01749    DWORD prev_time, last_irec_time;
01750    time_t ltime;
01751    int fh, fhd, fhi;
01752    INT i, j, delta, status, n_tag = 0, old_n_tag = 0;
01753    INDEX_RECORD irec;
01754    HIST_RECORD rec, drec;
01755    INT old_def_offset, offset;
01756    TAG *tag = NULL, *old_tag = NULL;
01757    char str[NAME_LENGTH], data_buffer[10000];
01758    struct tm *tms;
01759 
01760    /* if not time given, use present to one hour in past */
01761    if (start_time == 0)
01762       start_time = (DWORD) time(NULL) - 3600;
01763    if (end_time == 0)
01764       end_time = (DWORD) time(NULL);
01765 
01766    /* search history file for start_time */
01767    status = hs_search_file(&start_time, 1);
01768    if (status != HS_SUCCESS) {
01769       cm_msg(MERROR, "hs_dump", "cannot find recent history file");
01770       return HS_FILE_ERROR;
01771    }
01772 
01773    /* open history and definition files */
01774    hs_open_file(start_time, "hst", O_RDONLY, &fh);
01775    hs_open_file(start_time, "idf", O_RDONLY, &fhd);
01776    hs_open_file(start_time, "idx", O_RDONLY, &fhi);
01777    if (fh < 0 || fhd < 0 || fhi < 0) {
01778       cm_msg(MERROR, "hs_dump", "cannot open index files");
01779       return HS_FILE_ERROR;
01780    }
01781 
01782    /* search record closest to start time */
01783    lseek(fhi, 0, SEEK_END);
01784    delta = (TELL(fhi) / sizeof(irec)) / 2;
01785    lseek(fhi, delta * sizeof(irec), SEEK_SET);
01786    do {
01787       delta = (int) (abs(delta) / 2.0 + 0.5);
01788       read(fhi, (char *) &irec, sizeof(irec));
01789       if (irec.time > start_time)
01790          delta = -delta;
01791 
01792       i = lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
01793    } while (abs(delta) > 1 && irec.time != start_time);
01794    read(fhi, (char *) &irec, sizeof(irec));
01795    if (irec.time > start_time)
01796       delta = -abs(delta);
01797 
01798    i = TELL(fhi) + (delta - 1) * sizeof(irec);
01799    if (i <= 0)
01800       lseek(fhi, 0, SEEK_SET);
01801    else
01802       lseek(fhi, (delta - 1) * sizeof(irec), SEEK_CUR);
01803    read(fhi, (char *) &irec, sizeof(irec));
01804 
01805    /* read records, skip wrong IDs */
01806    old_def_offset = -1;
01807    prev_time = 0;
01808    last_irec_time = 0;
01809    do {
01810       if (irec.time < last_irec_time) {
01811          cm_msg(MERROR, "hs_dump", "corrupted history data: time does not increase: %d -> %d", last_irec_time, irec.time);
01812          hs_gen_index(last_irec_time);
01813          return HS_FILE_ERROR;
01814       }
01815       last_irec_time = irec.time;
01816       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
01817          if (irec.time >= prev_time + interval) {
01818             prev_time = irec.time;
01819             lseek(fh, irec.offset, SEEK_SET);
01820             read(fh, (char *) &rec, sizeof(rec));
01821 
01822             /* if definition changed, read new definition */
01823             if ((INT) rec.def_offset != old_def_offset) {
01824                lseek(fh, rec.def_offset, SEEK_SET);
01825                read(fh, (char *) &drec, sizeof(drec));
01826                read(fh, str, NAME_LENGTH);
01827 
01828                if (tag == NULL)
01829                   tag = (TAG *) M_MALLOC(drec.data_size);
01830                else
01831                   tag = (TAG *) realloc(tag, drec.data_size);
01832                if (tag == NULL)
01833                   return HS_NO_MEMORY;
01834                read(fh, (char *) tag, drec.data_size);
01835                n_tag = drec.data_size / sizeof(TAG);
01836 
01837                /* print tag names if definition has changed */
01838                if (old_tag == NULL || old_n_tag != n_tag || memcmp(old_tag, tag, drec.data_size) != 0) {
01839                   printf("Date\t");
01840                   for (i = 0; i < n_tag; i++) {
01841                      if (tag[i].n_data == 1 || tag[i].type == TID_STRING)
01842                         printf("%s\t", tag[i].name);
01843                      else
01844                         for (j = 0; j < (INT) tag[i].n_data; j++)
01845                            printf("%s%d\t", tag[i].name, j);
01846                   }
01847                   printf("\n");
01848 
01849                   if (old_tag == NULL)
01850                      old_tag = (TAG *) M_MALLOC(drec.data_size);
01851                   else
01852                      old_tag = (TAG *) realloc(old_tag, drec.data_size);
01853                   memcpy(old_tag, tag, drec.data_size);
01854                   old_n_tag = n_tag;
01855                }
01856 
01857                old_def_offset = rec.def_offset;
01858                lseek(fh, irec.offset + sizeof(rec), SEEK_SET);
01859             }
01860 
01861             /* print time from header */
01862             if (binary_time)
01863                printf("%d ", irec.time);
01864             else {
01865                ltime = (time_t) irec.time;
01866                sprintf(str, "%s", ctime(&ltime) + 4);
01867                str[20] = '\t';
01868                printf(str);
01869             }
01870 
01871             /* read data */
01872             read(fh, data_buffer, rec.data_size);
01873 
01874             /* interprete data from tag definition */
01875             offset = 0;
01876             for (i = 0; i < n_tag; i++) {
01877                /* strings have a length of n_data */
01878                if (tag[i].type == TID_STRING) {
01879                   printf("%s\t", data_buffer + offset);
01880                   offset += tag[i].n_data;
01881                } else if (tag[i].n_data == 1) {
01882                   /* non-array data */
01883                   db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0, tag[i].type);
01884                   printf("%s\t", str);
01885                   offset += rpc_tid_size(tag[i].type);
01886                } else
01887                   /* loop over array data */
01888                   for (j = 0; j < (INT) tag[i].n_data; j++) {
01889                      db_sprintf(str, data_buffer + offset, rpc_tid_size(tag[i].type), 0, tag[i].type);
01890                      printf("%s\t", str);
01891                      offset += rpc_tid_size(tag[i].type);
01892                   }
01893             }
01894             printf("\n");
01895          }
01896       }
01897 
01898       /* read next index record */
01899       i = read(fhi, (char *) &irec, sizeof(irec));
01900 
01901       /* end of file: search next history file */
01902       if (i <= 0) {
01903          close(fh);
01904          close(fhd);
01905          close(fhi);
01906 
01907          /* advance one day */
01908          ltime = (time_t) last_irec_time;
01909          tms = localtime(&ltime);
01910          tms->tm_hour = tms->tm_min = tms->tm_sec = 0;
01911          last_irec_time = (DWORD) mktime(tms);
01912 
01913          last_irec_time += 3600 * 24;
01914          if (last_irec_time > end_time)
01915             break;
01916 
01917          /* search next file */
01918          status = hs_search_file((DWORD *) & last_irec_time, 1);
01919          if (status != HS_SUCCESS)
01920             break;
01921 
01922          /* open history and definition files */
01923          hs_open_file(last_irec_time, "hst", O_RDONLY, &fh);
01924          hs_open_file(last_irec_time, "idf", O_RDONLY, &fhd);
01925          hs_open_file(last_irec_time, "idx", O_RDONLY, &fhi);
01926          if (fh < 0 || fhd < 0 || fhi < 0) {
01927             cm_msg(MERROR, "hs_dump", "cannot open index files");
01928             break;
01929          }
01930 
01931          /* read first record */
01932          i = read(fhi, (char *) &irec, sizeof(irec));
01933          if (i <= 0)
01934             break;
01935 
01936          /* old definition becomes invalid */
01937          old_def_offset = -1;
01938       }
01939    } while (irec.time < end_time);
01940 
01941    M_FREE(tag);
01942    M_FREE(old_tag);
01943    close(fh);
01944    close(fhd);
01945    close(fhi);
01946 
01947    return HS_SUCCESS;
01948 }
01949 
01950 /**dox***************************************************************/
01951 #ifndef DOXYGEN_SHOULD_SKIP_THIS
01952 
01953 /********************************************************************/
01954 INT hs_fdump(char *file_name, DWORD id, BOOL binary_time)
01955 /********************************************************************\
01956 
01957   Routine: hs_fdump
01958 
01959   Purpose: Display history for a given history file
01960 
01961   Input:
01962     char   *file_name       Name of file to dump
01963     DWORD  event_id         Event ID
01964     BOOL   binary_time      Display DWORD time stamp
01965 
01966   Output:
01967     <screen output>
01968 
01969   Function value:
01970     HS_SUCCESS              Successful completion
01971     HS_FILE_ERROR           Cannot open history file
01972 
01973 \********************************************************************/
01974 {
01975    int fh;
01976    INT n;
01977    time_t ltime;
01978    HIST_RECORD rec;
01979    char event_name[NAME_LENGTH];
01980    char str[256];
01981 
01982    /* open file, add O_BINARY flag for Windows NT */
01983    sprintf(str, "%s%s", _hs_path_name, file_name);
01984    fh = open(str, O_RDONLY | O_BINARY, 0644);
01985    if (fh < 0) {
01986       cm_msg(MERROR, "hs_fdump", "cannot open file %s", str);
01987       return HS_FILE_ERROR;
01988    }
01989 
01990    /* loop over file records in .hst file */
01991    do {
01992       n = read(fh, (char *) &rec, sizeof(rec));
01993       if (n < sizeof(rec))
01994          break;
01995 
01996       /* check if record type is definition */
01997       if (rec.record_type == RT_DEF) {
01998          /* read name */
01999          read(fh, event_name, sizeof(event_name));
02000 
02001          if (rec.event_id == id || id == 0)
02002             printf("Event definition %s, ID %d\n", event_name, rec.event_id);
02003 
02004          /* skip tags */
02005          lseek(fh, rec.data_size, SEEK_CUR);
02006       } else {
02007          /* print data record */
02008          if (binary_time)
02009             sprintf(str, "%d ", rec.time);
02010          else {
02011             ltime = (time_t) rec.time;
02012             strcpy(str, ctime(&ltime) + 4);
02013             str[15] = 0;
02014          }
02015          if (rec.event_id == id || id == 0)
02016             printf("ID %d, %s, size %d\n", rec.event_id, str, rec.data_size);
02017 
02018          /* skip data */
02019          lseek(fh, rec.data_size, SEEK_CUR);
02020       }
02021 
02022    } while (TRUE);
02023 
02024    close(fh);
02025 
02026    return HS_SUCCESS;
02027 }
02028 #endif                          /* OS_VXWORKS hs section */
02029 
02030 /**dox***************************************************************/
02031 #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
02032 
02033 /**dox***************************************************************/
02034          /** @} *//* end of hsfunctioncode */
02035 
02036 /**dox***************************************************************/

Midas DOC Version 3.0.0 ---- PSI Stefan Ritt ----
Contributions: Pierre-Andre Amaudruz - Sergio Ballestrero - Suzannah Daviel - Doxygen - Peter Green - Qing Gu - Greg Hackman - Gertjan Hofman - Paul Knowles - Exaos Lee - Rudi Meier - Glenn Moloney - Dave Morris - John M O'Donnell - Konstantin Olchanski - Renee Poutissou - Tamsen Schurman - Andreas Suter - Jan M.Wouters - Piotr Adam Zolnierczuk