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

Midas DOC Version 2.0.2 ---- 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