lazylogger.c

Go to the documentation of this file.
00001 /********************************************************************\
00002 Name:         lazylogger.c
00003 Created by:   Pierre-Andre Amaudruz
00004 
00005 Contents:     Disk to Tape copier for background job.
00006 
00007 $Id: lazylogger.c 4841 2010-09-27 22:48:59Z amaudruz $
00008 
00009 \********************************************************************/
00010 #include "midas.h"
00011 #include "msystem.h"
00012 
00013 #ifdef HAVE_FTPLIB
00014 #include "ftplib.h"
00015 #endif
00016 
00017 #include <mdsupport.h>
00018 #include <assert.h>
00019 
00020 #include <vector>
00021 #include <string>
00022 #include <algorithm>
00023 
00024 #define NOTHING_TODO  0
00025 #define FORCE_EXIT    1
00026 #define EXIT_REQUEST  2
00027 #define NEW_FILE      1
00028 #define REMOVE_FILE   2
00029 #define REMOVE_ENTRY  3
00030 #define MAX_LAZY_CHANNEL 100
00031 #define TRACE
00032 #define LOG_TYPE_SCRIPT (-1)
00033 
00034 #define STRLCPY(dst, src) strlcpy((dst), (src), sizeof(dst))
00035 
00036 BOOL debug = FALSE;
00037 BOOL nodelete = FALSE;
00038 
00039 typedef struct {
00040    std::string filename;
00041    INT runno;
00042    double size;
00043 } DIRLOG;
00044 
00045 typedef std::vector<DIRLOG> DIRLOGLIST;
00046 
00047 bool cmp_dirlog1(const DIRLOG&a, const DIRLOG&b)
00048 {
00049    if (a.runno < b.runno)
00050       return true;
00051 
00052    if (a.runno > b.runno)
00053       return false;
00054 
00055    const char* sa = a.filename.c_str();
00056    const char* sb = b.filename.c_str();
00057 
00058    while (1) {
00059       if (*sa == 0) // sa is shorter
00060          return true;
00061 
00062       if (*sb == 0) // sb is shorter
00063          return false;
00064 
00065       //printf("cmp char %c %c\n", *sa, *sb);
00066 
00067       if (*sa < *sb)
00068          return true;
00069 
00070       if (*sa > *sb)
00071          return false;
00072 
00073       // at this point, *sa == *sb
00074 
00075       if (isdigit(*sa)) {
00076          int ia = strtoul(sa, (char**)&sa, 10);
00077          int ib = strtoul(sb, (char**)&sb, 10);
00078 
00079          //printf("cmp int %d %d\n", ia, ib);
00080       
00081          if (ia < ib)
00082             return true;
00083 
00084          if (ia > ib)
00085             return false;
00086 
00087          // at this point, ia == ib
00088          continue;
00089       }
00090 
00091       sa++;
00092       sb++;
00093    }
00094 
00095 }
00096 
00097 bool cmp_dirlog(const DIRLOG&a, const DIRLOG&b)
00098 {
00099    bool r = cmp_dirlog1(a,b);
00100    //printf("compare %s %s yields %d\n", a.filename.c_str(), b.filename.c_str(), r);
00101    return r;
00102 }
00103 
00104 #ifndef MAX_FILE_PATH
00105 #define MAX_FILE_PATH 128 // must match value of 128 in LAZY_SETTINGS_STRING
00106 #endif
00107 
00108 #define LAZY_SETTINGS_STRING "\
00109 Period = INT : 10\n\
00110 Maintain free space (%) = INT : 0\n\
00111 Stay behind = INT : 0\n\
00112 Alarm Class = STRING : [32]\n\
00113 Running condition = STRING : [128] ALWAYS\n\
00114 Data dir = STRING : [256] \n\
00115 Data format = STRING : [8] MIDAS\n\
00116 Filename format = STRING : [128] run%05d.mid\n\
00117 Backup type = STRING : [8] Tape\n\
00118 Execute after rewind = STRING : [64]\n\
00119 Path = STRING : [128] \n\
00120 Capacity (Bytes) = FLOAT : 5e9\n\
00121 List label= STRING : [128] \n\
00122 Execute before writing file = STRING : [64]\n\
00123 Execute after writing file = STRING : [64]\n\
00124 Modulo.Position = STRING : [8]\n\
00125 Tape Data Append = BOOL : y\n\
00126 "
00127 #define LAZY_STATISTICS_STRING "\
00128 Backup file = STRING : [128] none \n\
00129 File size (Bytes) = DOUBLE : 0.0\n\
00130 KBytes copied = DOUBLE : 0.0\n\
00131 Total Bytes copied = DOUBLE : 0.0\n\
00132 Copy progress (%) = DOUBLE : 0\n\
00133 Copy Rate (Bytes per s) = DOUBLE : 0\n\
00134 Backup status (%) = DOUBLE : 0\n\
00135 Number of Files = INT : 0\n\
00136 Current Lazy run = INT : 0\n\
00137 "
00138 
00139 typedef struct {
00140    INT period;                  /* time dela in lazy_main */
00141    INT pupercent;               /* 0:100 % of disk space to keep free */
00142    INT staybehind;              /* keep x run file between current Acq and backup
00143                                    -x same as x but starting from oldest */
00144    char alarm[32];              /* Alarm Class */
00145    char condition[128];         /* key condition */
00146    char dir[256];               /* path to the data dir */
00147    char format[8];              /* Data format (MIDAS) */
00148    char backfmt[MAX_FILE_PATH]; /* format for the run files run%05d.mid */
00149    char type[8];                /* Backup device type  (Disk, Tape, Ftp, Script) */
00150    char command[64];            /* command to run after rewind */
00151    char path[MAX_FILE_PATH];    /* backup device name */
00152    float capacity;              /* backup device max byte size */
00153    char backlabel[MAX_FILE_PATH];       /* label of the array in ~/list. if empty like active = 0 */
00154    char commandBefore[64];      /* command to run Before writing a file */
00155    char commandAfter[64];       /* command to run After writing a file */
00156    char modulo[8];              /* Modulo for multiple lazy client */
00157    BOOL tapeAppend;             /* Flag for appending data to the Tape */
00158 } LAZY_SETTING;
00159 LAZY_SETTING lazy;
00160 
00161 typedef struct {
00162    char backfile[MAX_FILE_PATH];        /* current or last lazy file done (for info only) */
00163    double file_size;            /* file size in bytes */
00164    double cur_size;             /* current bytes copied */
00165    double cur_dev_size;         /* Total bytes backup on device */
00166    double progress;             /* copy % */
00167    double copy_rate;            /* copy rate Kb/s */
00168    double bckfill;              /* backup fill % */
00169    INT nfiles;                  /* # of backuped files */
00170    INT cur_run;                 /* current or last lazy run number done (for info only) */
00171 } LAZY_STATISTICS;
00172 LAZY_STATISTICS lazyst;
00173 
00174 
00175 typedef struct {
00176    HNDLE hKey;
00177    BOOL active;
00178    char name[32];
00179 } LAZY_INFO;
00180 
00181 LAZY_INFO lazyinfo[MAX_LAZY_CHANNEL] = { {0, FALSE, "Tape"} };
00182 
00183 INT channel = -1;
00184 
00185 /* Globals */
00186 INT lazy_semaphore;
00187 HNDLE hDB, hKey, pcurrent_hKey;
00188 double lastsz;
00189 HNDLE hKeyst;
00190 INT run_state, hDev;
00191 BOOL msg_flag;
00192 BOOL copy_continue = TRUE;
00193 INT data_fmt, dev_type;
00194 char lazylog[MAX_STRING_LENGTH];
00195 BOOL full_bck_flag = FALSE, maintain_touched = FALSE;
00196 INT blockn = 0;
00197 
00198 #define WATCHDOG_TIMEOUT 60000  /* 60 sec for tape access */
00199 
00200 /* prototypes */
00201 INT moduloCheck(INT lModulo, INT lPosition, INT lrun);
00202 BOOL lazy_file_exists(char *dir, char *file);
00203 INT lazy_main(INT, LAZY_INFO *);
00204 INT lazy_copy(char *dev, char *file);
00205 INT lazy_load_params(HNDLE hDB, HNDLE hKey);
00206 INT build_log_list(const char *fmt, const char *dir, DIRLOGLIST *plog);
00207 INT build_done_list_odb(HNDLE, INT **);
00208 void lazy_settings_hotlink(HNDLE hDB, HNDLE hKey, void *info);
00209 void lazy_maintain_check(HNDLE hKey, LAZY_INFO * pLall);
00210 
00211 void print_dirlog(const DIRLOGLIST* dirlog)
00212 {
00213    for (unsigned i=0; i<dirlog->size(); i++)
00214       printf("%d: %s, run %d, size %12.0f\n",
00215              i,
00216              (*dirlog)[i].filename.c_str(),
00217              (*dirlog)[i].runno,
00218              (*dirlog)[i].size);
00219 };
00220 
00221 /*------------------------------------------------------------------*/
00222 INT lazy_run_extract(const char *name)
00223 /********************************************************************\
00224 Routine: lazy_run_extract
00225 Purpose: extract the contigious digit at the right side from the 
00226 string to make up a run number.
00227 Input:
00228 char * name    string to search
00229 Output:
00230 Function value:
00231 INT        run number
00232 0 if no extraction
00233 \********************************************************************/
00234 {
00235 #if 0
00236    char run_str[256];
00237    int j;
00238 
00239    strlcpy(run_str, name, sizeof(run_str));
00240    if (strlen(run_str) < 2)
00241       return 0;
00242 
00243    for (j = strlen(run_str) - 1; j >= 0 && !isdigit(run_str[j]); j--)
00244       run_str[j] = 0;
00245 
00246    for (j = strlen(run_str) - 1; j >= 0 && isdigit(run_str[j]); j--);
00247 
00248    return atoi(run_str + j + 1);
00249 #endif
00250 
00251    while (*name && !isdigit(*name))
00252       name++;
00253 
00254    if (*name == 0)
00255       return 0;
00256 
00257    return atoi(name);
00258 }
00259 
00260 /*------------------------------------------------------------------*/
00261 INT lazy_file_remove(const char *pufile)
00262 {
00263    INT fHandle, status;
00264 
00265    if (nodelete) {
00266       printf("lazy_file_remove: running in nodelete mode (-n switch), will not remove \'%s\'\n", pufile);
00267       return !SS_SUCCESS;
00268    }
00269 
00270    /* open device */
00271    fHandle = open(pufile, O_RDONLY, 0644);
00272    if (fHandle == -1)
00273       return SS_INVALID_NAME;
00274 
00275    close(fHandle);
00276 
00277    status = ss_file_remove((char*)pufile);
00278    if (status != 0)
00279       return SS_FILE_ERROR;
00280    return SS_SUCCESS;
00281 }
00282 
00283 /*------------------------------------------------------------------*/
00284 INT lazy_log_update(INT action, INT run, const char *label, const char *file, DWORD perf_time)
00285 {
00286    char str[MAX_FILE_PATH];
00287    INT blocks;
00288 
00289    strcpy(str, "no action");
00290 
00291    /* log Lazy logger to midas.log only */
00292    if (action == NEW_FILE) {
00293       /* keep track of number of file on that channel */
00294       lazyst.nfiles++;
00295 
00296       if (equal_ustring(lazy.type, "FTP"))
00297          sprintf(str, "%s: (cp:%.1fs) %s %1.3lfMB file COPIED",
00298                  label, (double) perf_time / 1000., lazyst.backfile, lazyst.file_size / 1024.0 / 1024.0);
00299       else if (equal_ustring(lazy.type, "Script")) {
00300          sprintf(str, "%s[%i] (cp:%.1fs) %s %1.3lfMB  file NEW",
00301                  label, lazyst.nfiles, (double) perf_time / 1000., lazyst.backfile, lazyst.file_size / 1024.0 / 1024.0);
00302       } else if (equal_ustring(lazy.type, "Disk")) {
00303          if (lazy.path[0] != 0)
00304             if (lazy.path[strlen(lazy.path) - 1] != DIR_SEPARATOR)
00305                strcat(lazy.path, DIR_SEPARATOR_STR);
00306          sprintf(str, "%s[%i] (cp:%.1fs) %s%s %1.3lfMB  file NEW",
00307                  label, lazyst.nfiles, (double) perf_time / 1000.,
00308                  lazy.path, lazyst.backfile, lazyst.file_size / 1024.0 / 1024.0);
00309       } else if (equal_ustring(lazy.type, "Tape")) {
00310          blocks = (int) (lazyst.cur_dev_size / 32.0 / 1024.0) + lazyst.nfiles;
00311          /* June 2002, use variable blockn from the real tape position */
00312          sprintf(str, "%s[%i] (cp:%.1fs) %s/%s %1.3lfMB  file NEW (position at block %d)",
00313                  label, lazyst.nfiles, (double) perf_time / 1000.,
00314                  lazy.path, lazyst.backfile, lazyst.file_size / 1024.0 / 1024.0, blockn);
00315          if (lazy.commandAfter[0]) {
00316             char cmd[256];
00317             sprintf(cmd, "%s %s %i %s/%s %1.3lf %d", lazy.commandAfter,
00318                     lazy.backlabel, lazyst.nfiles, lazy.path, lazyst.backfile,
00319                     lazyst.file_size / 1024.0 / 1024.0, blockn);
00320             cm_msg(MINFO, "Lazy", "Exec post file write script:%s", cmd);
00321             ss_system(cmd);
00322          }
00323       }
00324    } else if (action == REMOVE_FILE)
00325       sprintf(str, "%i (rm:%dms) %s file REMOVED", run, perf_time, file);
00326 
00327    else if (action == REMOVE_ENTRY)
00328       sprintf(str, "%s run#%i entry REMOVED", label, run);
00329 
00330 #ifdef WRITE_MIDAS_LOG
00331    cm_msg(MINFO, "lazy_log_update", str);
00332 #endif
00333    /* Now add this info also to a special log file */
00334    cm_msg1(MINFO, "lazy", "lazy_log_update", str);
00335 
00336    return 0;
00337 }
00338 
00339 /*------------------------------------------------------------------*/
00340 INT moduloCheck(INT lModulo, INT lPosition, INT lrun)
00341 /********************************************************************\
00342 Routine: moduleCheck
00343 Purpose: return valid run number in case of Modulo function enabled
00344 or zero if not.
00345 Input:
00346 lModulo   : number of lazy channel
00347 lPosition : Position of the current channel
00348 lrun      : current run to test
00349 Function value:
00350 valid run number (0=skip run)
00351 \********************************************************************/
00352 {
00353    if (lModulo) {
00354       if ((lrun % lModulo) == lPosition)
00355          return lrun;
00356       else
00357          return 0;
00358    } else
00359       return lrun;
00360 }
00361 
00362 /*------------------------------------------------------------------*/
00363 INT build_log_list(const char *fmt, const char *xdir, DIRLOGLIST *dlist)
00364 /********************************************************************\
00365 Routine: build_log_list
00366 Purpose: build an internal directory file list from the disk directory
00367 Input:
00368 * fmt     format of the file to search for (ie:run%05d.ybs)
00369 * dir     path to the directory for the search
00370 Output:
00371 **plog     internal file list struct
00372 Function value:
00373 number of elements
00374 \********************************************************************/
00375 {
00376    char str[MAX_FILE_PATH];
00377    char dir[MAX_FILE_PATH];
00378    char *dot;
00379    int lModulo = 0, lPosition = 0;
00380 
00381    strlcpy(dir, xdir, sizeof(dir));
00382 
00383    while (fmt) {
00384       /* substitue %xx by * */
00385       STRLCPY(str, fmt);
00386 
00387       fmt = strchr(fmt, ',');
00388       if (fmt)
00389          fmt++;
00390 
00391       char*s = strchr(str, ',');
00392       if (s)
00393          *s = 0;
00394 
00395       if (strchr(str, '%')) {
00396          *strchr(str, '%') = '*';
00397          if (strchr(str, '.'))
00398             strcpy((strchr(str, '*') + 1), strchr(str, '.'));
00399       }
00400 
00401       char *list = NULL;
00402 
00403       /* create dir listing with given criteria */
00404       int nfile = ss_file_find(dir, str, &list);
00405 
00406       /* check */
00407       /*
00408         for (j=0;j<nfile;j++)
00409         printf ("list[%i]:%s\n",j, list+j*MAX_STRING_LENGTH);
00410       */
00411 
00412       std::vector<std::string> flist;
00413       for (int j=0;j<nfile;j++)
00414          flist.push_back(list+j*MAX_STRING_LENGTH);
00415 
00416       free(list);
00417 
00418       /* Check Modulo option */
00419       if (lazy.modulo[0]) {
00420          /* Modulo enabled, extract modulo and position */
00421          dot = strchr(lazy.modulo, '.');
00422          if (dot) {
00423             *dot = '\0';
00424             lModulo = atoi(lazy.modulo);
00425             lPosition = atoi(dot + 1);
00426             *dot = '.';
00427          }
00428       }
00429 
00430       /* fill structure */
00431       for (unsigned j = 0, l = 0; j < flist.size(); j++) {
00432          INT lrun;
00433          /* extract run number */
00434          lrun = lazy_run_extract((char*)flist[j].c_str());
00435          /* apply the modulo if enabled */
00436          lrun = moduloCheck(lModulo, lPosition, lrun);
00437          /* if modulo enable skip */
00438          if (lrun == 0)
00439             continue;
00440 
00441          std::string s = dir;
00442          s += flist[j];
00443 
00444          DIRLOG d;
00445          d.filename = flist[j];
00446          d.runno = lrun;
00447          d.size = ss_file_size((char*)s.c_str());
00448 
00449          dlist->push_back(d);
00450 
00451          l++;
00452       }
00453    }
00454 
00455    sort(dlist->begin(), dlist->end(), cmp_dirlog);
00456 
00457    return dlist->size();
00458 }
00459 
00460 /*------------------------------------------------------------------*/
00461 INT build_done_list_odb(HNDLE hLch, INT ** pdo)
00462 /********************************************************************\
00463 Routine: build_done_list
00464 Purpose: build a sorted internal /lazy/list list (pdo) tree.
00465 Input:
00466 HNDLE             Key of the Lazy channel
00467 **pdo     /lazy_xxx/list run listing
00468 Output:
00469 **pdo     /lazy_xxx/list run listing
00470 Function value:      number of elements
00471 \********************************************************************/
00472 {
00473    HNDLE hKey, hSubkey;
00474    KEY key;
00475    INT i, j, size, tot_nelement, nelement, temp;
00476 
00477    if (db_find_key(hDB, hLch, "List", &hKey) != DB_SUCCESS) {
00478       return 0;
00479    }
00480 
00481    tot_nelement = 0;
00482    for (i = 0;; i++) {
00483       db_enum_key(hDB, hKey, i, &hSubkey);
00484       if (!hSubkey)
00485          break;
00486       db_get_key(hDB, hSubkey, &key);
00487       nelement = key.num_values;
00488       *pdo = (INT*)realloc(*pdo, sizeof(INT)*(tot_nelement + nelement));
00489       size = nelement * sizeof(INT);
00490       db_get_data(hDB, hSubkey, (char *) (*pdo + tot_nelement), &size, TID_INT);
00491       tot_nelement += nelement;
00492    }
00493 
00494    if (0) {
00495       printf("read pdo: %d\n", tot_nelement);
00496       for (i=0; i<tot_nelement; i++)
00497          printf("%d: %d\n", i, (*pdo)[i]);
00498    }
00499 
00500    /* expand compressed run numbers */
00501    for (i=0; i<tot_nelement; i++)
00502       if ((*pdo)[i] < 0) {
00503          int first =  (*pdo)[i-1];
00504          int last = -(*pdo)[i];
00505          int nruns = last - first + 1;
00506          assert(nruns > 1);
00507 
00508          *pdo = (INT*)realloc(*pdo, sizeof(INT)*(tot_nelement + nruns - 2));
00509          assert(*pdo != NULL);
00510 
00511          memmove((*pdo) + i + nruns -1, (*pdo) + i + 1, sizeof(INT)*(tot_nelement - i - 1));
00512 
00513          for (j=1; j<nruns; j++)
00514             (*pdo)[i+j-1] = first + j;
00515 
00516          tot_nelement += nruns - 2;
00517       }
00518 
00519    if (0) {
00520       printf("uncompressed pdo: %d\n", tot_nelement);
00521       for (i=0; i<tot_nelement; i++)
00522          printf("%d: %d\n", i, (*pdo)[i]);
00523    }
00524 
00525    /* sort array of integers */
00526    for (j = 0; j < tot_nelement - 1; j++) {
00527       for (i = j + 1; i < tot_nelement; i++) {
00528          if (*(*pdo + j) > *(*pdo + i)) {
00529             memcpy(&temp, (*pdo + i), sizeof(INT));
00530             memcpy((*pdo + i), (*pdo + j), sizeof(INT));
00531             memcpy((*pdo + j), &temp, sizeof(INT));
00532          }
00533       }
00534    }
00535 
00536    if (0) {
00537       printf("sorted pdo: %d\n", tot_nelement);
00538       for (i=0; i<tot_nelement; i++)
00539          printf(" %d\n", (*pdo)[i]);
00540    }
00541 
00542    return tot_nelement;
00543 }
00544 
00545 std::string list_filename(const char* lazyname, const char* listname)
00546 {
00547    char path[256];
00548    cm_get_path(path);
00549 
00550    std::string s;
00551    s += path;
00552    s += ".Lazy_";
00553    s += lazyname;
00554    s += ".";
00555    s += listname;
00556    return s;
00557 }
00558 
00559 void load_done_list(const char* lazyname, const DIRLOGLIST* dirlist, DIRLOGLIST* dlist)
00560 {
00561    FILE *fp = fopen(list_filename(lazyname, "donelist").c_str(), "r");
00562    if (!fp)
00563       return;
00564 
00565    while (1) {
00566       char str[256];
00567       char* s = fgets(str, sizeof(str), fp);
00568       if (!s)
00569          break;
00570 
00571       DIRLOG d;
00572 
00573       char* p = strchr(s, ' ');
00574 
00575       if (p) {
00576          *p = 0;
00577          p++;
00578       }
00579       
00580       d.filename = s;
00581       d.runno = strtoul(p, &p, 0);
00582       d.size = strtod(p, &p);
00583 
00584       bool found = false;
00585       if (dirlist) {
00586          for (unsigned i=0; i<dirlist->size(); i++)
00587             if ((*dirlist)[i].filename == d.filename) {
00588                found = true;
00589                break;
00590             }
00591       }
00592       else
00593          found = true;
00594 
00595       if (found)
00596          dlist->push_back(d);
00597    }
00598 
00599    fclose(fp);
00600 
00601    sort(dlist->begin(), dlist->end(), cmp_dirlog);
00602 }
00603 
00604 int save_list(const char* lazyname, const char* listname, const DIRLOGLIST* dlist)
00605 {
00606    std::string fname = list_filename(lazyname, listname);
00607    std::string tmpname = fname + ".tmp";
00608 
00609    FILE *fp = fopen(tmpname.c_str(), "w");
00610    if (!fp) {
00611       cm_msg(MERROR, "save_list", "Cannot write to \'%s\', errno %d (%s)",
00612              tmpname.c_str(),
00613              errno,
00614              strerror(errno));
00615       return !SUCCESS;
00616    }
00617 
00618    for (unsigned i=0; i<dlist->size(); i++) {
00619       const char* s = (*dlist)[i].filename.c_str();
00620       const char* p;
00621       while ((p = strchr(s, DIR_SEPARATOR)))
00622          s = p+1;
00623       fprintf(fp, "%s %d %12.0f\n",
00624               s,
00625               (*dlist)[i].runno,
00626               (*dlist)[i].size
00627               );
00628    }
00629 
00630    fclose(fp);
00631 
00632    int status = rename(tmpname.c_str(), fname.c_str());
00633    if (status) {
00634       cm_msg(MERROR, "save_list", "Cannot rename \'%s\' to \'%s\', errno %d (%s)",
00635              tmpname.c_str(),
00636              fname.c_str(),
00637              errno,
00638              strerror(errno));
00639       return !SUCCESS;
00640    }
00641 
00642    return SUCCESS;
00643 }
00644 
00645 /*------------------------------------------------------------------*/
00646 void convert_done_list(HNDLE hLch)
00647 /********************************************************************\
00648 Routine: convert_done_list
00649 Purpose: build a sorted internal /lazy/list list (pdo) tree.
00650 Input:
00651 HNDLE             Key of the Lazy channel
00652 **pdo     /lazy_xxx/list run listing
00653 Output:
00654 **pdo     /lazy_xxx/list run listing
00655 Function value:      number of elements
00656 \********************************************************************/
00657 {
00658    DIRLOGLIST dlist;
00659    build_log_list(lazy.backfmt, lazy.dir, &dlist);
00660 
00661    INT *pdo = NULL;
00662    int n = build_done_list_odb(hLch, &pdo);
00663 
00664    DIRLOGLIST done;
00665 
00666    for (int i=0; i<n; i++)
00667       for (unsigned j=0; j<dlist.size(); j++)
00668          if (dlist[j].runno == pdo[i])
00669             done.push_back(dlist[j]);
00670 
00671    free(pdo);
00672 
00673    KEY key;
00674    int status = db_get_key(hDB, hLch, &key);
00675    assert(status == DB_SUCCESS);
00676 
00677    save_list(key.name, "donelist", &done);
00678 }
00679 
00680 /*------------------------------------------------------------------*/
00681 INT build_done_list(HNDLE hLch, const DIRLOGLIST *pdirlist, DIRLOGLIST *pdone)
00682 /********************************************************************\
00683 Routine: build_done_list
00684 Purpose: build a sorted internal /lazy/list list (pdo) tree.
00685 Input:
00686 HNDLE             Key of the Lazy channel
00687 **pdo     /lazy_xxx/list run listing
00688 Output:
00689 **pdo     /lazy_xxx/list run listing
00690 Function value:      number of elements
00691 \********************************************************************/
00692 {
00693    KEY key;
00694    int status = db_get_key(hDB, hLch, &key);
00695    assert(status == DB_SUCCESS);
00696 
00697    load_done_list(key.name, pdirlist, pdone);
00698 
00699    if (pdone->size() == 0) {
00700       convert_done_list(hLch);
00701       load_done_list(key.name, pdirlist, pdone);
00702    }
00703 
00704    return pdone->size();
00705 }
00706 
00707 /*------------------------------------------------------------------*/
00708 INT save_done_list(HNDLE hLch, DIRLOGLIST *pdone)
00709 /********************************************************************\
00710 Routine: save_done_list
00711 Purpose: save a sorted internal /lazy/list list (pdo) tree.
00712 Input:
00713 HNDLE             Key of the Lazy channel
00714 **pdo     /lazy_xxx/list run listing
00715 Output:
00716 **pdo     /lazy_xxx/list run listing
00717 Function value:      number of elements
00718 \********************************************************************/
00719 {
00720    KEY key;
00721    int status = db_get_key(hDB, hLch, &key);
00722    assert(status == DB_SUCCESS);
00723    sort(pdone->begin(), pdone->end(), cmp_dirlog);
00724    save_list(key.name, "donelist", pdone);
00725    return SUCCESS;
00726 }
00727 
00728 /*------------------------------------------------------------------*/
00729 int find_next_file(const DIRLOGLIST *plog, const DIRLOGLIST *pdone)
00730 /********************************************************************\
00731 Routine: find_next_file
00732 Purpose: find next file to be backed up and return it's index in plog
00733 Input:
00734 *plog :   disk file listing
00735 *pdone :  list of files already backed up
00736 Output:
00737 Function value:
00738 index into plog
00739 -1       : no files
00740 
00741 \********************************************************************/
00742 {
00743    for (unsigned j = 0; j < plog->size(); j++) {
00744       bool found = false;
00745       for (unsigned i = 0; i < pdone->size(); i++) {
00746          if ((*plog)[j].filename == (*pdone)[i].filename)
00747             found = true;
00748       }
00749 
00750       if (!found)
00751          if ((*plog)[j].size > 0)
00752             return j;
00753    }
00754 
00755    return -1;
00756 }
00757 
00758 /*------------------------------------------------------------------*/
00759 INT lazy_select_purge(HNDLE hKey, INT channel, LAZY_INFO * pLall, const char* fmt, const char* dir, DIRLOG *f)
00760                       /********************************************************************\
00761                       Routine: lazy_select_purge
00762                       Purpose: Search oldest run number which can be purged
00763                       condition : oldest run# in (pdo AND present in plog)
00764                       AND scan all the other channels based on the following
00765                       conditions:
00766                       "data dir" && "filename format" are the same &&
00767                       the /list/run_number exists in all the above condition.
00768                       Input:
00769                       hKey      : Current channel key
00770                       channel   : Current channel number
00771                       *pLall    : Pointer to all channels
00772                       fmt       : Format string
00773                       dir       : Directory string
00774                       Output:
00775                       fpufile   : file to purge
00776                       run       : corresponding run# to be purged
00777                       Function value:
00778                       0          success
00779                       -1         run not found
00780                       \********************************************************************/
00781 {
00782    int size;
00783    char ddir[256], ff[128], strmodulo[8];
00784 
00785    /* Scan donelist from first element (oldest)
00786       check if run exists in dirlog
00787       if yes return file and run number */
00788 
00789    while (1) {
00790       /* try to find the oldest matching run present in the list AND on disk */
00791       /* build current done list */
00792 
00793       DIRLOGLIST dirlists[MAX_LAZY_CHANNEL];
00794       DIRLOGLIST donelists[MAX_LAZY_CHANNEL];
00795 
00796       /* build matching dir and file format (ff) */
00797       for (int i = 0; i < MAX_LAZY_CHANNEL; i++) {
00798          /* Check if key present && key different than currrent
00799             and if modulo is off */
00800          if (((pLall + i)->hKey)) {
00801             /* extract dir and ff */
00802             size = sizeof(strmodulo);
00803             db_get_value(hDB, (pLall + i)->hKey, "Settings/Modulo.Position", strmodulo, &size, TID_STRING, TRUE);
00804             if (strmodulo[0]) {
00805                /* Modulo enabled, skip this channel */
00806                if (debug)
00807                   printf("purge: skipping channel \'%s\' which uses modulo \'%s\'\n", lazyinfo[i].name, strmodulo);
00808                continue;
00809             }
00810 
00811             size = sizeof(ddir);
00812             db_get_value(hDB, (pLall + i)->hKey, "Settings/Data dir", ddir, &size, TID_STRING, TRUE);
00813             size = sizeof(ff);
00814             db_get_value(hDB, (pLall + i)->hKey, "Settings/filename format", ff, &size, TID_STRING, TRUE);
00815 
00816             // monkey about with trailing '/' characters
00817 
00818             int len = strlen(ddir);
00819             if (ddir[len-1] != '/') {
00820                ddir[len] = '/';
00821                ddir[len+1] = 0;
00822             }
00823 
00824             /* if same dir && same ff => mark lazy channel */
00825             if (strcmp(ddir, dir) != 0) {
00826                if (debug)
00827                   printf("purge: skipping channel \'%s\' which uses different data dir \'%s\', we use \'%s\'\n", lazyinfo[i].name, ddir, dir);
00828                continue;
00829             }
00830 
00831             if (debug)
00832                printf("Loading file lists for channel \'%s\', data dir \'%s\', format \'%s\'\n", lazyinfo[i].name, ddir, ff);
00833 
00834             /* load file list and done list for matching channels */
00835             build_log_list(ff, ddir, &dirlists[i]);
00836             build_done_list(pLall[i].hKey, &dirlists[i], &donelists[i]);
00837          }
00838       } /* end of loop over channels */
00839 
00840       /* search for files to delete */
00841       for (unsigned k = 0; k < dirlists[channel].size(); k++) {
00842          bool can_delete = true;
00843       
00844          if (debug)
00845             printf("purge: consider file \'%s\'\n", dirlists[channel][k].filename.c_str());
00846 
00847          for (int i = 0; i < MAX_LAZY_CHANNEL; i++)
00848             if (((pLall + i)->hKey)) {
00849                bool in_dir_list = false;
00850                bool in_done_list = false;
00851 
00852                for (unsigned j=0; j<dirlists[i].size(); j++)
00853                   if (dirlists[i][j].filename == dirlists[channel][k].filename) {
00854                      in_dir_list = true;
00855                      break;
00856                   }
00857 
00858                if (!in_dir_list) {
00859                   if (debug)
00860                      printf("channel \'%s\': file is not in dir list, ok to delete\n", lazyinfo[i].name);
00861                   continue;
00862                }
00863 
00864                for (unsigned j=0; j<donelists[i].size(); j++)
00865                   if (donelists[i][j].filename == dirlists[channel][k].filename) {
00866                      in_done_list = true;
00867                      break;
00868                   }
00869 
00870                if (!in_done_list) {
00871                   if (debug)
00872                      printf("channel \'%s\': file is in dirlist but not in done list, cannot delete\n", lazyinfo[i].name);
00873                   can_delete = false;
00874                   break;
00875                }
00876 
00877                if (debug)
00878                   printf("channel \'%s\': file is in dirlist and in done list, ok to delete\n", lazyinfo[i].name);
00879             }
00880 
00881          if (can_delete) {
00882             if (debug)
00883                printf("purge: file \'%s\' is done by all channels\n", dirlists[channel][k].filename.c_str());
00884             *f = dirlists[channel][k];
00885             return SUCCESS;
00886          }
00887       }
00888 
00889       /* nothing to remove */
00890       return !SUCCESS;
00891    }
00892 }
00893 
00894 /*------------------------------------------------------------------*/
00895 void lazy_settings_hotlink(HNDLE hDB, HNDLE hKey, void *info)
00896 {
00897    INT size, maintain;
00898 
00899    /* check if Maintain has been touched */
00900    size = sizeof(maintain);
00901    db_get_value(hDB, hKey, "Maintain free space (%)", &maintain, &size, TID_INT, TRUE);
00902    if (maintain != 0)
00903       maintain_touched = TRUE;
00904    else
00905       maintain_touched = FALSE;
00906 }
00907 
00908 /*------------------------------------------------------------------*/
00909 void lazy_maintain_check(HNDLE hKey, LAZY_INFO * pLall)
00910 /********************************************************************\
00911 Routine: lazy_maintain_check
00912 Purpose: Check if the "maintain free space" of any of the other matching
00913 channels is != 0. matching correspond to the condition:
00914 "data dir" && "filename format"
00915 if found != 0 then set to 0 and inform
00916 Input:
00917 hKey   :   Current lazy channel key
00918 *pLall :   Pointer to all channels
00919 \********************************************************************/
00920 {
00921    INT size;
00922    INT i, maintain;
00923    char dir[256], ff[128];
00924    char cdir[256], cff[128];
00925    char cmodulostr[8], modulostr[8];
00926 
00927    /* do test only if maintain has been touched */
00928    if (!maintain_touched)
00929       return;
00930    maintain_touched = FALSE;
00931 
00932    /* check is Maintain free is enabled */
00933    size = sizeof(maintain);
00934    db_get_value(hDB, hKey, "Settings/Maintain free space (%)", &maintain, &size, TID_INT, TRUE);
00935    if (maintain != 0) {
00936       /* scan other channels */
00937 
00938       /* get current specification */
00939       size = sizeof(cdir);
00940       db_get_value(hDB, hKey, "Settings/Data dir", cdir, &size, TID_STRING, TRUE);
00941       size = sizeof(cff);
00942       db_get_value(hDB, hKey, "Settings/filename format", cff, &size, TID_STRING, TRUE);
00943       size = sizeof(modulostr);
00944       db_get_value(hDB, hKey, "Settings/Modulo.Position", cmodulostr, &size, TID_STRING, TRUE);
00945 
00946       /* build matching dir and ff */
00947       for (i = 0; i < MAX_LAZY_CHANNEL; i++) {
00948          if (((pLall + i)->hKey) && (hKey != (pLall + i)->hKey)) {      /* valid channel */
00949             size = sizeof(dir);
00950             db_get_value(hDB, (pLall + i)->hKey, "Settings/Data dir", dir, &size, TID_STRING, TRUE);
00951             size = sizeof(ff);
00952             db_get_value(hDB, (pLall + i)->hKey, "Settings/filename format", ff, &size, TID_STRING, TRUE);
00953             size = sizeof(modulostr);
00954             db_get_value(hDB, (pLall + i)->hKey, "Settings/Modulo.Position", modulostr, &size, TID_STRING, TRUE);
00955 
00956 
00957             if ((strcmp(dir, cdir) == 0) && (strcmp(ff, cff) == 0) && !cmodulostr[0]
00958                 && !modulostr[0]) {
00959                /* check "maintain free space" */
00960                size = sizeof(maintain);
00961                db_get_value(hDB, (pLall + i)->hKey, "Settings/Maintain free space (%)",
00962                             &maintain, &size, TID_INT, TRUE);
00963                if (maintain) {
00964                   /* disable and inform */
00965                   size = sizeof(maintain);
00966                   maintain = 0;
00967                   db_set_value(hDB, (pLall + i)->hKey, "Settings/Maintain free space (%)", &maintain, size, 1, TID_INT);
00968                   cm_msg(MINFO, "lazy_maintain_check",
00969                          "Maintain free space on channel %s has been disable", (pLall + i)->name);
00970                }
00971             }
00972          }
00973       }
00974    }
00975 }
00976 
00977 /*------------------------------------------------------------------*/
00978 void lazy_statistics_update(INT cploop_time)
00979 /********************************************************************\
00980 Routine: lazy_statistics_update
00981 Purpose: update the /lazy/statistics
00982 Input:
00983 INT cploop_time : time of the last call
00984 Output:
00985 Function value:
00986 0           success
00987 \********************************************************************/
00988 {
00989    /* update rate [kb/s] statistics */
00990    if (cploop_time == 0)        // special function: set copy_rate to zero at end of copy
00991       lazyst.copy_rate = 0.0;
00992    if ((ss_millitime() - cploop_time) > 100)
00993       lazyst.copy_rate = 1000.f * (lazyst.cur_size - lastsz) / (ss_millitime() - cploop_time);
00994    if (lazyst.copy_rate < 0.0)
00995       lazyst.copy_rate = 0.0;
00996 
00997    /* update % statistics */
00998    if (lazyst.file_size != 0.0f)
00999       lazyst.progress = (100.0f * (lazyst.cur_size / lazyst.file_size));
01000    else
01001       lazyst.progress = 0.0f;
01002 
01003    /* update backup % statistics */
01004    if (lazy.capacity > 0.0f)
01005       lazyst.bckfill = 100.0f * (lazyst.cur_dev_size / lazy.capacity);
01006    else
01007       lazyst.bckfill = 0.0f;
01008 
01009    lastsz = lazyst.cur_size;
01010    /* udate ODB statistics */
01011    db_send_changed_records();
01012 }
01013 
01014 /*------------------------------------------------------------------*/
01015 BOOL condition_test(char *string)
01016 /********************************************************************\
01017 Routine: condition_check
01018 Purpose: return TRUE or FALSE depending if condition is satisfied
01019 I expect a statement of the following form:
01020 <key> <operator> <value>
01021 with <key> : either link or key[index] or key(index)
01022 <operator> : either: = < >
01023 <value> : [+/-][digits]
01024 Input: char * string to decode and test
01025 Output:
01026 Function value:
01027 TRUE           condition TRUE  (carry on the copy)
01028 FALSE          condition FALSE (hold the copy)
01029 \********************************************************************/
01030 {
01031    KEY key;
01032    double value;
01033    double lcond_value;
01034    INT size, index, status;
01035    char str[128], left[64], right[64];
01036    char *p = NULL, *pp, *ppl, *pc, *lp;
01037 
01038    index = 0;
01039    p = string;
01040    if (p) {
01041       while (isspace(*p))
01042          p++;
01043 
01044       pc = strpbrk(p, "<=>");
01045       if (pc) {
01046          strncpy(left, p, pc - p);
01047          lp = left + (pc - p - 1);
01048          while (isspace(*lp))
01049             lp--;
01050          *(lp + 1) = '\0';
01051          strncpy(right, pc + 1, (strlen(p) - strlen(left)));
01052          right[strlen(p) - strlen(left) - 1] = '\0';
01053       }
01054 
01055       if ((pp = strpbrk(left, "[(")) != NULL) {
01056          if ((ppl = strpbrk(left, "])")) != NULL) {
01057             *pp = '\0';
01058             *ppl = '\0';
01059             index = atoi(pp + 1);
01060          }
01061          *pp = '\0';
01062       }
01063 
01064       /* convert value */
01065       value = (double) (atoi(right));
01066 
01067       status = db_find_key(hDB, 0, left, &hKey);
01068       if (status != DB_SUCCESS) {
01069          cm_msg(MINFO, "condition_check", "Key %s not found", left);
01070          return FALSE;
01071       }
01072       status = db_get_key(hDB, hKey, &key);
01073       if ((status == DB_SUCCESS) && (key.type <= TID_DOUBLE)) {
01074          /* get value of the condition */
01075          size = sizeof(lcond_value);
01076          db_get_data_index(hDB, hKey, &lcond_value, &size, index, key.type);
01077          db_sprintf(str, &lcond_value, key.item_size, 0, key.type);
01078          lcond_value = atof(str);
01079       }
01080 
01081       /*  printf("string:%s\n condition: %s %f %c %f \n"
01082          , string, left, lcond_value, *pc, value);
01083        */
01084       /*
01085          if (pv == NULL)
01086          return TRUE;
01087        */
01088       /* perform condition check */
01089       if (((*pc == '>') && ((double) lcond_value > value)) ||
01090           ((*pc == '=') && ((double) lcond_value == value)) || ((*pc == '<') && ((double) lcond_value < value)))
01091          return TRUE;
01092       else
01093          return FALSE;
01094    }
01095    /* catch wrong argument in the condition as TRUE */
01096    return TRUE;
01097 }
01098 
01099 /*------------------------------------------------------------------*/
01100 BOOL lazy_condition_check(void)
01101 /********************************************************************\
01102 Routine: lazy_condition_check
01103 Purpose: Check if the copy should continue.
01104 The condition can have the following setting:
01105 ALWAYS, NEVER, WHILE_NO_ACQ_RUNNING, key<=>value
01106 Input:
01107 Output:
01108 Function value:
01109 BOOL :  new copy condition
01110 \********************************************************************/
01111 {
01112    /* Get condition */
01113    if (equal_ustring(lazy.condition, "ALWAYS"))
01114       return TRUE;
01115    else if (equal_ustring(lazy.condition, "NEVER"))
01116       return FALSE;
01117    else if (equal_ustring(lazy.condition, "WHILE_ACQ_NOT_RUNNING")) {
01118       if (run_state == STATE_RUNNING)
01119          return FALSE;
01120       else
01121          return TRUE;
01122    } else
01123       return (condition_test(lazy.condition));
01124 }
01125 
01126 /*------------------------------------------------------------------*/
01127 INT lazy_copy(char *outfile, char *infile)
01128 /********************************************************************\
01129 Routine: lazy_copy
01130 Purpose: backup file to backup device
01131 every 2 second will update the statistics and yield
01132 if condition requires no copy, every 5 second will yield
01133 Input:
01134 char * outfile   backup destination file
01135 char * infile    source file to be backed up
01136 Output:
01137 Function value:
01138 0           success
01139 \********************************************************************/
01140 {
01141    void *plazy = NULL;
01142    DWORD szlazy;
01143    INT status, no_cpy_last_time = 0;
01144    INT last_time, cpy_loop_time;
01145    DWORD watchdog_timeout;
01146    static INT last_error = 0;
01147    //char *pext;
01148    BOOL watchdog_flag, exit_request = FALSE;
01149    char filename[256];
01150 
01151    /* init copy variables */
01152    lazyst.cur_size = 0.0f;
01153    last_time = 0;
01154 
01155    /* open any logging file (output) */
01156    strlcpy(filename, outfile, sizeof(filename));        // ftp modifies filename
01157    if ((status = md_file_wopen(dev_type, data_fmt, filename, &hDev)) != 1) {
01158       if ((ss_time() - last_error) > 60) {
01159          last_error = ss_time();
01160          cm_msg(MTALK, "Lazy_copy", "cannot open %s, error %d", outfile, status);
01161       }
01162       return (FORCE_EXIT);
01163    }
01164 
01165    /* New lazy copy if TAPE & append required force a mv to EOD */
01166    if ((dev_type == LOG_TYPE_TAPE) && lazy.tapeAppend) {
01167       /* Position Tape to end of Data */
01168       cm_msg(MINFO, "Lazy", "Positioning Tape to EOD");
01169 
01170       cm_get_watchdog_params(&watchdog_flag, &watchdog_timeout);
01171       cm_set_watchdog_params(watchdog_flag, 300000);    /* 5 min for tape rewind */
01172       status = ss_tape_spool(hDev);
01173       cm_set_watchdog_params(watchdog_flag, watchdog_timeout);
01174       if (status != SS_SUCCESS) {
01175          cm_msg(MINFO, "Lazy", "Error while Positioning Tape to EOD (%d)", status);
01176          ss_tape_close(hDev);
01177          return (FORCE_EXIT);
01178       }
01179    }
01180 
01181    /* reset error message */
01182    last_error = 0;
01183 
01184    /* open input data file */
01185    if (md_file_ropen(infile, data_fmt) != MD_SUCCESS)
01186       return (FORCE_EXIT);
01187 
01188    /* run shell command if available */
01189    if (equal_ustring(lazy.type, "Tape")) {
01190       /* get the block number. If -1 it probably means that the tape
01191          is not ready. Wait for a while  */
01192       blockn = -1;
01193       while (blockn < 0) {
01194          blockn = ss_tape_get_blockn(hDev);
01195          if (blockn >= 0)
01196             break;
01197          cm_msg(MINFO, "Lazy", "Tape is not ready");
01198          cm_yield(3000);
01199       }
01200       if (lazy.commandBefore[0]) {
01201          char cmd[256];
01202          sprintf(cmd, "%s %s %s %d %s %i", lazy.commandBefore, infile, outfile, blockn, lazy.backlabel, lazyst.nfiles);
01203          cm_msg(MINFO, "Lazy", "Exec pre file write script:%s", cmd);
01204          ss_system(cmd);
01205       }
01206    }
01207 
01208    /* force a statistics update on the first loop */
01209    cpy_loop_time = -2000;
01210    if (dev_type == LOG_TYPE_TAPE) {
01211       char str[MAX_FILE_PATH];
01212       sprintf(str, "Starting lazy job on %s at block %d", lazyst.backfile, blockn);
01213       if (msg_flag)
01214          cm_msg(MTALK, "Lazy", str);
01215       cm_msg(MINFO, "Lazy", str);
01216       cm_msg1(MINFO, "lazy_log_update", "lazy", str);
01217    }
01218 
01219    /* infinite loop while copying */
01220    while (1) {
01221       if (copy_continue) {
01222          if (md_physrec_get(data_fmt, &plazy, &szlazy) == MD_SUCCESS) {
01223             status = md_log_write(hDev, data_fmt, dev_type, plazy, szlazy);
01224             if (status != SS_SUCCESS) {
01225                /* close source file */
01226                md_file_rclose(dev_type);
01227                /* close output data file */
01228                md_file_wclose(hDev, dev_type, data_fmt, outfile);
01229                /* szlazy is the requested block size. Why is it copied to cm_msg?
01230                   cm_msg(MERROR,"lazy_copy","Write error %i",szlazy); */
01231                cm_msg(MERROR, "lazy_copy", "Write error ");
01232                if (status == SS_NO_SPACE)
01233                   return status;
01234                return (FORCE_EXIT);
01235             }
01236             lazyst.cur_size += (double) szlazy;
01237             lazyst.cur_dev_size += (double) szlazy;
01238             if ((ss_millitime() - cpy_loop_time) > 2000) {
01239                /* update statistics */
01240                lazy_statistics_update(cpy_loop_time);
01241 
01242                /* check conditions */
01243                copy_continue = lazy_condition_check();
01244 
01245                /* update check loop */
01246                cpy_loop_time = ss_millitime();
01247 
01248                /* yield quickly */
01249                status = cm_yield(1);
01250                if (status == RPC_SHUTDOWN || status == SS_ABORT || exit_request) {
01251                   cm_msg(MINFO, "Lazy", "Abort postponed until end of copy of %s %1.0lf[%%]",
01252                          infile, (double) lazyst.progress);
01253                   exit_request = TRUE;
01254                }
01255             }
01256          } /* get physrec */
01257          else
01258             break;
01259       } /* copy_continue */
01260       else {                    /* !copy_continue */
01261          status = cm_yield(1000);
01262          if (status == RPC_SHUTDOWN || status == SS_ABORT)
01263             return (FORCE_EXIT);
01264          if ((ss_millitime() - no_cpy_last_time) > 5000) {
01265             copy_continue = lazy_condition_check();
01266             no_cpy_last_time = ss_millitime();
01267          }
01268       }                         /* !copy_continue */
01269    }                            /* while forever */
01270 
01271    /* update for last the statistics */
01272    lazy_statistics_update(0);
01273 
01274    /* close input log device */
01275    md_file_rclose(dev_type);
01276 
01277    /* close output data file */
01278    if (equal_ustring(lazy.type, "Tape")) {
01279       blockn = ss_tape_get_blockn(hDev);
01280    }
01281    status = md_file_wclose(hDev, dev_type, data_fmt, outfile);
01282    if (status != SS_SUCCESS) {
01283       if (status == SS_NO_SPACE)
01284          return status;
01285       return (FORCE_EXIT);
01286    }
01287 
01288    /* request exit */
01289    if (exit_request)
01290       return (EXIT_REQUEST);
01291    return 0;
01292 
01293 }
01294 
01295 /*------------------------------------------------------------------*/
01296 
01297 #ifdef OS_LINUX                 // does not work under Windows because of missing popen
01298 
01299 INT lazy_script_copy(char *infile)
01300 /********************************************************************\
01301 Routine: lazy_script_copy
01302 Purpose: call user script to backup file to backup device
01303 Input:
01304 char * infile    source file to be backed up
01305 Output:
01306 Function value:
01307 0           success
01308 \********************************************************************/
01309 {
01310    int status;
01311    FILE *fin;
01312    int cpy_loop_time = ss_millitime();
01313    char cmd[256];
01314 
01315    /* init copy variables */
01316    lazyst.cur_size = 0;
01317 
01318    /* run shell command if available */
01319    if (lazy.commandBefore[0]) {
01320       char cmd[256];
01321       sprintf(cmd, "%s %s %i", lazy.commandBefore, infile, lazyst.nfiles);
01322       cm_msg(MINFO, "Lazy", "Exec pre file write script:%s", cmd);
01323       ss_system(cmd);
01324    }
01325 
01326    /* create the command for the backup script */
01327    sprintf(cmd, "%s %s 2>&1", lazy.path, infile);
01328 
01329    {
01330       char str[MAX_FILE_PATH];
01331       sprintf(str, "Starting lazy job \'%s\'", cmd);
01332       if (msg_flag)
01333          cm_msg(MTALK, "Lazy", str);
01334       cm_msg(MINFO, "Lazy", str);
01335       cm_msg1(MINFO, "lazy_log_update", "lazy", str);
01336    }
01337 
01338    /* start the backup script */
01339    fin = popen(cmd, "r");
01340 
01341    if (fin == NULL) {
01342       cm_msg(MTALK, "Lazy_copy", "cannot start %s, errno %d (%s)", cmd, errno, strerror(errno));
01343       return FORCE_EXIT;
01344    }
01345 
01346    if (1) {
01347       int desc = fileno(fin);
01348       int flags = fcntl(desc, F_GETFL, 0);
01349       fcntl(desc, F_SETFL, flags | O_NONBLOCK);
01350    }
01351 
01352    /* infinite loop while copying */
01353    while (1) {
01354       char buf[1024];
01355       int rd = read(fileno(fin), buf, sizeof(buf));
01356       if (rd == 0) {
01357          /* file closed - backup script completed */
01358          break;
01359       } else if (rd < 0) {
01360          /* i/o error */
01361 
01362          if (errno == EAGAIN) {
01363             /* this is the main loop while waiting for the backup script */
01364             status = cm_yield(1000);
01365             continue;
01366          }
01367 
01368          cm_msg(MERROR, "lazy_copy", "Error reading output of the backup script, errno %d (%s)", errno,
01369                 strerror(errno));
01370          break;
01371       } else {
01372          char *p;
01373          /* backup script printed something, write it to log file */
01374 
01375          /* make sure text is NUL-terminated */
01376          buf[rd] = 0;
01377 
01378          /* chop the output of the backup script into lines separated by '\n' */
01379          p = buf;
01380          while (p) {
01381             char *q = strchr(p, '\n');
01382             if (q) {
01383                *q = 0;          // replace '\n' with NUL
01384                q++;
01385                if (q[0] == 0)   // check for end of line
01386                   q = NULL;
01387             }
01388             cm_msg(MINFO, "lazy_copy", p);
01389             p = q;
01390          }
01391       }
01392    }
01393 
01394    /* update for last the statistics */
01395    lazy_statistics_update(cpy_loop_time);
01396 
01397    /* close input log device */
01398    status = pclose(fin);
01399 
01400    if (status != 0) {
01401       cm_msg(MERROR, "lazy_copy", "Backup script finished with exit code %d, status 0x%x", (status >> 8), status);
01402       return SS_NO_SPACE;
01403    }
01404 
01405    cm_msg(MINFO, "lazy_copy", "Backup script finished in %.1f sec with exit code %d",
01406           (ss_millitime() - cpy_loop_time) / 1000.0, (status >> 8));
01407 
01408    /* request exit */
01409    return 0;
01410 }
01411 
01412 #endif                          // OS_LINUX
01413 
01414 /*------------------------------------------------------------------*/
01415 BOOL lazy_file_exists(char *dir, char *file)
01416 /********************************************************************\
01417 Routine: lazy_file_exists
01418 Purpose: check if file exists in dir by extracting its size
01419 Input:
01420 char * dir       data directory
01421 char * file      file to be checked
01422 Output:
01423 Function value:
01424 TRUE           file found
01425 FALSE          file not found
01426 \********************************************************************/
01427 {
01428    char *list;
01429    char fullfile[MAX_FILE_PATH] = { '\0' };
01430 
01431    if (ss_file_find(dir, file, &list) == 1) {
01432       strcat(fullfile, dir);
01433       strcat(fullfile, DIR_SEPARATOR_STR);
01434       strcat(fullfile, file);
01435       if ((lazyst.file_size = (double) (ss_file_size(fullfile))) > 0) {
01436          free(list);
01437          return TRUE;
01438       }
01439    }
01440    free(list);
01441    return FALSE;
01442 }
01443 
01444 /*------------------------------------------------------------------*/
01445 
01446 INT lazy_maintain_free_space(LAZY_INFO *pLch, LAZY_INFO *pLall)
01447 {
01448    int status;
01449 
01450    /* update "maintain free space" */
01451    lazy_maintain_check(pLch->hKey, pLall);
01452 
01453    /* check SPACE and purge if necessary = % (1:99) */
01454    if (lazy.pupercent > 0) {
01455 
01456       // Compute initial disk space
01457       double freepercent = 100. * ss_disk_free(lazy.dir) / ss_disk_size(lazy.dir);
01458 
01459       // Check for Disk full first
01460       if (freepercent < 5.0) {
01461          char buf[256];
01462          sprintf(buf, "Disk %s is almost full, free space: %.1f%%", lazy.dir, freepercent);
01463          al_trigger_alarm("Disk Full", buf, lazy.alarm, "Disk buffer full", AT_INTERNAL);
01464       } else {
01465          HNDLE hKey;
01466          if (db_find_key(hDB, 0, "/Alarms/Alarms/Disk Full", &hKey) == DB_SUCCESS)
01467             al_reset_alarm("Disk Full");
01468       }
01469 
01470       if (debug)
01471          printf("free space on %s: %.1f%%, lazy limit %d%%\n", lazy.dir, freepercent, lazy.pupercent);
01472 
01473       /* Lock other clients out of this following code as
01474          it may access the other client info tree */
01475 
01476       status = ss_semaphore_wait_for(lazy_semaphore, 5000);
01477       if (status != SS_SUCCESS) {
01478          /* exit for now and come back later */
01479          return NOTHING_TODO;
01480       }
01481 
01482       /* check purging action */
01483       while (freepercent <= (double) lazy.pupercent) {
01484          // flag for single purge
01485          BOOL donepurge = FALSE;
01486          /* search file to purge : run in donelist AND run in dirlog */
01487          /* synchronize donelist to dir log in case user has purged
01488             some file by hand. Basically remove the run in the list if the
01489             file is not anymore in the source dir. */
01490          DIRLOG f;
01491          if (lazy_select_purge(pLch->hKey, channel, pLall, lazy.backfmt, lazy.dir, &f) == SUCCESS) {
01492             /* check if beyond keep file + 1 */
01493             if (1 /*purun < (cur_acq_run - abs(lazy.staybehind) - 1)*/) {
01494                DWORD rm_time;
01495                /* remove file */
01496                rm_time = ss_millitime();
01497                std::string path = lazy.dir;
01498                path += f.filename;
01499                if (debug)
01500                   printf("file selected for removal %s [%s]\n", f.filename.c_str(), path.c_str());
01501                status = lazy_file_remove(path.c_str());
01502                rm_time = ss_millitime() - rm_time;
01503                if (status != SS_SUCCESS) {
01504                   cm_msg(MERROR, "Lazy", "lazy_file_remove failed on file %s", path.c_str());
01505                   // break out if can't delete files
01506                   break;
01507                } else {
01508                   status = lazy_log_update(REMOVE_FILE, f.runno, NULL, path.c_str(), rm_time);
01509                   donepurge = TRUE;
01510                }
01511             }                   // purun found
01512 
01513             // Re-compute free space
01514             freepercent = 100. * ss_disk_free(lazy.dir) / ss_disk_size(lazy.dir);
01515 
01516             if (debug)
01517                printf("free space on %s: %.1f%%, lazy limit %d%%\n", lazy.dir, freepercent, lazy.pupercent);
01518 
01519          }                      // select_purge
01520 
01521          // donepurge = FALSE => nothing else to do 
01522          // donepurge = TRUE  => one purge done loop back once more
01523          if (!donepurge)
01524             break;
01525       }                         // while
01526 
01527       /* Release the semaphore  */
01528       status = ss_semaphore_release(lazy_semaphore);
01529 
01530    } /* end of if pupercent > 0 */
01531 
01532    return SUCCESS;
01533 }
01534 
01535 
01536 /*------------------------------------------------------------------*/
01537 INT lazy_main(INT channel, LAZY_INFO * pLall)
01538 /********************************************************************\
01539 Routine: lazy_main
01540 Purpose: check if backup is necessary...
01541 Input:
01542 channel: Current channel number
01543 *pLall :   Pointer to all channels
01544 Output:
01545 Function value:
01546 \********************************************************************/
01547 {
01548    DWORD cp_time;
01549    INT size, status;
01550    char str[MAX_FILE_PATH], inffile[MAX_FILE_PATH], outffile[MAX_FILE_PATH];
01551    BOOL watchdog_flag, exit_request = FALSE;
01552    DWORD watchdog_timeout;
01553    LAZY_INFO *pLch;
01554    static BOOL eot_reached = FALSE;
01555    BOOL haveTape;
01556 
01557    /* current channel */
01558    pLch = &pLall[channel];
01559 
01560    /* extract Data format from the struct */
01561    if (equal_ustring(lazy.format, "MIDAS"))
01562       data_fmt = FORMAT_MIDAS;
01563    else {
01564       cm_msg(MERROR, "Lazy", "Unknown data format %s (MIDAS)", lazy.format);
01565       return DB_NO_ACCESS;
01566    }
01567 
01568    /* extract Device type from the struct */
01569    if (equal_ustring(lazy.type, "DISK"))
01570       dev_type = LOG_TYPE_DISK;
01571    else if (equal_ustring(lazy.type, "TAPE"))
01572       dev_type = LOG_TYPE_TAPE;
01573    else if (equal_ustring(lazy.type, "FTP"))
01574       dev_type = LOG_TYPE_FTP;
01575    else if (equal_ustring(lazy.type, "SCRIPT"))
01576       dev_type = LOG_TYPE_SCRIPT;
01577    else {
01578       cm_msg(MERROR, "Lazy", "Unknown device type %s (Disk, Tape, FTP or SCRIPT)", lazy.type);
01579       return DB_NO_ACCESS;
01580    }
01581 
01582    if (dev_type == LOG_TYPE_SCRIPT)
01583       if (lazy.backlabel[0] == 0 || strcmp(lazy.backlabel, lazyinfo[channel].name) != 0) {
01584          strlcpy(lazy.backlabel, lazyinfo[channel].name, sizeof(lazy.backlabel));
01585          size = sizeof(lazy.backlabel);
01586          db_set_value(hDB, pLch->hKey, "Settings/List label", lazy.backlabel, size, 1, TID_STRING);
01587       }
01588 
01589    /* make sure that we don't operate on the current DAQ file if so set to oldest */
01590    //if (lazy.staybehind == 0) {
01591    //   cm_msg(MERROR, "Lazy", "Stay behind cannot be 0");
01592    //   return NOTHING_TODO;
01593    //}
01594 
01595    /* Check if Tape is OK */
01596    /* ... */
01597 
01598    haveTape = (lazy.backlabel[0] != '\0');
01599 
01600    /* check if space on device (not empty tape label) */
01601    if (lazy.backlabel[0] == '\0') {
01602       full_bck_flag = TRUE;
01603    } else {
01604       if (full_bck_flag) {
01605          full_bck_flag = FALSE;
01606          size = sizeof(lazyst);
01607          memset(&lazyst, 0, size);
01608          if (db_find_key(hDB, pLch->hKey, "Statistics", &hKeyst) == DB_SUCCESS) {
01609             status = db_set_record(hDB, hKeyst, &lazyst, size, 0);
01610             /* New session of lazy, apply append in case of Tape */
01611             /* DISK for debugging */
01612             if ((dev_type == LOG_TYPE_DISK) && lazy.tapeAppend) {
01613                /* Position Tape to end of Data */
01614                cm_msg(MINFO, "Lazy", "Positioning Tape to EOD");
01615             }
01616          } else
01617             cm_msg(MERROR, "lazy_main", "did not find /Lazy/Lazy_%s/Statistics for zapping", pLch->name);
01618          // INT al_reset_alarm(char *alarm_name)
01619          if (dev_type == LOG_TYPE_TAPE)
01620             al_reset_alarm("Tape");
01621       }
01622    }
01623    /* check if data dir is none empty */
01624    if (lazy.dir[0] == '\0') {
01625       cm_msg(MINFO, "Lazy", "Please setup Data dir for input source path!");
01626       return NOTHING_TODO;
01627    }
01628 
01629    if (lazy.dir[0] != 0)
01630       if (lazy.dir[strlen(lazy.dir)-1] != DIR_SEPARATOR)
01631          strcat(lazy.dir, DIR_SEPARATOR_STR);
01632 
01633    /* check if device path is set */
01634    if (lazy.path[0] == '\0') {
01635       cm_msg(MINFO, "Lazy", "Please setup backup device path too!");
01636       return NOTHING_TODO;
01637    }
01638 
01639    DIRLOGLIST dirlist;
01640    build_log_list(lazy.backfmt, lazy.dir, &dirlist);
01641 
01642    save_list(pLch->name, "dirlist", &dirlist);
01643 
01644    DIRLOGLIST donelist;
01645    build_done_list(pLch->hKey, &dirlist, &donelist);
01646 
01647    lazy_maintain_free_space(pLch, pLall);
01648 
01649    /* compare list : run NOT in donelist AND run in dirlog */
01650    int tobe_backup = find_next_file(&dirlist, &donelist);
01651    //const DIRLOG* tobe_backup = cmp_log2donelist(&dirlist, &donelist);
01652 
01653    //if (debug)
01654    //print_dirlog(&dirlist);
01655 
01656    //print_dirlog(&donelist);
01657    //printf("tobe_backup: %p\n", tobe_backup);
01658 
01659    if (tobe_backup < 0)
01660       return NOTHING_TODO;
01661 
01662    if (debug)
01663       printf("selected for backup: %s, run %d\n", dirlist[tobe_backup].filename.c_str(), dirlist[tobe_backup].runno);
01664 
01665    /* Get current run number */
01666    int cur_acq_run;
01667    size = sizeof(cur_acq_run);
01668    status = db_get_value(hDB, 0, "Runinfo/Run number", &cur_acq_run, &size, TID_INT, FALSE);
01669    assert(status == SUCCESS);
01670 
01671    lazyst.cur_run = dirlist[tobe_backup].runno;
01672 
01673    int behind_files = dirlist.size() - tobe_backup;
01674    int behind_runs  = cur_acq_run - dirlist[tobe_backup].runno;
01675 
01676    bool nothing_todo_files = (behind_files <= lazy.staybehind);
01677    bool nothing_todo_runs  = (behind_runs < abs(lazy.staybehind));
01678 
01679    bool nothing_todo = false;
01680 
01681    /* "stay behind by so many runs" mode */
01682    if (lazy.staybehind < 0)
01683       nothing_todo = nothing_todo_runs;
01684 
01685    /* "stay behind by so many files" mode */
01686    if (lazy.staybehind > 0)
01687       nothing_todo = nothing_todo_files;
01688 
01689    /* "no stay behind" mode */
01690    if (lazy.staybehind == 0) {
01691       if (dirlist[tobe_backup].runno != cur_acq_run)
01692          nothing_todo = false;
01693       else if (behind_files > 1)
01694          nothing_todo = false;
01695       else {
01696          nothing_todo = false;
01697 
01698          /* In case it is the current run make sure 
01699             1) no transition is in progress
01700             2) the run start has not been aborted
01701             3) the run has been ended
01702          */
01703 
01704          int flag;
01705          size = sizeof(flag);
01706          status = db_get_value(hDB, 0, "Runinfo/Transition in progress", &flag, &size, TID_INT, FALSE);
01707          assert(status == SUCCESS);
01708          if (flag) {
01709             if (debug)
01710                printf("transition in progress, cannot backup last file\n");
01711             nothing_todo = true;
01712          }
01713          
01714          size = sizeof(flag);
01715          status = db_get_value(hDB, 0, "Runinfo/Start abort", &flag, &size, TID_INT, FALSE);
01716          assert(status == SUCCESS);
01717          if (flag) {
01718             if (debug)
01719                printf("run start aborted, cannot backup last file\n");
01720             nothing_todo = true;
01721          }
01722 
01723          int cur_state_run;
01724          status = db_get_value(hDB, 0, "Runinfo/State", &cur_state_run, &size, TID_INT, FALSE);
01725          assert(status == SUCCESS);
01726          if ((cur_state_run != STATE_STOPPED)) {
01727             if (debug)
01728                printf("run still running, cannot backup last file\n");
01729             nothing_todo = true;
01730          }
01731       }
01732    }
01733 
01734    if (debug)
01735       printf("behind: %d files, %d runs, staybehind: %d, nothing_todo: files: %d, runs: %d, lazylogger: %d\n", behind_files, behind_runs, lazy.staybehind, nothing_todo_files, nothing_todo_runs, nothing_todo);
01736 
01737    if (nothing_todo) {
01738       return NOTHING_TODO;
01739    }
01740 
01741    if (dev_type != LOG_TYPE_SCRIPT)
01742       if (!haveTape)
01743          return NOTHING_TODO;
01744 
01745    strlcpy(lazyst.backfile, dirlist[tobe_backup].filename.c_str(), sizeof(lazyst.backfile));
01746 
01747    std::string xfile = lazy.dir;
01748    xfile += dirlist[tobe_backup].filename;
01749    strlcpy(inffile, xfile.c_str(), sizeof(inffile));
01750 
01751    /* Check again if the backup file is present in the logger dir */
01752    if (lazy_file_exists(lazy.dir, lazyst.backfile)) {
01753       /* compose the destination file name */
01754       if (dev_type == LOG_TYPE_DISK) {
01755          if (lazy.path[0] != 0)
01756             if (lazy.path[strlen(lazy.path) - 1] != DIR_SEPARATOR)
01757                strcat(lazy.path, DIR_SEPARATOR_STR);
01758          strcpy(outffile, lazy.path);
01759          strcat(outffile, lazyst.backfile);
01760       } else if (dev_type == LOG_TYPE_TAPE)
01761          strcpy(outffile, lazy.path);
01762       else if (dev_type == LOG_TYPE_FTP) {
01763          /* Format for FTP
01764             lazy.path=host,port,user,password,directory,filename[,umask]
01765             expect filename in format such as "bck%08d.mid" */
01766 
01767          /*
01768             if (lazy.path[0] != 0)
01769             if (lazy.path[strlen(lazy.path)-1] != DIR_SEPARATOR)
01770             strcat(lazy.path, DIR_SEPARATOR_STR);
01771           */
01772 
01773 
01774          strcpy(str, lazy.path);
01775          /* substitute "%d" for current run number */
01776          if (strchr(str, '%'))
01777             sprintf(outffile, str, lazyst.cur_run);
01778          else
01779             strcpy(outffile, str);
01780 
01781          /* substitute "#d" for current run number millenium */
01782          strlcpy(str, outffile, sizeof(str));
01783          if (strchr(str, '#')) {
01784             *strchr(str, '#') = '%';
01785             sprintf(outffile, str, lazyst.cur_run / 1000);
01786          }
01787       }
01788 
01789       /* check if space on backup device ONLY in the TAPE case */
01790       if (((dev_type == LOG_TYPE_TAPE)
01791            && (lazy.capacity < (lazyst.cur_dev_size + lazyst.file_size)))
01792           || eot_reached) {
01793          char pre_label[32];
01794          /* save the last label for shell script */
01795          strcpy(pre_label, lazy.backlabel);
01796 
01797          /* Reset EOT reached */
01798          eot_reached = FALSE;
01799 
01800          /* not enough space => reset list label */
01801          lazy.backlabel[0] = '\0';
01802          size = sizeof(lazy.backlabel);
01803          db_set_value(hDB, pLch->hKey, "Settings/List label", lazy.backlabel, size, 1, TID_STRING);
01804          full_bck_flag = TRUE;
01805          cm_msg(MINFO, "Lazy", "Not enough space for next copy on backup device!");
01806 
01807          /* rewind device if TAPE type */
01808          if (dev_type == LOG_TYPE_TAPE) {
01809             INT status, channel;
01810             char str[128];
01811 
01812             sprintf(str, "Tape %s is full with %d files", pre_label, lazyst.nfiles);
01813             cm_msg(MINFO, "Lazy", str);
01814 
01815             /* Setup alarm */
01816             lazy.alarm[0] = 0;
01817             size = sizeof(lazy.alarm);
01818             db_get_value(hDB, pLch->hKey, "Settings/Alarm Class", lazy.alarm, &size, TID_STRING, TRUE);
01819 
01820             /* trigger alarm if defined */
01821             if (lazy.alarm[0])
01822                al_trigger_alarm("Tape",
01823                                 "Tape full, Please remove current tape and load new one!",
01824                                 lazy.alarm, "Tape full", AT_INTERNAL);
01825 
01826             /* run shell command if available */
01827             if (lazy.command[0]) {
01828                char cmd[256];
01829                sprintf(cmd, "%s %s %s %s", lazy.command, lazy.path, pLch->name, pre_label);
01830                cm_msg(MINFO, "Lazy", "Exec post-rewind script:%s", cmd);
01831                ss_system(cmd);
01832             }
01833 
01834             cm_msg(MINFO, "Lazy", "backup device rewinding...");
01835             cm_get_watchdog_params(&watchdog_flag, &watchdog_timeout);
01836             cm_set_watchdog_params(watchdog_flag, 300000);      /* 5 min for tape rewind */
01837             status = ss_tape_open(outffile, O_RDONLY, &channel);
01838             if (channel < 0) {
01839                cm_msg(MERROR, "Lazy", "Cannot rewind tape %s - %d - %d", outffile, channel, status);
01840                return NOTHING_TODO;
01841             }
01842             //else
01843             //    cm_msg(MINFO,"Lazy", "After call ss_tape_open used to rewind tape %s - %d - %d", outffile, channel, status);
01844             cm_msg(MINFO, "Lazy", "Calling ss_tape_unmount");
01845             ss_tape_unmount(channel);
01846             ss_tape_close(channel);
01847             cm_set_watchdog_params(watchdog_flag, watchdog_timeout);
01848             return NOTHING_TODO;
01849          }                      // 1
01850       }                         // LOG_TYPE_TAPE 
01851 
01852       /* Finally do the copy */
01853       cp_time = ss_millitime();
01854       status = 0;
01855       if (dev_type == LOG_TYPE_SCRIPT) {
01856 #ifdef OS_LINUX
01857          //sprintf(lazy.backlabel, "%06d", 100 * (lazyst.cur_run / 100));
01858          status = lazy_script_copy(inffile);
01859 #else
01860          assert(!"lazy_script_copy not supported under Windows");
01861 #endif
01862       } else {
01863          status = lazy_copy(outffile, inffile);
01864       }
01865 
01866       if ((status != 0) && (status != EXIT_REQUEST)) {
01867          if (status == SS_NO_SPACE) {
01868             /* Consider this case as EOT reached */
01869             eot_reached = TRUE;
01870             return status;
01871          } else if (status == FORCE_EXIT)
01872             return status;
01873          cm_msg(MERROR, "Lazy", "copy failed -%s-%s-%i", lazy.path, lazyst.backfile, status);
01874          return FORCE_EXIT;
01875       }
01876    } /* file exists */
01877    else {
01878       // If file not present it may be due to a run abort.
01879       // As the file is based on the "tobe_backup" run number
01880       // which is evaluated every lazy check, if a run is missing
01881       // it will be skiped properly.
01882       // No message is needed in this case.
01883       //    cm_msg(MERROR, "Lazy", "lazy_file_exists file %s doesn't exists", lazyst.backfile);
01884       return NOTHING_TODO;
01885    }
01886 
01887    if (status == EXIT_REQUEST)
01888       exit_request = TRUE;
01889 
01890    cp_time = ss_millitime() - cp_time;
01891 
01892    donelist.push_back(dirlist[tobe_backup]);
01893 
01894    save_done_list(pLch->hKey, &donelist);
01895 
01896    lazy_log_update(NEW_FILE, lazyst.cur_run, lazy.backlabel, lazyst.backfile, cp_time);
01897 
01898    if (msg_flag)
01899       cm_msg(MTALK, "Lazy", "         lazy job %s done!", lazyst.backfile);
01900 
01901    /* generate/update a <channel>_recover.odb file when everything is Ok
01902       after each file copy */
01903    {
01904       char str[128];
01905       /* leave "list label" as it is, as long as the _recover.odb is loaded before
01906          the lazylogger is started with NO -z things should be fine */
01907       /* save the recover with "List Label" empty */
01908       if (lazy.dir[strlen(lazy.dir) - 1] != DIR_SEPARATOR)
01909          sprintf(str, "%s%c%s_recover.odb", lazy.dir, DIR_SEPARATOR, pLch->name);
01910       else
01911          sprintf(str, "%s%s_recover.odb", lazy.dir, pLch->name);
01912 
01913       db_save(hDB, pLch->hKey, str, TRUE);
01914    }
01915 
01916    if (exit_request)
01917       return (FORCE_EXIT);
01918    return NOTHING_TODO;
01919 }
01920 
01921 /*------------------------------------------------------------------*/
01922 int main(int argc, char **argv)
01923 {
01924    char channel_name[32];
01925    char host_name[HOST_NAME_LENGTH];
01926    char expt_name[HOST_NAME_LENGTH];
01927    BOOL daemon;
01928    INT i, msg, ch, size, status, mainlast_time;
01929 
01930    setbuf(stdout, NULL);
01931    setbuf(stderr, NULL);
01932 
01933    /* set default */
01934    host_name[0] = 0;
01935    expt_name[0] = 0;
01936    channel_name[0] = 0;
01937    BOOL zap_flag = FALSE;
01938    msg_flag = FALSE;
01939    debug = daemon = FALSE;
01940 
01941    /* set default */
01942    cm_get_environment(host_name, sizeof(host_name), expt_name, sizeof(expt_name));
01943 
01944    /* get parameters */
01945    for (i = 1; i < argc; i++) {
01946       if (argv[i][0] == '-' && argv[i][1] == 'd')
01947          debug = TRUE;
01948       else if (argv[i][0] == '-' && argv[i][1] == 'n')
01949          nodelete = TRUE;
01950       else if (argv[i][0] == '-' && argv[i][1] == 'D')
01951          daemon = TRUE;
01952       else if (strncmp(argv[i], "-z", 2) == 0)
01953          zap_flag = TRUE;
01954       else if (strncmp(argv[i], "-t", 2) == 0)
01955          msg_flag = TRUE;
01956       else if (argv[i][0] == '-') {
01957          if (i + 1 >= argc || argv[i + 1][0] == '-')
01958             goto usage;
01959          if (strncmp(argv[i], "-e", 2) == 0)
01960             strcpy(expt_name, argv[++i]);
01961          else if (strncmp(argv[i], "-h", 2) == 0)
01962             strcpy(host_name, argv[++i]);
01963          else if (strncmp(argv[i], "-c", 2) == 0)
01964             strcpy(channel_name, argv[++i]);
01965       } else {
01966        usage:
01967          printf("Lazylogger: Multi channel background data copier\n");
01968          printf("\n");
01969          printf("Usage: lazylogger [-d] [-n] [-D] [-h <Hostname>] [-e <Experiment>] [-z] [-t] -c <channel name>\n");
01970          printf("\n");
01971          printf("Options:\n");
01972          printf(" -d - enable debug printout\n");
01973          printf(" -n - do not delete any files (for testing \"Maintain free space\")\n");
01974          printf(" -D - start as a daemon\n");
01975          printf(" -h - connect to experiment on another machine\n");
01976          printf(" -e - connect to non-default experiment\n");
01977          printf(" -z - clear /Lazy/channel/Statistics\n");
01978          printf(" -t - permit lazy logger to TALK (see mlxspeaker)\n");
01979          printf("\n");
01980          printf("Quick man :\n");
01981          printf("The Lazy/Settings tree is composed of the following parameters:\n");
01982          printf("Maintain free space (%%)(0): purge source device to maintain free space on the source directory\n");
01983          printf("                      (0) : no purge      \n");
01984          printf("Stay behind  (0)         : If negative number : lazylog runs starting from the OLDEST\n");
01985          printf("                             run file sitting in the 'Dir data' to the current acquisition\n");
01986          printf("                             run minus the 'Stay behind number'\n");
01987          printf("                            If positive number : lazylog starts from the current\n");
01988          printf("                             acquisition run minus 'Stay behind number' \n");
01989          printf("                            Zero : no stay-behind - files are saved as soon as they are closed\n");
01990          printf("Alarm Class               : Specify the Class to be used in case of Tape Full condition\n");
01991          printf("Running condition         : active/deactive lazylogger under given condition i.e:\n");
01992          printf("                           'ALWAYS' (default)     : Independent of the ACQ state ...\n");
01993          printf("                           'NEVER'                : ...\n");
01994          printf("                           'WHILE_ACQ_NOT_RUNNING': ...\n");
01995          printf("                           '/alias/max_rate < 200'    (max_rate is a link)\n");
01996          printf("                           '/equipment/scaler/variables/scal[4] < 23.45'\n");
01997          printf("                           '/equipment/trigger/statistics/events per sec. < 400'\n");
01998          printf("Data dir                  : MIDAS Data Directory (same as \"/Logger/Data Dir\")\n");
01999          printf("Data format               : Data format (MIDAS)\n");
02000          printf("Filename format           : Run format i.e. \"run%%05d.mid\", or \"*.mid.gz\" or \"*.mid.gz,*.xml\" \n");
02001          printf("List label                : Label of destination save_set.\n");
02002          printf("                            Prevent lazylogger to run if not given.\n");
02003          printf("                            Will be reset if maximum capacity reached.\n");
02004          printf("Execute after rewind      : Execute the command <cmd> after rewind complete\n");
02005          printf("                          : args passed are: 'device path' 'channel name' 'list label'\n");
02006          printf("                          : The actual command will look like: <cmd> /dev/nst0 Tape Data_2000\n");
02007          printf("Backup type               : Destination device type (Disk, Tape, Script, FTP)\n");
02008          printf("Path                      : Destination path (file.ext, /dev/nst0, ftp...)\n");
02009          printf("                            in case of FTP type, the 'Path' entry should be:\n");
02010          printf("                            host, port, user, password, directory, run%%05d.mid\n");
02011          printf("Capacity (Bytes)          : Maximum capacity of the destination device.\n");
02012          printf("modulo                    : Enable multiple lazy on same source. Ex: 3ch : 3.0, 3.1, 3.2\n");
02013          printf("tapeAppend                : Enable positioning of the TAPE to EOD before each lazy copy\n");
02014          return 1;
02015       }
02016    }
02017 
02018    /* Handle SIGPIPE signals generated from errors on the pipe */
02019 #ifdef SIGPIPE
02020    signal(SIGPIPE, SIG_IGN);
02021 #endif
02022    if (daemon) {
02023       printf("Becoming a daemon...\n");
02024       ss_daemon_init(FALSE);
02025    }
02026 
02027    /* connect to experiment */
02028    status = cm_connect_experiment1(host_name, expt_name, "Lazy", 0, DEFAULT_ODB_SIZE, WATCHDOG_TIMEOUT);
02029    if (status != CM_SUCCESS)
02030       return 1;
02031 
02032    /* create a common semaphore for the independent lazylogger */
02033    status = ss_semaphore_create("LAZY", &lazy_semaphore);
02034 
02035    /* check lazy status for multiple channels */
02036    cm_get_experiment_database(&hDB, &hKey);
02037    if (db_find_key(hDB, 0, "/Lazy", &hKey) == DB_SUCCESS) {
02038       HNDLE hSubkey;
02039       KEY key;
02040       char strclient[32];
02041       INT j = 0;
02042       for (i = 0;; i++) {
02043          db_enum_key(hDB, hKey, i, &hSubkey);
02044          if (!hSubkey)
02045             break;
02046          db_get_key(hDB, hSubkey, &key);
02047          if (key.type == TID_KEY) {
02048             /* compose client name */
02049             sprintf(strclient, "Lazy_%s", key.name);
02050             if (cm_exist(strclient, TRUE) == CM_SUCCESS)
02051                lazyinfo[j].active = TRUE;
02052             else
02053                lazyinfo[j].active = FALSE;
02054             strcpy(lazyinfo[j].name, key.name);
02055 
02056             lazyinfo[j].hKey = hSubkey;
02057             j++;
02058          }
02059       }
02060    } else {
02061       /* create settings tree */
02062       char str[32];
02063       channel = 0;
02064       if (channel_name[0] != 0)
02065          strlcpy(lazyinfo[channel].name, channel_name, sizeof(lazyinfo[channel].name));
02066       sprintf(str, "/Lazy/%s/Settings", lazyinfo[channel].name);
02067       db_create_record(hDB, 0, str, LAZY_SETTINGS_STRING);
02068    }
02069 
02070    {  /* Selection of client */
02071       INT i, j;
02072       char str[32];
02073 
02074       if (lazyinfo[0].hKey) {
02075          if (channel_name[0] == 0) {
02076             /* command prompt */
02077             printf(" Available Lazy channels to connect to:\n");
02078             i = 0;
02079             j = 1;
02080             while (lazyinfo[i].hKey) {
02081                if (!lazyinfo[i].active)
02082                   printf("%d) Lazy %s \n", j, lazyinfo[i].name);
02083                else
02084                   printf(".) Lazy %s already active\n", lazyinfo[i].name);
02085                j++;
02086                i++;
02087             }
02088             printf("Enter client number or new lazy client name: ");
02089             i = atoi(ss_gets(str, 32));
02090             if ((i == 0) && ((strlen(str) == 0) || (strncmp(str, " ", 1) == 0))) {
02091                cm_msg(MERROR, "Lazy", "Please specify a valid channel name (%s)", str);
02092                goto error;
02093             }
02094          } else {
02095             /* Skip the command prompt for serving the -c option */
02096             /*
02097                scan if channel_name already exists
02098                Yes : check if client is running
02099                Yes : get out (goto error)
02100                No  : connect (extract index)
02101                No  : new channel (i=0)
02102              */
02103             i = 0;
02104             j = -1;
02105             while (lazyinfo[i].hKey) {
02106                if (equal_ustring(channel_name, lazyinfo[i].name)) {
02107                   /* correct name => check active  */
02108                   if (lazyinfo[i].active) {
02109                      cm_msg(MERROR, "Lazy", "Lazy channel " "%s" " already running!", lazyinfo[i].name);
02110                      goto error;
02111                   }
02112                   j = i;
02113                }
02114                i++;
02115             }
02116             if (j == -1) {
02117                /* new entry */
02118                i = 0;
02119                sprintf(str, "%s", channel_name);
02120             } else {
02121                /* connect to */
02122                i = j + 1;
02123             }
02124          }
02125 
02126          if (i == 0) { /* new entry */
02127             char strclient[32];
02128             for (j = 0; j < MAX_LAZY_CHANNEL; j++) {
02129                if (lazyinfo[j].hKey == 0) {
02130                   /* compose client name */
02131                   sprintf(strclient, "Lazy_%s", str);
02132                   if (cm_exist(strclient, TRUE) == CM_SUCCESS)
02133                      lazyinfo[j].active = TRUE;
02134                   else
02135                      lazyinfo[j].active = FALSE;
02136                   strcpy(lazyinfo[j].name, str);
02137                   lazyinfo[j].hKey = 0;
02138                   channel = j;
02139                   break;
02140                }
02141             }
02142          } else if (!lazyinfo[i - 1].active)
02143             channel = i - 1;
02144          else
02145             channel = -1;
02146       }
02147       
02148       if (channel < 0) goto error;
02149       
02150       { /* creation of the lazy channel */
02151         char str[128];
02152         
02153         if (lazyinfo[channel].hKey == 0)
02154           printf(" Creating Lazy channel %s\n", lazyinfo[channel].name);
02155         
02156         /* create/update settings */
02157         sprintf(str, "/Lazy/%s/Settings", lazyinfo[channel].name);
02158         db_create_record(hDB, 0, str, LAZY_SETTINGS_STRING);
02159         /* create/update statistics */
02160         sprintf(str, "/Lazy/%s/Statistics", lazyinfo[channel].name);
02161         db_create_record(hDB, 0, str, LAZY_STATISTICS_STRING);
02162         sprintf(str, "/Lazy/%s", lazyinfo[channel].name);
02163         db_find_key(hDB, 0, str, &lazyinfo[channel].hKey);
02164       }
02165    }
02166    /* disconnect from experiment */
02167    cm_disconnect_experiment();
02168    
02169    {                            /* reconnect to experiment with proper name */
02170      char str[32];
02171      sprintf(str, "Lazy_%s", lazyinfo[channel].name);
02172      status = cm_connect_experiment1(host_name, expt_name, str, 0, DEFAULT_ODB_SIZE, WATCHDOG_TIMEOUT);
02173    }
02174    if (status != CM_SUCCESS)
02175      goto error;
02176    
02177    cm_get_experiment_database(&hDB, &hKey);
02178    
02179    /* Remove temporary Lazy entry */
02180    {
02181      HNDLE hPkey;
02182      
02183      status = db_find_key(hDB, 0, "Programs/Lazy", &hPkey);
02184      if (status == DB_SUCCESS) {
02185        status = db_delete_key(hDB, hPkey, FALSE);
02186        if (status != DB_SUCCESS) {
02187          cm_msg(MERROR, "Lazy", "Cannot delete /Programs/Lazy");
02188        }
02189      }
02190    }
02191    
02192    /* turn on keepalive messages with increased timeout */
02193    if (debug)
02194      cm_set_watchdog_params(TRUE, 0);
02195    
02196 #ifdef HAVE_FTPLIB
02197    if (debug)
02198      ftp_debug((int (*)(char *)) puts, (int (*)(char *)) puts);
02199 #endif
02200    
02201    printf("Lazy_%s starting... " "!" " to exit \n", lazyinfo[channel].name);
02202    
02203    if (zap_flag) {
02204      /* reset the statistics */
02205      cm_msg(MINFO, "Lazy", "zapping %s/statistics content", lazyinfo[channel].name);
02206      size = sizeof(lazyst);
02207      memset(&lazyst, 0, size);
02208      if (db_find_key(hDB, lazyinfo[channel].hKey, "Statistics", &hKeyst) == DB_SUCCESS)
02209        status = db_set_record(hDB, hKeyst, &lazyst, size, 0);
02210      else
02211        cm_msg(MERROR, "Lazy", "did not find %s/Statistics for zapping", lazyinfo[channel].name);
02212    }
02213 
02214    /* get value once & hot links the run state */
02215    db_find_key(hDB, 0, "/runinfo/state", &hKey);
02216    size = sizeof(run_state);
02217    db_get_data(hDB, hKey, &run_state, &size, TID_INT);
02218    status = db_open_record(hDB, hKey, &run_state, sizeof(run_state), MODE_READ, NULL, NULL);
02219    if (status != DB_SUCCESS) {
02220       cm_msg(MERROR, "Lazy", "cannot open /runinfo/state record");
02221    }
02222    /* hot link for statistics in write mode */
02223    size = sizeof(lazyst);
02224    if (db_find_key(hDB, lazyinfo[channel].hKey, "Statistics", &hKey) == DB_SUCCESS)
02225       db_get_record(hDB, hKey, &lazyst, &size, 0);
02226    status = db_open_record(hDB, hKey, &lazyst, sizeof(lazyst), MODE_WRITE, NULL, NULL);
02227    if (status != DB_SUCCESS) {
02228       cm_msg(MERROR, "Lazy", "cannot open %s/Statistics record", lazyinfo[channel].name);
02229    }
02230    /* get /settings once & hot link settings in read mode */
02231    db_find_key(hDB, lazyinfo[channel].hKey, "Settings", &hKey);
02232    size = sizeof(lazy);
02233    status = db_open_record(hDB, hKey, &lazy, sizeof(lazy)
02234                            , MODE_READ, lazy_settings_hotlink, NULL);
02235    if (status != DB_SUCCESS) {
02236       cm_msg(MERROR, "Lazy", "cannot open %s/Settings record", lazyinfo[channel].name);
02237    }
02238 
02239    /* set global key for that channel */
02240    pcurrent_hKey = lazyinfo[channel].hKey;
02241 
02242    /* set Data dir from /logger if local is empty & /logger exists */
02243    if ((lazy.dir[0] == '\0') && (db_find_key(hDB, 0, "/Logger/Data dir", &hKey) == DB_SUCCESS)) {
02244       size = sizeof(lazy.dir);
02245       db_get_data(hDB, hKey, lazy.dir, &size, TID_STRING);
02246       db_set_value(hDB, lazyinfo[channel].hKey, "Settings/Data dir", lazy.dir, size, 1, TID_STRING);
02247    }
02248 
02249    mainlast_time = 0;
02250 
02251    /* initialize ss_getchar() */
02252    ss_getchar(0);
02253 
02254    do {
02255       msg = cm_yield(2000);
02256       DWORD period = lazy.period*1000;
02257       if (period < 1)
02258          period = 1;
02259       if ((ss_millitime() - mainlast_time) > period) {
02260          status = lazy_main(channel, &lazyinfo[0]);
02261          if (status == FORCE_EXIT) {
02262             cm_msg(MERROR, "lazy", "Exit requested by program");
02263             break;
02264          }
02265          mainlast_time = ss_millitime();
02266       }
02267       ch = 0;
02268       while (ss_kbhit()) {
02269          ch = ss_getchar(0);
02270          if (ch == -1)
02271             ch = getchar();
02272          if ((char) ch == '!')
02273             break;
02274       }
02275    } while (msg != RPC_SHUTDOWN && msg != SS_ABORT && ch != '!');
02276 
02277  error:
02278    cm_disconnect_experiment();
02279    return 1;
02280 }

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