/*****************************************************************************/ /* StmLf.c This module implements a full multi-threaded, AST-driven, asynchronous file conversion from variable to stream-LF record format, by copying it into a next-highest version. When called it allocates a dynamic structure and creates its own I/O-event-driven thread. Errors are reported to the HTTPd process log, and do not affect any other activities (including request processing) within the server. Will purge the pre-existing version, however if multiple versions already existed only deletes the one copied from! Why this module's functionality? The WASD VMS HTTPd is significantly more efficient in transfering stream-format than variable-format files. The reason? Stream files can be read using block I/O, variable must be processed record-by-record and buffered into a larger block before network I/O, very much more expensive. This module allows detected variable-format files to be converted to stream-LF "on-the-fly". This happens in a thread separate to the detecting and initiating request, so the original variable-format file is used to concurrently supply that particular request, with the converted file probably available by the time the next request for it occurs. In addition, the WASD HTTPd can determine the "Content-Length:" of stream-LF files and hence can not only supply this item to the client but as a consequence participate in a persistent connection request. VERSION HISTORY --------------- 13-MAR-2008 MGD StmLfLog() include timestamp 30-MAY-2006 MGD bugfix; StmLfLog() -E- to -I- for non-status-value call 27-APR-2002 MGD use sys$setprv() 04-AUG-2001 MGD support module WATCHing 27-DEC-1999 MGD support ODS-2 and ODS-5 28-JAN-1999 MGD ensure created files are truncated-on-close 07-OCT-1998 MGD change path specification to SET rule mapping 19-AUG-1998 MGD confine conversions to specified paths 17-AUG-1997 MGD SYSUAF-authenticated users security-profile 01-AUG-1996 MGD initial, v3.3 */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include /* VMS related header files */ #include #include #include #include #include /* application header files */ #include "wasd.h" #include "httpd.h" #include "stmlf.h" #define WASD_MODULE "STMLF" /** #define STMLF_NOPURGE 1 **/ #define MAX_PURGE_ATTEMPTS 30 /********************/ /* external storage */ /********************/ extern const int64 Delta01Sec; extern unsigned long SysPrvMask[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern CONFIG_STRUCT Config; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* */ StmLfBegin ( char *FilePath, char *FileName, int FileNameLength, BOOL OdsExtended, BOOL VmsUserHasAccess ) { int status; char *cptr, *sptr; struct StreamLfStruct *stmptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (WATCHALL, WATCH_MOD__OTHER, "StmLfBegin() !&Z !&Z !UL !UL", FilePath, FileName, OdsExtended, VmsUserHasAccess); /* allocate dynamic memory for the conversion thread */ stmptr = (struct StreamLfStruct*) VmGet (sizeof(struct StreamLfStruct)); /* reset this boolean so that anything but source EOF is not success */ stmptr->ConversionOk = stmptr->DstFileCreated = false; /* copy the filename into thread-persistent storage */ memcpy (stmptr->FileName, FileName, FileNameLength+1); /* set a limit on the number of attempts to purge the source file */ stmptr->PurgeAttemptCount = MAX_PURGE_ATTEMPTS; stmptr->SrcFileFab = cc$rms_fab; stmptr->SrcFileFab.fab$b_fac = FAB$M_GET; stmptr->SrcFileFab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT; #ifdef ODS_EXTENDED if (OdsExtended) { stmptr->SrcFileFab.fab$l_fna = -1; stmptr->SrcFileFab.fab$b_fns = 0; stmptr->SrcFileFab.fab$l_nam = &stmptr->SrcFileNaml; ENAMEL_RMS_NAML(stmptr->SrcFileNaml) stmptr->SrcFileNaml.naml$l_long_filename = stmptr->FileName; stmptr->SrcFileNaml.naml$l_long_filename_size = FileNameLength; stmptr->SrcFileNaml.naml$l_long_expand = stmptr->SrcExpFileName; stmptr->SrcFileNaml.naml$l_long_expand_alloc = sizeof(stmptr->SrcExpFileName)-1; } else #endif /* ODS_EXTENDED */ { stmptr->SrcFileFab.fab$l_fna = stmptr->FileName; stmptr->SrcFileFab.fab$b_fns = FileNameLength; stmptr->SrcFileFab.fab$l_nam = &stmptr->SrcFileNam; stmptr->SrcFileNam = cc$rms_nam; stmptr->SrcFileNam.nam$l_esa = stmptr->SrcExpFileName; stmptr->SrcFileNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; } if (VmsUserHasAccess) sys$setprv (1, &SysPrvMask, 0, 0); status = sys$open (&stmptr->SrcFileFab, 0, 0); if (VmsUserHasAccess) sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { StmLfLog (stmptr, "sys$open()", status); StmLfEnd (stmptr); return; } #ifdef ODS_EXTENDED if (OdsExtended) stmptr->SrcFileNaml.naml$l_long_ver[stmptr->SrcFileNaml.naml$l_long_ver_size] = '\0'; else #endif /* ODS_EXTENDED */ stmptr->SrcFileNam.nam$l_ver[stmptr->SrcFileNam.nam$b_ver] = '\0'; stmptr->SrcFileRab = cc$rms_rab; stmptr->SrcFileRab.rab$l_fab = &stmptr->SrcFileFab; stmptr->SrcFileRab.rab$b_mbf = 2; stmptr->SrcFileRab.rab$l_rop = RAB$M_RAH | RAB$M_ASY; stmptr->SrcFileRab.rab$l_ubf = stmptr->Buffer; stmptr->SrcFileRab.rab$w_usz = sizeof(stmptr->Buffer); /* set the RAB context to contain the conversion thread pointer */ stmptr->SrcFileRab.rab$l_ctx = stmptr; status = sys$connect (&stmptr->SrcFileRab, 0, 0); if (VMSnok (status)) { StmLfLog (stmptr, "sys$connect() src", status); StmLfEnd (stmptr); return; } stmptr->DstFileFab = cc$rms_fab; stmptr->DstFileFab.fab$l_fop = FAB$M_SQO | FAB$M_TEF; stmptr->DstFileFab.fab$b_rfm = FAB$C_STMLF; #ifdef ODS_EXTENDED if (OdsExtended) { stmptr->DstFileFab.fab$l_fna = -1; stmptr->DstFileFab.fab$b_fns = 0; stmptr->DstFileFab.fab$l_nam = &stmptr->DstFileNaml; ENAMEL_RMS_NAML(stmptr->DstFileNaml) stmptr->DstFileNaml.naml$l_long_filename = stmptr->FileName; for (cptr = stmptr->FileName; *cptr && *cptr != ';'; cptr++); stmptr->DstFileNaml.naml$l_long_filename_size = cptr - stmptr->FileName; stmptr->DstFileNaml.naml$l_long_expand = stmptr->DstExpFileName; stmptr->DstFileNaml.naml$l_long_expand_alloc = sizeof(stmptr->DstExpFileName)-1; stmptr->DstFileNaml.naml$l_long_result = stmptr->DstResFileName; stmptr->DstFileNaml.naml$l_long_result_alloc = sizeof(stmptr->DstResFileName)-1; } else #endif /* ODS_EXTENDED */ { stmptr->DstFileFab.fab$l_fna = stmptr->FileName; for (cptr = stmptr->FileName; *cptr && *cptr != ';'; cptr++); stmptr->DstFileFab.fab$b_fns = cptr - stmptr->FileName; stmptr->DstFileFab.fab$l_nam = &stmptr->DstFileNam; stmptr->DstFileNam = cc$rms_nam; stmptr->DstFileNam.nam$l_esa = stmptr->DstExpFileName; stmptr->DstFileNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; stmptr->DstFileNam.nam$l_rsa = stmptr->DstResFileName; stmptr->DstFileNam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH; } /* use SYSPRV to ensure creation of file (provided protection is S:RWED) */ sys$setprv (1, &SysPrvMask, 0, 0); status = sys$create (&stmptr->DstFileFab, 0, 0); sys$setprv (0, &SysPrvMask, 0, 0); /* check the status of the file create */ if (VMSnok (status)) { StmLfLog (stmptr, "sys$create()", status); StmLfEnd (stmptr); return; } stmptr->DstFileCreated = true; #ifdef ODS_EXTENDED if (OdsExtended) stmptr->DstFileNaml.naml$l_long_ver[stmptr->DstFileNaml.naml$l_long_ver_size] = '\0'; else #endif /* ODS_EXTENDED */ stmptr->DstFileNam.nam$l_ver[stmptr->DstFileNam.nam$b_ver] = '\0'; stmptr->DstFileRab = cc$rms_rab; stmptr->DstFileRab.rab$l_fab = &stmptr->DstFileFab; stmptr->DstFileRab.rab$b_mbf = 2; stmptr->DstFileRab.rab$l_rop = RAB$M_WBH | RAB$M_ASY; stmptr->DstFileRab.rab$l_rbf = stmptr->Buffer; /* set the RAB context to contain the conversion thread pointer */ stmptr->DstFileRab.rab$l_ctx = stmptr; status = sys$connect (&stmptr->DstFileRab, 0, 0); if (VMSnok (status)) { StmLfLog (stmptr, "sys$connect() dst", status); StmLfEnd (stmptr); return; } /* get the first reacord */ status = sys$get (&stmptr->SrcFileRab, &StmLfNextRecordAst, &StmLfNextRecordAst); } /****************************************************************************/ /* Close source and destination files. If successfully converted attempt to purge the version it was copied from. If the file is still locked due to a still continuing transfer to a (the) client then set a timer and try again in one second. Do this a number of times before giving up! If not successfully copied then delete any new version created in the attempt. */ StmLfEnd (struct StreamLfStruct *stmptr) { int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (WATCHALL, WATCH_MOD__OTHER, "StmLfEnd() !8XL", stmptr); if (stmptr->SrcFileFab.fab$w_ifi) sys$close (&stmptr->SrcFileFab, 0, 0); if (stmptr->ConversionOk) { InstanceGblSecIncrLong (&AccountingPtr->StreamLfConversionCount); if (stmptr->DstFileFab.fab$w_ifi) sys$close (&stmptr->DstFileFab, 0, 0); #ifndef STMLF_NOPURGE stmptr->SrcFileFab.fab$l_fop = FAB$M_NAM; /* use SYSPRV to ensure erasure of file */ sys$setprv (1, &SysPrvMask, 0, 0); status = sys$erase (&stmptr->SrcFileFab, 0, 0); sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { if (status == RMS$_FLK) { if (stmptr->PurgeAttemptCount--) { status = sys$setimr (0, &Delta01Sec, &StmLfEnd, stmptr, 0); if (VMSok (status)) return; StmLfLog (stmptr, NULL, status); } else StmLfLog (stmptr, "sys$erase()", status); } else StmLfLog (stmptr, "sys$erase() src", status); } #endif StmLfLog (stmptr, "converted", 0); } else if (stmptr->DstFileCreated) { sys$close (&stmptr->DstFileFab, 0, 0); stmptr->DstFileFab.fab$l_fop = FAB$M_NAM; /* use SYSPRV to ensure erasure of file */ sys$setprv (1, &SysPrvMask, 0, 0); status = sys$erase (&stmptr->DstFileFab, 0, 0); sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) StmLfLog (stmptr, "sys$erase() dst", status); } VmFree (stmptr, FI_LI); } /****************************************************************************/ /* */ StmLfNextRecord (struct RAB *RabPtr) { int status; struct StreamLfStruct *stmptr; /*********/ /* begin */ /*********/ stmptr = RabPtr->rab$l_ctx; if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (WATCHALL, WATCH_MOD__OTHER, "StmLfNextRecord() !&F !8XL sts:!&X stv:!&X\n", &StmLfNextRecord, stmptr, RabPtr->rab$l_sts, RabPtr->rab$l_stv); if (VMSnok (stmptr->DstFileRab.rab$l_sts)) { StmLfEnd (stmptr); return; } status = sys$get (&stmptr->SrcFileRab, &StmLfNextRecordAst, &StmLfNextRecordAst); } /****************************************************************************/ /* */ StmLfNextRecordAst (struct RAB *RabPtr) { int status; struct StreamLfStruct *stmptr; /*********/ /* begin */ /*********/ stmptr = RabPtr->rab$l_ctx; if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (WATCHALL, WATCH_MOD__OTHER, "StmLfNextRecordAst() !&F !8XL sts:!&X stv:!&X\n", &StmLfNextRecordAst, stmptr, RabPtr->rab$l_sts, RabPtr->rab$l_stv, RabPtr->rab$w_rsz); if (VMSnok (stmptr->SrcFileRab.rab$l_sts)) { if (stmptr->SrcFileRab.rab$l_sts == RMS$_EOF) { /* only place this boolean gets set */ stmptr->ConversionOk = true; StmLfEnd (stmptr); return; } StmLfLog (stmptr, "sys$get()", stmptr->SrcFileRab.rab$l_sts); StmLfEnd (stmptr); return; } stmptr->DstFileRab.rab$l_rbf = stmptr->SrcFileRab.rab$l_ubf; stmptr->DstFileRab.rab$w_rsz = stmptr->SrcFileRab.rab$w_rsz; status = sys$put (&stmptr->DstFileRab, &StmLfNextRecord, &StmLfNextRecord); } /****************************************************************************/ /* */ StmLfLog ( struct StreamLfStruct *stmptr, char *Explanation, int StatusValue ) { int status; char *FileNamePtr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (WATCHALL, WATCH_MOD__OTHER, "StmLfLog() !8XL", stmptr); if (stmptr) FileNamePtr = stmptr->FileName; else FileNamePtr = ""; if (StatusValue) FaoToStdout ("%HTTPD-E-STREAMLF, !20%D, !&X !AZ !AZ\n", 0, StatusValue, Explanation, FileNamePtr); else FaoToStdout ("%HTTPD-I-STREAMLF, !20%D, !AZ !AZ\n", 0, Explanation, FileNamePtr); } /****************************************************************************/