diff --git a/src/as/Makefile b/src/as/Makefile index d5e82b806..a75f96e73 100644 --- a/src/as/Makefile +++ b/src/as/Makefile @@ -4,11 +4,13 @@ include $(TOP)/configure/CONFIG INC += asLib.h INC += asDbLib.h INC += asCa.h +INC += asTrapWrite.h LIBSRCS += asLib.c asIoc_SRCS += asDbLib.c asIoc_SRCS += asCa.c +asIoc_SRCS += asTrapWrite.c LIBRARY_HOST = asHost LIBRARY_IOC = asIoc diff --git a/src/as/asLib.h b/src/as/asLib.h index e9142df88..3eb7ddc81 100644 --- a/src/as/asLib.h +++ b/src/as/asLib.h @@ -35,13 +35,19 @@ long asCheckGet(ASCLIENTPVT asClientPvt); long asCheckPut(ASCLIENTPVT asClientPvt); */ #define asCheckGet(asClientPvt)\ - (asActive ?\ - ((asClientPvt)->access>=asREAD ? TRUE : FALSE)\ + (asActive \ + ? ((asClientPvt)->access>=asREAD ? TRUE : FALSE)\ : TRUE) #define asCheckPut(asClientPvt)\ - (asActive ?\ - ((asClientPvt)->access>=asWRITE ? TRUE : FALSE)\ + (asActive \ + ? ((asClientPvt)->access>=asWRITE ? TRUE : FALSE)\ : TRUE) +#define asTrapWriteBefore(asClientPvt,user,host,addr) \ + (((asActive) && (asClientPvt)->trapMask) \ + ? asTrapWriteBeforeWrite((user),(host),(addr)) \ + : 0) + +#define asTrapWriteAfter(pvt) if((pvt)) asTrapWriteAfterWrite((pvt)) epicsShareFunc long epicsShareAPI asInitialize(ASINPUTFUNCPTR inputfunction); epicsShareFunc long epicsShareAPI asInitFile(const char *filename,const char *substitutions); @@ -76,6 +82,11 @@ epicsShareFunc int epicsShareAPI asDumpRules(char *asgname); epicsShareFunc int epicsShareAPI asDumpMem(char *asgname,void (*memcallback)(ASMEMBERPVT),int clients); epicsShareFunc int epicsShareAPI asDumpHash(void); +epicsShareFunc void * epicsShareAPI asTrapWriteBeforeWrite( + char *userid,char *hostid,void *addr); + +epicsShareFunc void epicsShareAPI asTrapWriteAfterWrite(void *pvt); + #define S_asLib_clientsExist (M_asLib| 1) /*Client Exists*/ #define S_asLib_noUag (M_asLib| 2) /*User Access Group does not exist*/ #define S_asLib_noHag (M_asLib| 3) /*Host Access Group does not exist*/ @@ -137,6 +148,7 @@ typedef struct { ELLNODE node; HAG *phag; }ASGHAG; +#define AS_TRAP_WRITE 1 typedef struct{ ELLNODE node; asAccessRights access; @@ -147,6 +159,7 @@ typedef struct{ void *rpcl; ELLLIST uagList; /*List of ASGUAG*/ ELLLIST hagList; /*List of ASGHAG*/ + int trapMask; } ASGRULE; typedef struct{ ELLNODE node; @@ -173,6 +186,7 @@ typedef struct asgMember { char *asgName; void *userPvt; } ASGMEMBER; + typedef struct asgClient { ELLNODE node; ASGMEMBER *pasgMember; @@ -182,6 +196,7 @@ typedef struct asgClient { ASCLIENTCALLBACK pcallback; int level; asAccessRights access; + int trapMask; } ASGCLIENT; epicsShareFunc long epicsShareAPI asComputeAsg(ASG *pasg); diff --git a/src/as/asLib.y b/src/as/asLib.y index 958a375b2..a61180a69 100644 --- a/src/as/asLib.y +++ b/src/as/asLib.y @@ -132,7 +132,9 @@ inp_body: tokenNAME rule_config: tokenRULE rule_head rule_body | tokenRULE rule_head -rule_head: '(' tokenINTEGER ',' tokenNAME ')' +rule_head: rule_head_manditory rule_head_options + +rule_head_manditory: '(' tokenINTEGER ',' tokenNAME { asAccessRights rights; @@ -151,6 +153,21 @@ rule_head: '(' tokenINTEGER ',' tokenNAME ')' } ; +rule_head_options: ')' + | rule_log_options + +rule_log_options: ',' tokenNAME ')' + { + if((strcmp($2,"TRAPWRITE")==0)) { + long status; + status = asAsgAddRuleOptions(yyAsgRule,AS_TRAP_WRITE); + if(status) yyerror(""); + } else if((strcmp($2,"NOTRAPWRITE")!=0)) { + yyerror("Illegal access type"); + } + free((void *)$2); + } + ; rule_body: '{' rule_list '}' ; diff --git a/src/as/asLibRoutines.c b/src/as/asLibRoutines.c index cd4c0e6f7..ce97c5e19 100644 --- a/src/as/asLibRoutines.c +++ b/src/as/asLibRoutines.c @@ -69,6 +69,7 @@ static long asHagAddHost(HAG *phag,char *host); static ASG *asAsgAdd(char *asgName); static long asAsgAddInp(ASG *pasg,char *inp,int inpIndex); static ASGRULE *asAsgAddRule(ASG *pasg,asAccessRights access,int level); +static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask); static long asAsgRuleUagAdd(ASGRULE *pasgrule,char *name); static long asAsgRuleHagAdd(ASGRULE *pasgrule,char *name); static long asAsgRuleCalc(ASGRULE *pasgrule,char *calc); @@ -465,6 +466,7 @@ long epicsShareAPI asCompute(ASCLIENTPVT asClientPvt) /*The dump routines do not lock. Thus they may get inconsistant data.*/ /*HOWEVER if they did lock and a user interrupts one of then then BAD BAD*/ static char *asAccessName[] = {"NONE","READ","WRITE"}; +static char *asTrapOption[] = {"NOTRAPWRITE","TRAPWRITE"}; static char *asLevelName[] = {"ASL0","ASL1"}; int epicsShareAPI asDump( void (*memcallback)(struct asgMember *), @@ -541,8 +543,9 @@ int epicsShareAPI asDump( while(pasgrule) { int print_end_brace; - printf("\tRULE(%d,%s)", - pasgrule->level,asAccessName[pasgrule->access]); + printf("\tRULE(%d,%s,%s)", + pasgrule->level,asAccessName[pasgrule->access], + asTrapOption[pasgrule->trapMask]); pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); if(pasguag || pasghag || pasgrule->calc) { @@ -592,7 +595,9 @@ int epicsShareAPI asDump( else printf(" Illegal Level %d",pasgclient->level); if(pasgclient->access>=0 && pasgclient->access<=2) - printf(" %s",asAccessName[pasgclient->access]); + printf(" %s %s", + asAccessName[pasgclient->access], + asTrapOption[pasgclient->trapMask]); else printf(" Illegal Access %d",pasgclient->access); if(clientcallback) clientcallback(pasgclient); @@ -698,8 +703,9 @@ int epicsShareAPI asDumpRules(char *asgname) while(pasgrule) { int print_end_brace; - printf("\tRULE(%d,%s)", - pasgrule->level,asAccessName[pasgrule->access]); + printf("\tRULE(%d,%s,%s)", + pasgrule->level,asAccessName[pasgrule->access], + asTrapOption[pasgrule->trapMask]); pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); if(pasguag || pasghag || pasgrule->calc) { @@ -771,7 +777,9 @@ int epicsShareAPI asDumpMem(char *asgname,void (*memcallback)(ASMEMBERPVT),int c else printf(" Illegal Level %d",pasgclient->level); if(pasgclient->access>=0 && pasgclient->access<=2) - printf(" %s",asAccessName[pasgclient->access]); + printf(" %s %s", + asAccessName[pasgclient->access], + asTrapOption[pasgclient->trapMask]); else printf(" Illegal Access %d",pasgclient->access); printf("\n"); @@ -891,6 +899,7 @@ static long asComputeAsgPvt(ASG *pasg) static long asComputePvt(ASCLIENTPVT asClientPvt) { asAccessRights access=asNOACCESS; + int trapMask=0; ASGCLIENT *pasgclient = asClientPvt; ASGMEMBER *pasgMember; ASG *pasg; @@ -943,12 +952,15 @@ check_hag: } check_calc: if(!pasgrule->calc - || (!(pasg->inpBad & pasgrule->inpUsed) && (pasgrule->result==1))) + || (!(pasg->inpBad & pasgrule->inpUsed) && (pasgrule->result==1))) { access = pasgrule->access; + trapMask = pasgrule->trapMask; + } next_rule: pasgrule = (ASGRULE *)ellNext((ELLNODE *)pasgrule); } pasgclient->access = access; + pasgclient->trapMask = trapMask; if(pasgclient->pcallback && oldaccess!=access) { (*pasgclient->pcallback)(pasgclient,asClientCOAR); } @@ -1187,6 +1199,7 @@ static ASGRULE *asAsgAddRule(ASG *pasg,asAccessRights access,int level) if(!pasg) return(0); pasgrule = asCalloc(1,sizeof(ASGRULE)); pasgrule->access = access; + pasgrule->trapMask = 0; pasgrule->level = level; ellInit(&pasgrule->uagList); ellInit(&pasgrule->hagList); @@ -1194,6 +1207,16 @@ static ASGRULE *asAsgAddRule(ASG *pasg,asAccessRights access,int level) return(pasgrule); } +static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask) +{ + if(!pasgrule) { + errMessage(S_asLib_badConfig," Access Security internal failure"); + return(0); + } + pasgrule->trapMask = trapMask; + return(0); +} + static long asAsgRuleUagAdd(ASGRULE *pasgrule,char *name) { ASGUAG *pasguag; diff --git a/src/as/asTrapWrite.c b/src/as/asTrapWrite.c new file mode 100644 index 000000000..5fafc0181 --- /dev/null +++ b/src/as/asTrapWrite.c @@ -0,0 +1,166 @@ +/*asTrapWrite.c */ +/* Author: Marty Kraimer Date: 07NOV2000 */ +/***************************************************************** + COPYRIGHT NOTIFICATION +***************************************************************** + +(C) COPYRIGHT 1993 UNIVERSITY OF CHICAGO + +This software was developed under a United States Government license +described on the COPYRIGHT_UniversityOfChicago file included as part +of this distribution. +**********************************************************************/ + +/* Matthias Clausen and Vladis Korobov at DESY + * implemented the first logging of Channel Access Puts + * This implementation uses many ideas from their implementation +*/ + +#include +#include +#include +#include + +#include "ellLib.h" +#include "freeList.h" +#include "cantProceed.h" +#include "asLib.h" +#include "asTrapWrite.h" +#include "osiSem.h" +#include "ellLib.h" + +typedef struct listenerPvt { + ELLNODE node; + struct listener *plistener; + void *userPvt; +}listenerPvt; + +typedef struct listener{ + ELLNODE node; + asTrapWriteListener func; +}listener; + +typedef struct writeMessage { + ELLNODE node; + asTrapWriteMessage message; + ELLLIST listenerPvtList; +}writeMessage; + + +typedef struct asTrapWritePvt +{ + ELLLIST listenerList; + ELLLIST writeMessageList; + void *freeListWriteMessage; + void *freeListListenerPvt; + semMutexId lock; +}asTrapWritePvt; + +static asTrapWritePvt *pasTrapWritePvt = 0; + +static void asTrapWriteInit(void) +{ + pasTrapWritePvt = callocMustSucceed(1,sizeof(asTrapWritePvt),"asTrapWriteInit"); + ellInit(&pasTrapWritePvt->listenerList); + ellInit(&pasTrapWritePvt->writeMessageList); + freeListInitPvt( + &pasTrapWritePvt->freeListWriteMessage,sizeof(writeMessage),20); + freeListInitPvt( + &pasTrapWritePvt->freeListListenerPvt,sizeof(listenerPvt),20); + pasTrapWritePvt->lock = semMutexMustCreate(); +} + +asTrapWriteId epicsShareAPI asTrapWriteRegisterListener( + asTrapWriteListener func) +{ + listener *plistener; + if(pasTrapWritePvt==0) asTrapWriteInit(); + plistener = callocMustSucceed(1,sizeof(listener),"asTrapWriteRegisterListener"); + plistener->func = func; + semMutexMustTake(pasTrapWritePvt->lock); + ellAdd(&pasTrapWritePvt->listenerList,&plistener->node); + semMutexGive(pasTrapWritePvt->lock); + return((asTrapWriteId)plistener); +} + +void epicsShareAPI asTrapWriteUnregisterListener(asTrapWriteId id) +{ + listener *plistener = (listener *)id; + writeMessage *pwriteMessage; + + if(pasTrapWritePvt==0) return; + semMutexMustTake(pasTrapWritePvt->lock); + pwriteMessage = (writeMessage *)ellFirst(&pasTrapWritePvt->writeMessageList); + while(pwriteMessage) { + listenerPvt *plistenerPvt + = (listenerPvt *)ellFirst(&pwriteMessage->listenerPvtList); + while(plistenerPvt) { + listenerPvt *pnext + = (listenerPvt *)ellNext(&plistenerPvt->node); + if(plistenerPvt->plistener == plistener) { + ellDelete(&pwriteMessage->listenerPvtList,&plistenerPvt->node); + freeListFree(pasTrapWritePvt->freeListListenerPvt,(void *)plistenerPvt); + } + plistenerPvt = pnext; + } + pwriteMessage = (writeMessage *)ellNext(&pwriteMessage->node); + } + ellDelete(&pasTrapWritePvt->listenerList,&plistener->node); + free((void *)plistener); + semMutexGive(pasTrapWritePvt->lock); +} + +void * epicsShareAPI asTrapWriteBeforeWrite( + char *userid,char *hostid,void *addr) +{ + writeMessage *pwriteMessage; + listener *plistener; + listenerPvt *plistenerPvt; + + if(pasTrapWritePvt==0) return(0); + if(ellCount(&pasTrapWritePvt->listenerList)<=0) return 0; + pwriteMessage = (writeMessage *)freeListCalloc( + pasTrapWritePvt->freeListWriteMessage); + pwriteMessage->message.userid = userid; + pwriteMessage->message.hostid = hostid; + pwriteMessage->message.serverSpecific = addr; + ellInit(&pwriteMessage->listenerPvtList); + semMutexMustTake(pasTrapWritePvt->lock); + ellAdd(&pasTrapWritePvt->writeMessageList,&pwriteMessage->node); + plistener = (listener *)ellFirst(&pasTrapWritePvt->listenerList); + while(plistener) { + plistenerPvt = (listenerPvt *)freeListCalloc( + pasTrapWritePvt->freeListListenerPvt); + plistenerPvt->plistener = plistener; + pwriteMessage->message.userPvt = 0; + (*plistener->func)(&pwriteMessage->message,0); + plistenerPvt->userPvt = pwriteMessage->message.userPvt; + ellAdd(&pwriteMessage->listenerPvtList,&plistenerPvt->node); + plistener = (listener *)ellNext(&plistener->node); + } + semMutexGive(pasTrapWritePvt->lock); + return((void *)pwriteMessage); +} + +void epicsShareAPI asTrapWriteAfterWrite(void *pvt) +{ + writeMessage *pwriteMessage = (writeMessage *)pvt; + listenerPvt *plistenerPvt; + + if(pwriteMessage==0 || pasTrapWritePvt==0) return; + semMutexMustTake(pasTrapWritePvt->lock); + plistenerPvt = (listenerPvt *)ellFirst(&pwriteMessage->listenerPvtList); + while(plistenerPvt) { + listenerPvt *pnext = (listenerPvt *)ellNext(&plistenerPvt->node); + listener *plistener; + plistener = plistenerPvt->plistener; + pwriteMessage->message.userPvt = plistenerPvt->userPvt; + (*plistener->func)(&pwriteMessage->message,1); + ellDelete(&pwriteMessage->listenerPvtList,&plistenerPvt->node); + freeListFree(pasTrapWritePvt->freeListListenerPvt,(void *)plistenerPvt); + plistenerPvt = pnext; + } + ellDelete(&pasTrapWritePvt->writeMessageList,&pwriteMessage->node); + freeListFree(pasTrapWritePvt->freeListWriteMessage,(void *)pwriteMessage); + semMutexGive(pasTrapWritePvt->lock); +} diff --git a/src/as/asTrapWrite.h b/src/as/asTrapWrite.h new file mode 100644 index 000000000..6eac7b5d0 --- /dev/null +++ b/src/as/asTrapWrite.h @@ -0,0 +1,55 @@ +/*asTrapWrite.h*/ +/* Author: Marty Kraimer Date: 07NOV2000 */ +/***************************************************************** + COPYRIGHT NOTIFICATION +***************************************************************** + +(C) COPYRIGHT 1993 UNIVERSITY OF CHICAGO + +This software was developed under a United States Government license +described on the COPYRIGHT_UniversityOfChicago file included as part +of this distribution. +**********************************************************************/ + +#ifndef INCasTrapWriteh +#define INCasTrapWriteh + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct asTrapWriteMessage { + char *userid; + char *hostid; + void *serverSpecific; + void *userPvt; +} asTrapWriteMessage; + + +typedef void *asTrapWriteId; +typedef void(*asTrapWriteListener)(asTrapWriteMessage *pmessage,int after); + +epicsShareFunc asTrapWriteId epicsShareAPI asTrapWriteRegisterListener( + asTrapWriteListener func); +epicsShareFunc void epicsShareAPI asTrapWriteUnregisterListener( + asTrapWriteId id); + +/* + * asTrapWriteListener is called before and after the write is performed. + * The listener can set userPvt on the before call and retrieve it after + * after = (0,1) (before,after) the put. + * + * Each asTrapWriteMessage can change or may be deleted after + * the user's asTrapWriteListener returns + * + * asTrapWriteListener delays the associated server thread so it must not + * do anything that causes to to block. +*/ + +#ifdef __cplusplus +} +#endif + +#endif /*INCasTrapWriteh*/ diff --git a/src/rsrv/camessage.c b/src/rsrv/camessage.c index b15764468..e79520c14 100644 --- a/src/rsrv/camessage.c +++ b/src/rsrv/camessage.c @@ -651,6 +651,7 @@ struct client *client struct channel_in_use *pciu; int v41; long status; + void *asWritePvt; pciu = MPTOPCIU(mp); if(!pciu){ @@ -699,11 +700,14 @@ struct client *client mp->m_count); #endif + asWritePvt = asTrapWriteBefore(pciu->asClientPVT, + pciu->client->pUserName,pciu->client->pHostName,(void *)&pciu->addr); status = db_put_field( &pciu->addr, mp->m_dataType, mp + 1, mp->m_count); + asTrapWriteAfter(asWritePvt); if (status < 0) { SEND_LOCK(client); send_err(