program FHOOK1,1.0(106) ! sample file hook processing SBX !---------------------------------------------------------------------------------- !EDIT HISTORY ![100] January 17, 2010 03:49 PM Edited by jacques ! Created ![101] January 19, 2010 05:49 PM Edited by jacques ! Add HFE_PRE_ALLOC handler ![102] January 20, 2010 06:15 PM Edited by jacques ! Process pre'rec data for was/is; add SORT, KILL events ![103] February 14, 2010 05:50 PM Edited by jacques ! Convert the event numeric code back to a descriptive name (to make ! it easier to browse the log by eye - not that you would want to do ! so in a 'real' audit trail where compactness and speed would be paramount.) ![104] March 31, 2010 11:17 AM Edited by jacques ! Fix name is/was trace (was testing name but outputting id) ![105] December 15, 2010 06:02 PM Edited by jack ! Test HFF_DATA_WAS_NA flag (set in 1197.6+) ![106] June 09, 2011 04:22 PM Edited by jack ! Add recsiz to log of open (test bugfix in 5.1.1221) !---------------------------------------------------------------------------------- !NOTES: !- Gets called internally from various file processing routines as follows: ! ! XCALL ,status,envelope,rec,pre'rec ! !Where: ! map1 status,i,2 ! routine should return: ! 0 = ok ! 1, 3, 5, ... 127 = ok, but cancel file op (for HPE_PRE_xxx only) ! ! Note: to signal an error, use ASFLAG,AF_SETCTRLC to send ^C ! ! and return and odd value 129 - 255 ! ! map1 envelope,ST_HOOK_ENV ! ! map1 rec,x,maxrecsiz ! record data ! map1 pre'rec,x,maxrecsiz ! prior record data [not yet implemented as of 1174] ! !- Requires 5.1.1174+ ! !- rec and pre'rec can be mapped as any size, but typically if you are interested in ! logging the record data, you would map them large enough to match the largest ! record layout to be handled by this hook. (You can then use overlays or copying ! to apply the appropriate map layout, after identifying the file from the ! envelope.fileid field). In this sample, we are just using the record layout ! from the FHOOKTST1 test program (ST_TESTHOOKREC) !------------------------------------------------------------------------------------ ++pragma SBX ++include ashinc:ashell.def ++include ashinc:hook.def ++include fhooktst1.def define AUDIT_CH = 65199 ! must be UNIQUE across entire app!!!!! define AUDIT_FILE$ = "opr:fhook1.log" ! audit log file define MAX_RECLEN = 1024 ! max rec size we care about !params map1 params map2 status,i,2 map2 hookenv,ST_HOOK_ENV map2 rec,x,MAX_RECLEN map2 pre'rec,x,MAX_RECLEN ! define one or more record layouts and overlay them on the rec and pre'rec fields ! if you want to be able to break out the record data passed to us map1 testrec,ST_TESTHOOKREC,@rec ! overlay ST_TESTHOOKREC layout on passed rec map1 pre'testrec,ST_TESTHOOKREC,@pre'rec ! [102] map1 misc map2 filename$,s,260 map2 hookmsg$,s,260,@filename$ ! [102] alt names for same param map2 appmsg$,s,512,@filename$ map2 sbxver$,s,20 ! [102] map2 chgflg,b,1 ! [102] significance 11 on error goto TRAP ! [102] xgetargs status,hookenv,rec,pre'rec status = 0 if eof(AUDIT_CH) = -1 then open #AUDIT_CH, AUDIT_FILE$, append ! xcall MIAMEX, MX_NOBUF, AUDIT_CH ! disable buffering endif ? #AUDIT_CH, ODTIM(0,0, &h0200);",";odtim(0,0,&h001);","; & hookenv.user;",";hookenv.prog;",";hookenv.sbx;","; & fn'hook'event'descr$(hookenv.event);",";hookenv.fileid; & hookenv.recno;","; ![103] switch hookenv.event case HFE_PRE_OPEN case HFE_POST_OPEN filename$ = rec ! rec data passed on open is actually the filespec ? #AUDIT_CH;filename$;", rsiz="+str(hookenv.recsiz) ! [106] log recsiz exit case HFE_POST_KILL ! [102] filename$ = rec ! rec data passed on open is actually the filespec ? #AUDIT_CH, filename$ ! log it just for readability, or so it can be easily assoc with fileid exit case HFE_APP_EVENT1 ! [101] app-defined event passes us string in 'rec', just like open does appmsg$ = rec ! [102] rec data passed app event is actually the message from app (MX_FILEHOOK HOOKOP_APPEVENT) ? #AUDIT_CH, appmsg$ ! log it just for readability, or so it can be easily assoc with fileid exit case HFE_PRE_KILL ! [101] make a copy of the file before killing it filename$ = rec ! rec data passed on kill is actually the filespec if lookup(filename$) then xcall MIAMEX, MX_COPYFILE, filename$, "kill.sav", CPYF_REPL, status ? #AUDIT_CH, "prior to killing ";filename$;" make copy as kill.sav; status=";status status = 0 else ? #AUDIT_CH, "Warning: cannot kill ";filename$;" - does not exist" endif exit case HFE_PRE_ALLOC ! [101] filename$ = rec ! rec data passed on alloc is actually the filespec ? #AUDIT_CH, filename$; ! log it just for readability, or so it can be easily assoc with fileid ! [101] before doing our own allocation, prevent a recursive infinite loop by disabling any ! [101] hook on this file xcall MIAMEX, MX_FILEHOOK,HOOKOP_DISABLE,status,FHOOKTST1_FILE$ allocate filename$, hookenv.recno*2 ! [101] but then do our own allocation for twice the size ! now re-enable the hook xcall MIAMEX, MX_FILEHOOK,HOOKOP_ENABLE,status,FHOOKTST1_FILE$ status = 1 ! [101] and return 1 to tell caller not to skip the allocation ? #AUDIT_CH,", (allocated ";hookenv.recno*2;" blocks inside hook!)" exit case HFE_POST_ALLOC ! [101] filename$ = rec ! rec data passed on alloc is actually the filespec ? #AUDIT_CH, filename$ ! log it just for readability, or so it can be easily assoc with fileid exit case HFE_PRE_CLOSE case HFE_POST_CLOSE ? #AUDIT_CH ! nothing more to log on these events exit case HFE_PRE_SORT ! [102] Note that hookenv.recno = # recs to sort case HFE_POST_SORT ! [102] hookmsg$ = rec ! [102] rec contains msg formated by basort.sbr (param info) ? #AUDIT_CH, "Sort: ";hookmsg$ ! log it just for readability, or so it can be easily assoc with fileid exit case HFE_PRE_READ case HFE_PRE_READL case HFE_POST_READ case HFE_POST_READL ? #AUDIT_CH, "" ! [102] exit ! [102] case HFE_PRE_WRITE case HFE_PRE_WRITEL case HFE_POST_WRITE case HFE_POST_WRITEL ! split the raw rec data into fields, based on the rec structure for the fileid switch hookenv.fileid case FHOOKTST1_ID ! FHOOKTST1.DAT if hookenv.flags and HFF_DATA_WAS_NA then ! [105] ? #AUDIT_CH,"(prior data NA) "; endif if ((hookenv.flags and HFF_DATA_WAS)#0) & and ((hookenv.flags and HFF_DATA_WAS_NA)=0) then ![102][105] if (testrec.id # pre'testrec.id) then ? #AUDIT_CH,"id was=";pre'testrec.id;" is=";testrec.id;", "; chgflg = 1 endif if (testrec.name # pre'testrec.name) then ? #AUDIT_CH,"name was=""";pre'testrec.name;""" is=""";testrec.name;""", "; ![104] chgflg = 1 endif if (testrec.balance # pre'testrec.balance) then ? #AUDIT_CH,"balance was=";pre'testrec.balance;" is=";testrec.balance;", "; chgflg = 1 endif if (testrec.limit # pre'testrec.limit) then ? #AUDIT_CH,"limit was=";pre'testrec.limit;" is=";testrec.limit; chgflg = 1 endif if chgflg = 0 then ? #AUDIT_CH,"" else ? #AUDIT_CH endif exit else ![102] if we have no before info, just output new rec fields (if not ]]]) ? #AUDIT_CH,"id=";testrec.id;", name=""";testrec.name;""""; if testrec.name[1,3] # "]]]" then ? #AUDIT_CH,", balance=";testrec.balance; & ", limit=";testrec.limit using "########.##" else ? #AUDIT_CH endif endif exit default ? #AUDIT_CH,"" endswitch exit case HFE_APP_EVENT1 ! custom application event (msg in rec) appmsg$ = rec ! rec data passed on open is actually the filespec ? #AUDIT_CH, appmsg$ ! log it just for readability, or so it can be easily assoc with fileid exit default ? #AUDIT_CH,"" endswitch close #AUDIT_CH ! close file each time xputarg 1,status ! return status end !-------------------------------------------------------------------------------- ![102] Error trap. Currently there is no way to pass the specific error code ! back to the calling program, so it is best to log the error here, then ! attempt to abort the caller by setting Control-C !-------------------------------------------------------------------------------- TRAP: xcall GETVER, sbxver$ hookmsg$ = "[Error #"+err(0)+" in FHOOK1.SBX "+sbxver$+" at location "+err(8)+" !!!]" ? "Setting ^C flag..." SLEEP 5 xcall MIAMEX, MX_ASHLOG, hookmsg$ ! log to ashlog.log xcall ASFLAG, AF_SETCTRLC ! set ^C in caller end ! return to caller !-------------------------------------------------------------------------------- ![103] Convert the numeric hook event code to a description (e.g. WRITE, OPEN, etc) !This makes it easier to check the log visually, which is useful in this test hook, !but probably wouldn't be useful in a 'real' audit trail !Params ! event [b4, in] event code !Returns ! "OPEN", "WRITE", etc. !-------------------------------------------------------------------------------- FUNCTION fn'hook'event'descr$(event as b4) as s10 switch event case HFE_PRE_READ fn'hook'event'descr$ = "READ-" exit case HFE_POST_READ fn'hook'event'descr$ = "READ+" exit case HFE_PRE_READL fn'hook'event'descr$ = "READL-" exit case HFE_POST_READL fn'hook'event'descr$ = "READL+" exit case HFE_PRE_WRITE fn'hook'event'descr$ = "WRITE-" exit case HFE_POST_WRITE fn'hook'event'descr$ = "WRITE+" exit case HFE_PRE_WRITEL fn'hook'event'descr$ = "WRITEL-" exit case HFE_POST_WRITEL fn'hook'event'descr$ = "WRITEL+" exit case HFE_PRE_OPEN fn'hook'event'descr$ = "OPEN-" exit case HFE_POST_OPEN fn'hook'event'descr$ = "OPEN+" exit case HFE_PRE_CLOSE fn'hook'event'descr$ = "CLOSE-" exit case HFE_POST_CLOSE fn'hook'event'descr$ = "CLOSE+" exit case HFE_PRE_ALLOC fn'hook'event'descr$ = "ALLOC-" exit case HFE_POST_ALLOC fn'hook'event'descr$ = "ALLOC+" exit case HFE_PRE_KILL fn'hook'event'descr$ = "KILL-" exit case HFE_POST_KILL fn'hook'event'descr$ = "KILL+" exit case HFE_PRE_SORT fn'hook'event'descr$ = "SORT-" exit case HFE_POST_SORT fn'hook'event'descr$ = "SORT+" exit case HFE_APP_EVENT1 fn'hook'event'descr$ = "APP-EVENT1" exit default fn'hook'event'descr$ = str(event) exit endswitch ENDFUNCTION