/* iocInit.c ioc initialization */ /* base/src/db $Id$ */ /* * Author: Marty Kraimer * Date: 06-01-91 * * Experimental Physics and Industrial Control System (EPICS) * * Copyright 1991, the Regents of the University of California, * and the University of Chicago Board of Governors. * * This software was produced under U.S. Government contracts: * (W-7405-ENG-36) at the Los Alamos National Laboratory, * and (W-31-109-ENG-38) at Argonne National Laboratory. * * Initial development by: * The Controls and Automation Group (AT-8) * Ground Test Accelerator * Accelerator Technology Division * Los Alamos National Laboratory * * Co-developed with * The Controls and Computing Group * Accelerator Systems Division * Advanced Photon Source * Argonne National Laboratory * * Modification Log: * ----------------- * .01 07-20-91 rac print release data; set env params * .02 08-06-91 mrk parm string length test changed to warning * .03 08-09-91 joh added ioc log client init * .04 09-10-91 joh moved VME stuff from here to initVme() * .05 09-10-91 joh printf() -> logMsg() * .06 09-10-91 joh print message only on failure * .07 08-30-91 rcz completed .02 fix * .08 10-10-91 rcz changed getResources to accomodate EPICS_ * parameters in a structure (first try) * .09 12-02-91 mrk Added finishDevSup * .10 02-10-92 jba Changed error messages * .11 02-28-92 jba ANSI C changes * .12 03-26-92 mrk changed test if(status) to if(rtnval) * .13 04-17-92 rcz changed sdrLoad to dbRead * .14 04-17-92 mrk Added wait before interruptAccept * .15 05-17-92 rcz moved sdrSum stuff to dbReadWrite.c * .16 05-19-92 mrk Changes for internal database structure changes * .17 06-16-92 jba Added prset test to call of init_record second time loop * .18 07-31-92 rcz moved database loading to function dbLoad * .19 08-14-92 jba included dblinks with maximize severity in lockset * .20 08-27-92 mrk removed wakeup_init (For old I/O Event scanning) * .21 09-05-92 rcz changed dbUserExit to initHooks * .22 09-10-92 rcz added many initHooks - INITHOOK* argument * .23 09-10-92 rcz changed funcptr pinitHooks from ret long to void * .24 09-11-92 rcz moved setMasterTimeToSelf to a seperate C file * .25 07-15-93 mrk Changed dbLoad for new dbStaticLib support * .26 02-09-94 jbk changed to new time stamp support software ts_init() * .27 03-18-94 mcn added comments * .28 03-23-94 mrk Added asInit * .29 04-04-94 mcn added code for uninitialized conversions (link conversion field) * .30 01-10-95 joh Fixed no quoted strings in resource.def problem * .31 02-10-95 joh static => LOCAL */ #include #include #include #include #include #include #include #include #include /* for sysSymTbl*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*This module will declare and initilize module_type variables*/ #define MODULE_TYPES_INIT 1 #include LOCAL int initialized=FALSE; /* The following is for use by interrupt routines */ int interruptAccept=FALSE; struct dbBase *pdbBase=NULL; /* added for Channel Access Links */ long dbCommonInit(); /* define forward references*/ LOCAL long initDrvSup(void); LOCAL long initRecSup(void); LOCAL long initDevSup(void); LOCAL long finishDevSup(void); LOCAL long initDatabase(void); LOCAL void createLockSets(void); LOCAL short makeSameSet(struct dbAddr *paddr,short set); LOCAL long initialProcess(void); LOCAL long getResources(char *fname); LOCAL int getResourceToken(FILE *fp, char *pToken, unsigned maxToken); LOCAL int getResourceTokenInternal(FILE *fp, char *pToken, unsigned maxToken); /* * Initialize EPICS on the IOC. */ int iocInit(char * pResourceFilename) { long status; char name[40]; long rtnval; void (*pinitHooks)() = NULL; SYM_TYPE type; if (initialized) { logMsg("iocInit can only be called once\n",0,0,0,0,0,0); return(-1); } if (!pdbBase) { logMsg("iocInit aborting because No database loaded by dbLoad\n",0,0,0,0,0,0); return(-1); } errInit(); /*Initialize errPrintf task*/ /* * Setup initialization hooks, but only if an initHooks routine has been defined. */ strcpy(name, "_"); strcat(name, "initHooks"); rtnval = symFindByName(sysSymTbl, name, (void *) &pinitHooks, &type); /* Call the user-defined initialization hook before anything else is done */ if (pinitHooks) (*pinitHooks)(INITHOOKatBeginning); /* * Print out version of iocCore */ coreRelease(); /* * Read EPICS environment */ epicsSetEnvParams(); /* Hook after environment has been set */ if (pinitHooks) (*pinitHooks)(INITHOOKafterSetEnvParams); /* * Read EPICS resources. */ status = getResources(pResourceFilename); /* Call hook for after resources are read. */ if (pinitHooks) (*pinitHooks)(INITHOOKafterGetResources); /* Initialize log client, and call hook */ status = iocLogInit(); if (status!=0) { logMsg("iocInit Failed to Initialize Ioc Log Client \n",0,0,0,0,0,0); } if (pinitHooks) (*pinitHooks)(INITHOOKafterLogInit); /* * After this point, further calls to iocInit() are disallowed. */ initialized = TRUE; /* * Initialize the watchdog task. These is different from vxWorks watchdogs, * this task is assigned to watching and reporting if the vxWorks tasks * (that it has been told to watch) are suspended. */ taskwdInit(); /* * Initialize EPICS callback tasks */ callbackInit(); /* * Wait 1/10 second to make sure that the above initializations * are complete. */ (void)taskDelay(sysClkRateGet()/10); /* Hook after callbacks are initialized */ if (pinitHooks) (*pinitHooks)(INITHOOKafterCallbackInit); /* * Initialize Channel Access Link mechanism. Pass #1 * Call hook after CA links are initialized */ dbCaLinkInit((int) 1); if (pinitHooks) (*pinitHooks)(INITHOOKafterCaLinkInit1); /* * Initialize Driver Support, Record Support, and finally Device Support. */ if (initDrvSup() != 0) logMsg("iocInit: Drivers Failed during Initialization\n",0,0,0,0,0,0); if (pinitHooks) (*pinitHooks)(INITHOOKafterInitDrvSup); if (initRecSup() != 0) logMsg("iocInit: Record Support Failed during Initialization\n",0,0,0,0,0,0); if (pinitHooks) (*pinitHooks)(INITHOOKafterInitRecSup); if (initDevSup() != 0) logMsg("iocInit: Device Support Failed during Initialization\n",0,0,0,0,0,0); if (pinitHooks) (*pinitHooks)(INITHOOKafterInitDevSup); /* * Initialize the time stamp code. This starts up the time-stamp task. */ /* ts_init(); */ /* old time stamp driver (jbk) */ TSinit(); /* new time stamp driver (jbk) */ if (pinitHooks) (*pinitHooks)(INITHOOKafterTS_init); /* * First pass at initializing the database structure */ if (initDatabase() != 0) logMsg("iocInit: Database Failed during Initialization\n",0,0,0,0,0,0); createLockSets(); if (pinitHooks) (*pinitHooks)(INITHOOKafterInitDatabase); /* added for Channel Access Links */ dbCaLinkInit((int) 2); if (pinitHooks) (*pinitHooks)(INITHOOKafterCaLinkInit2); /* * Finish initializing device support */ if (finishDevSup() != 0) logMsg("iocInit: Device Support Failed during Finalization\n",0,0,0,0,0,0); if (pinitHooks) (*pinitHooks)(INITHOOKafterFinishDevSup); /* * Initialize Scan tasks */ scanInit(); /* wait 1/2 second to make sure all tasks are started*/ /* initialize access security */ asInit(); (void)taskDelay(sysClkRateGet()/2); if (pinitHooks) (*pinitHooks)(INITHOOKafterScanInit); /* * Enable scan tasks and some driver support functions. */ interruptAccept=TRUE; if (pinitHooks) (*pinitHooks)(INITHOOKafterInterruptAccept); /* * Process all records that have their "process at initialization" * field set (pini). */ if (initialProcess() != 0) logMsg("iocInit: initialProcess Failed\n",0,0,0,0,0,0); if (pinitHooks) (*pinitHooks)(INITHOOKafterInitialProcess); /* * Start up CA server */ rsrv_init(); logMsg("iocInit: All initialization complete\n",0,0,0,0,0,0); if (pinitHooks) (*pinitHooks)(INITHOOKatEnd); return(0); } /* * Initialize Driver Support * Locate all driver support entry tables. * Call the initialization routine (init) for each * driver type. */ LOCAL long initDrvSup(void) /* Locate all driver support entry tables */ { char *pname; char name[40]; int i; SYM_TYPE type; char message[100]; long status=0; long rtnval; STATUS vxstatus; struct drvSup *pdrvSup; /* * Make sure at least one device driver is defined in * the ASCII file. */ if (!(pdrvSup=pdbBase->pdrvSup)) { status = S_drv_noDrvSup; errMessage(status,""); return(status); } /* * For every driver support module, look up the name * for that function in the vxWorks symbol table. If * a driver entry table doesn't exist, report that * fact. */ for(i=0; i< (pdrvSup->number); i++) { if (!(pname = pdrvSup->papDrvName[i])) continue; strcpy(name,"_"); strcat(name,pname); vxstatus = symFindByName(sysSymTbl, name, (void *) &(pdrvSup->papDrvet[i]), &type); if (vxstatus != OK) { strcpy(message,": "); strcat(message,pname); status = S_drv_noDrvet; errMessage(status,message); continue; } /* * If an initialization routine is defined (not NULL), * for the driver support call it. */ if (!(pdrvSup->papDrvet[i]->init)) continue; rtnval = (*(pdrvSup->papDrvet[i]->init))(); if (status == 0) status = rtnval; } return(status); } /* * Initialize Record Support * Allocate a record support structure for every record type * plus space for an array of pointers to RSETs. * Locate all record support entry tables. * Call the initialization routine (init) for each * record type. */ LOCAL long initRecSup(void) { char name[40]; int i; SYM_TYPE type; char message[100]; long status=0; long rtnval; STATUS vxstatus; int nbytes; struct recType *precType; struct recSup *precSup; if(!(precType=pdbBase->precType)) { status = S_rectype_noRecs; errMessage(status,""); return(status); } /* * Allocate record support structure, and a list of pointers * after it to point to the Record Support Entry Tables (RSETs) * of individual record types. */ nbytes = sizeof(struct recSup) + precType->number * sizeof(void *); precSup = dbCalloc(1,nbytes); pdbBase->precSup = precSup; /* The number of record support entry tables equals the number of record types */ precSup->number = precType->number; /* * The pointer to the array of pointers of Record Support Entry Tables * exists at the end of the record support structure. */ precSup->papRset = (void *)((long)precSup + (long)sizeof(struct recSup)); /* * For every record support module, look up the name * for that function in the vxWorks symbol table. If * a record support entry table doesn't exist, report * that fact. */ for(i=0; i< (precSup->number); i++) { if (precType->papName[i] == NULL) continue; /* * Create string that is the name of the RSET * (record support entry table) structure for * each record type. */ strcpy(name,"_"); strcat(name,precType->papName[i]); strcat(name,"RSET"); /* Lookup name in vxWorks symbol table - and make sure it is program text */ vxstatus = symFindByName(sysSymTbl, name, (void *) (&precSup->papRset[i]), &type); if (vxstatus != OK) { strcpy(message,": "); strcat(message,name); status = S_rec_noRSET; errMessage(status,message); continue; } /* If an initialization routine exists for a record type, execute it */ if (!(precSup->papRset[i]->init)) continue; else { rtnval = (*(precSup->papRset[i]->init))(); if (status==0) status = rtnval; } } return(status); } /* * Initialize Device Support * Locate all device support entry tables. * Call the initialization routine (init) for each * device type (First Pass). */ LOCAL long initDevSup(void) { char *pname; char name[40]; int i,j; SYM_TYPE type; char message[100]; long status=0; long rtnval; STATUS vxstatus; struct recDevSup *precDevSup; struct devSup *pdevSup; if (!(precDevSup = pdbBase->precDevSup)) { status = S_dev_noDevSup; errMessage(status,""); return(status); } /* * For all possible records, initialize their device support * routines. There is usually more than one device support * routine defined for every record type, this accounts for * the doubly nested for() loops. precDevSup->number = number * of record types. */ for (i=0; i< (precDevSup->number); i++) { /* * Find the device support routine in the array of pointers */ if ((pdevSup = precDevSup->papDevSup[i]) == NULL) continue; /* For every device support module defined for a record type... */ for(j=0; j < (pdevSup->number); j++) { /* Create the name */ if (!(pname = pdevSup->papDsetName[j])) continue; strcpy(name, "_"); strcat(name, pname); /* Lookup name in vxWorks symbol table - make sure it is program text */ vxstatus = (long) symFindByName(sysSymTbl, name, (void *) &(pdevSup->papDset[j]), &type); if (vxstatus != OK) { pdevSup->papDset[j]=NULL; strcpy(message, ": "); strcat(message, pname); status = S_dev_noDSET; errMessage(status, message); continue; } /* If an init routine exists for the device support module, execute it */ if (!(pdevSup->papDset[j]->init)) continue; rtnval = (*(pdevSup->papDset[j]->init))(0); if (status == 0) status = rtnval; } } return(status); } /* * Calls the second pass for each device support * initialization routine. The second pass is made * after the database records have been initialized and * placed into lock sets. */ LOCAL long finishDevSup(void) { int i,j; struct recDevSup *precDevSup; struct devSup *pdevSup; /* Find pointer to device support for all record types */ if (!(precDevSup=pdbBase->precDevSup)) return(0); /* For every record type... */ for(i=0; i< (precDevSup->number); i++) { /* Find pointer to device support for an individual record type */ if ((pdevSup = precDevSup->papDevSup[i]) == NULL) continue; /* For every device support module defined for a record type */ for(j=0; j < (pdevSup->number); j++) { if (!(pdevSup->papDset[j])) continue; /* Call the second pass of the initialization, if the routine exists */ if (!(pdevSup->papDset[j]->init)) continue; (*(pdevSup->papDset[j]->init))(1); } } return(0); } LOCAL long initDatabase(void) { char name[PVNAME_SZ+FLDNAME_SZ+2]; short i,j; char message[120]; long status=0; long rtnval=0; struct recLoc *precLoc; struct rset *prset; struct recDes *precDes; struct recTypDes *precTypDes; struct recHeader *precHeader; RECNODE *precNode; struct fldDes *pfldDes; struct dbCommon *precord; struct dbAddr dbAddr; struct link *plink; struct devSup *pdevSup; struct recSup *precSup; struct recType *precType; /* Find the record type and record support structures */ if(!(precType=pdbBase->precType)) return(0); if(!(precSup=pdbBase->precSup)) return(0); /* * Locate database record header * This structure contains a pointer to an array of pointers to * record location structures. A record location structure * exists for every record type. It contains a pointer to a * linked list of all instances of records with that type. */ if (!(precHeader = pdbBase->precHeader)) { status = S_record_noRecords; errMessage(status,""); return(status); } /* * Find record descriptions * The record descriptions contain every piece of information * you ever wanted to know about the individual fields in a * particular record type. See header file for more information. */ if (!(precDes = pdbBase->precDes)) { status = S_record_noRecords; errMessage(status,"Database record descriptions were not defined"); return(status); } /* For every record type ... */ for(i=0; i< (precHeader->number); i++) { /* Get pointer to location structure for instances of record type 'i' */ if (!(precLoc = precHeader->papRecLoc[i]))continue; if (!precLoc->preclist) continue; /* * Find record support entry table, record description, and device * support associated with record type 'i' */ prset = GET_PRSET(precSup,i); precTypDes = precDes->papRecTypDes[i]; pdevSup = GET_PDEVSUP(pdbBase->precDevSup,i); /* * For all instances of record type 'i '... * These records are contained in the linked list */ for (precNode=(RECNODE *)ellFirst(precLoc->preclist); precNode; precNode = (RECNODE *)ellNext(&precNode->node)) { /* * If there is not record support entry table for record * type 'i', report message, and break from for loop. */ if (!prset) { strcpy(name,precType->papName[i]); strcat(name,"RSET"); strcpy(message,": "); strcat(message,name); status = S_rec_noRSET; errMessage(status,message); break; } /* Find pointer to record instance */ precord = precNode->precord; /* If NAME is null then skip this record*/ if(!(precord->name[0])) continue; /* Initialize record's record support entry table field */ precord->rset = prset; /* * Initialize mlok and mlis. * (The monitor lock - basically a semaphore) and the * monitor list field. The list of "applications" * observing changes in a database field. */ FASTLOCKINIT(&precord->mlok); ellInit(&(precord->mlis)); /* Reset the process active field */ precord->pact=FALSE; /* Init DSET NOTE that result may be NULL */ precord->dset=(struct dset *)GET_PDSET(pdevSup,precord->dtyp); /* Initialize dbCommon structure of the record - First pass (pass=0) */ rtnval = dbCommonInit(precord,0); /* * Call record support init_record routine - First pass * (pass=0). (only if init_record is defined...) */ if (!(precSup->papRset[i]->init_record)) continue; rtnval = (*(precSup->papRset[i]->init_record))(precord,0); if (status==0) status = rtnval; } } /* * Second pass to resolve links */ /* For all record types */ for(i=0; i< (precHeader->number); i++) { /* Find record instances */ if (!(precLoc = precHeader->papRecLoc[i]))continue; if (!precLoc->preclist) continue; /* Find record field descriptions */ precTypDes = precDes->papRecTypDes[i]; /* For all record instances of type 'i' in the linked list... */ for (precNode=(RECNODE *)ellFirst(precLoc->preclist); precNode; precNode = (RECNODE *)ellNext(&precNode->node)) { precord = precNode->precord; /* If NAME is null then skip this record*/ if (!(precord->name[0])) continue; /* * Convert all PV_LINKs to DB_LINKs or CA_LINKs * Figures out what type of link to use. A * database link local to the IOC, or a channel * access link across the network. */ /* For all the links in the record type... */ for(j=0; jno_links; j++) { pfldDes = precTypDes->papFldDes[precTypDes->link_ind[j]]; /* * The actual link structure is located at this offset * within the link structure. */ plink = (struct link *)((char *)precord + pfldDes->offset); /* * Link type should be PV_LINK, unless it is * constant or hardware-specific */ if (plink->type == PV_LINK) { strncpy(name,plink->value.pv_link.pvname,PVNAME_SZ); /* create record name */ name[PVNAME_SZ]=0; strcat(name,"."); strncat(name,plink->value.pv_link.fldname,FLDNAME_SZ); /* * Lookup record name in database * If a record is _not_ local to the IOC, it is a * channel access link, otherwise it is a * database link. */ if (dbNameToAddr(name,&dbAddr) == 0) { plink->type = DB_LINK; plink->value.db_link.pdbAddr = dbCalloc(1,sizeof(struct dbAddr)); *((struct dbAddr *)(plink->value.db_link.pdbAddr))=dbAddr; /* * Initialize conversion to "uninitialized" conversion */ plink->value.db_link.conversion = cvt_uninit; } else { /* * Not a local process variable ... assuming a CA_LINK * Only supporting Non Process Passive links, Input Maximize * Severity/No Maximize Severity(MS/NMS), and output NMS * links ... The following code checks for this. */ if (errVerbose && (plink->value.db_link.process_passive || (pfldDes->field_type == DBF_OUTLINK && plink->value.db_link.maximize_sevr))) { /* * Link PP and/or Outlink MS ... * neither supported under CA_LINKs */ strncpy(message,precord->name,PVNAME_SZ); message[PVNAME_SZ]=0; strcat(message,"."); strncat(message,pfldDes->fldname,FLDNAME_SZ); strcat(message,": link process variable ="); strcat(message,name); strcat(message," PP and/or MS illegal"); status = S_db_badField; errMessage(status,message); status = 0; } } } } } } /* * Call record support init_record routine - Second pass */ /* For all record types... */ for(i=0; i< (precHeader->number); i++) { if (!(precLoc = precHeader->papRecLoc[i])) continue; if (!precLoc->preclist) continue; if (!(prset=GET_PRSET(precSup,i))) continue; precTypDes = precDes->papRecTypDes[i]; /* For all record instances of type 'i' */ for (precNode=(RECNODE *)ellFirst(precLoc->preclist); precNode; precNode = (RECNODE *)ellNext(&precNode->node)) { /* Locate name of record */ precord = precNode->precord; /* If NAME is null then skip this record*/ if (!(precord->name[0])) continue; rtnval = dbCommonInit(precord,1); if (status==0) status = rtnval; /* call record support init_record routine - Second pass (pass = 1) */ if (!(precSup->papRset[i]->init_record)) continue; rtnval = (*(precSup->papRset[i]->init_record))(precord, 1); if (status == 0) status = rtnval; } } return(status); } /* * Create lock sets for records * A lock set is a set of records that must be locked * with a semaphore so as to prevent mutual exclusion * hazards. Lock sets are determined by examining the * links interconnecting the records. If a link connecting * two records is not an NPP/NMS single-valued input link, * then the two records are considered part of the same * lock set. Records connected by forward links are * definately considered part of the same lockset. */ LOCAL void createLockSets(void) { int i,link; struct recLoc *precLoc; struct recDes *precDes; struct recTypDes *precTypDes; struct recHeader *precHeader; RECNODE *precNode; struct fldDes *pfldDes; struct dbCommon *precord; struct link *plink; short nset,maxnset,newset; int again; nset = 0; if(!(precHeader = pdbBase->precHeader)) return; if(!(precDes = pdbBase->precDes)) return; for(i=0; i< (precHeader->number); i++) { if(!(precLoc = precHeader->papRecLoc[i]))continue; if(!precLoc->preclist) continue; precTypDes = precDes->papRecTypDes[i]; for(precNode=(RECNODE *)ellFirst(precLoc->preclist); precNode; precNode = (RECNODE *)ellNext(&precNode->node)) { precord = precNode->precord; /* If NAME is null then skip this record*/ if(!(precord->name[0])) continue; if(precord->lset) continue; /*already in a lock set*/ /* * At First, assume record is in a different lockset * We shall see later if this assumption is incorrect. */ precord->lset = maxnset = ++nset; /* * Use the process active flag to eliminate traversing * cycles in the database "graph" */ precord->pact = TRUE; again = TRUE; while(again) { again = FALSE; for(link=0; linkno_links; link++) { struct dbAddr *pdbAddr; pfldDes = precTypDes->papFldDes[precTypDes->link_ind[link]]; /* * Find link structure, which is at an offset in the record */ plink = (struct link *)((char *)precord + pfldDes->offset); /* Ignore link if type is constant, channel access, or hardware specific */ if(plink->type != DB_LINK) continue; pdbAddr = (struct dbAddr *)(plink->value.db_link.pdbAddr); /* The current record is in a different lockset -IF- * 1. Input link * 2. Not Process Passive * 3. Not Maximize Severity * 4. Not An Array Operation - single element only */ if (pfldDes->field_type==DBF_INLINK && ( !(plink->value.db_link.process_passive) && !(plink->value.db_link.maximize_sevr)) && pdbAddr->no_elements<=1) continue; /* * Combine the lock sets of the current record with the * remote record pointed to by the link. (recursively) */ newset = makeSameSet(pdbAddr,precord->lset); /* * Perform an iteration of the while-loop again * if we find that the record pointed to by * the link has its lockset set earlier. If * it has, set the current record's lockset to * that of the link's endpoint. */ if (newset!=precord->lset) { if(precord->lset==maxnset && maxnset==nset) nset--; precord->lset = newset; again = TRUE; break; } } } precord->pact = FALSE; } } dbScanLockInit(nset); } LOCAL short makeSameSet(struct dbAddr *paddr, short lset) { struct dbCommon *precord = paddr->precord; short link; struct fldDes *pfldDes; struct link *plink; struct recTypDes *precTypDes; struct recDes *precDes; int again; /* * Use the process active flag to eliminate traversing * cycles in the database "graph." Effectively converts * the arbitrary database "graph" (where edges are * defined as links) into a tree structure. */ if(precord->pact) return(((precord->lsetlset : lset)); /* * If the lock set of the link's endpoint is already set * to the lockset we are setting it to, return... */ if(lset == precord->lset) return(lset); /* * If the record has an uninitialized lock set field, * we set it here. */ if(precord->lset == 0) precord->lset = lset; /* * If the record is already in a lockset determined earlier, * return that lock set. */ if(precord->lset < lset) return(precord->lset); if(!(precDes = pdbBase->precDes)) return(0); /* set pact to prevent cycles */ precord->lset = lset; precord->pact = TRUE; again = TRUE; while(again) { again = FALSE; precTypDes = precDes->papRecTypDes[paddr->record_type]; for(link=0; linkno_links; link++) { struct dbAddr *pdbAddr; short newset; pfldDes = precTypDes->papFldDes[precTypDes->link_ind[link]]; plink = (struct link *)((char *)precord + pfldDes->offset); if(plink->type != DB_LINK) continue; pdbAddr = (struct dbAddr *)(plink->value.db_link.pdbAddr); if( pfldDes->field_type==DBF_INLINK && ( !(plink->value.db_link.process_passive) && !(plink->value.db_link.maximize_sevr) ) && pdbAddr->no_elements<=1) continue; newset = makeSameSet(pdbAddr,precord->lset); if(newset != precord->lset) { precord->lset = newset; again = TRUE; break; } } } precord->pact = FALSE; return(precord->lset); } /* * Process database records at initialization if * their pini (process at init) field is set. */ LOCAL long initialProcess(void) { short i; struct recHeader *precHeader; struct recLoc *precLoc; RECNODE *precNode; struct dbCommon *precord; if(!(precHeader = pdbBase->precHeader)) return(0); for(i=0; i< (precHeader->number); i++) { if(!(precLoc = precHeader->papRecLoc[i]))continue; if(!precLoc->preclist) continue; for(precNode=(RECNODE *)ellFirst(precLoc->preclist); precNode; precNode = (RECNODE *)ellNext(&precNode->node)) { precord = precNode->precord; /* If NAME is null then skip this record*/ if(!(precord->name[0])) continue; if(!precord->pini) continue; (void)dbProcess(precord); } } return(0); } #define MAX 256 #define SAME 0 enum resType { resDBF_STRING, resDBF_SHORT, resDBF_LONG, resDBF_FLOAT, resDBF_DOUBLE, resInvalid}; LOCAL char *cvt_str[] = { "DBF_STRING", "DBF_SHORT", "DBF_LONG", "DBF_FLOAT", "DBF_DOUBLE", "Invalid", }; #define EPICS_ENV_PREFIX "EPICS_" long getResources(char *fname) { char s1[MAX]; char s2[MAX]; char s3[MAX]; char name[40]; FILE *fp; enum resType cvType = resInvalid; int epicsFlag; SYM_TYPE type; char *pSymAddr; short n_short; long n_long; float n_float; double n_double; int status; if (!fname) return (0); if ((fp = fopen(fname, "r")) == 0) { errPrintf( -1L, __FILE__, __LINE__, "No such Resource file - %s", fname); return (-1); } while (TRUE) { status = getResourceToken (fp, s1, sizeof(s1)); if (status<0) { /* * EOF */ break; } status = getResourceToken (fp, s2, sizeof(s2)); if (status<0) { errPrintf ( -1L, __FILE__, __LINE__, "Missing resource data type field for resource=%s in file=%s", s1, fname); break; } status = getResourceToken (fp, s3, sizeof(s3)); if (status<0) { errPrintf ( -1L, __FILE__, __LINE__, "Missing resource value field for resource=%s data type=%s file=%s", s1, s2, fname); break; /* EOF */ } for (cvType = 0; cvType < resInvalid; cvType++) { if (strcmp(s2, cvt_str[cvType]) == SAME) { break; } } strcpy(name, "_"); strcat(name, s1); status = symFindByName(sysSymTbl, name, &pSymAddr, &type); if (status!= OK) { errPrintf ( -1L, __FILE__, __LINE__, "Matching Symbol name not found for resource=%s", s1); continue; } status = strncmp ( s1, EPICS_ENV_PREFIX, strlen (EPICS_ENV_PREFIX)); if (status == SAME) { epicsFlag = 1; if (cvType != resDBF_STRING) { errPrintf ( -1L, __FILE__, __LINE__, "%s should be set with type DBF_STRING not type %s", s1, s2); continue; } } else { epicsFlag = 0; } switch (cvType) { case resDBF_STRING: if ( epicsFlag ) { char *pEnv; /* * space for two strings, an '=' character, * and a null termination */ pEnv = malloc (strlen (s3) + strlen (s1) + 2); if (!pEnv) { errPrintf( -1L, __FILE__, __LINE__, "Failed to set environment parameter \"%s\" to \"%s\" because \"%s\"\n", s1, s3, strerror (errnoGet())); break; } strcpy (pEnv, s1); strcat (pEnv, "="); strcat (pEnv, s3); status = putenv (pEnv); if (status<0) { errPrintf( -1L, __FILE__, __LINE__, "Failed to set environment parameter \"%s\" to \"%s\" because \"%s\"\n", s1, s3, strerror (errnoGet())); } /* * vxWorks copies into a private buffer * (this does not match UNIX behavior) */ free (pEnv); } else{ strcpy(pSymAddr, s3); } break; case resDBF_SHORT: if (sscanf(s3, "%hd", &n_short) != 1) { errPrintf ( -1L, __FILE__, __LINE__, "Resource=%s value=%s conversion to %s failed", s1, s3, cvt_str[cvType]); continue; } *(short *) pSymAddr = n_short; break; case resDBF_LONG: if (sscanf(s3, "%ld", &n_long) != 1) { errPrintf ( -1L, __FILE__, __LINE__, "Resource=%s value=%s conversion to %s failed", s1, s3, cvt_str[cvType]); continue; } *(long *) pSymAddr = n_long; break; case resDBF_FLOAT: if (sscanf(s3, "%e", &n_float) != 1) { errPrintf ( -1L, __FILE__, __LINE__, "Resource=%s value=%s conversion to %s failed", s1, s3, cvt_str[cvType]); continue; } *(float *) pSymAddr = n_float; break; case resDBF_DOUBLE: if (sscanf(s3, "%le", &n_double) != 1) { errPrintf ( -1L, __FILE__, __LINE__, "Resource=%s value=%s conversion to %s failed", s1, s3, cvt_str[cvType]); continue; } *(double *) pSymAddr = n_double; break; default: errPrintf ( -1L, __FILE__, __LINE__, "Invalid data type field=%s for resource=%s", s2, s1); continue; } } fclose(fp); return (0); } /* * getResourceToken */ LOCAL int getResourceToken(FILE *fp, char *pToken, unsigned maxToken) { int status; /* * keep reading until we get a token * (and comments have been stripped) */ while (TRUE) { status = getResourceTokenInternal (fp, pToken, maxToken); if (status < 0) { return status; } if (pToken[0] != '\0') { return status; } } } /* * getResourceTokenInternal */ LOCAL int getResourceTokenInternal(FILE *fp, char *pToken, unsigned maxToken) { char formatString[32]; char quoteCharString[2]; int status; quoteCharString[0] = '\0'; status = fscanf (fp, " %1[\"`'!]", quoteCharString); if (status<0) { return status; } switch (quoteCharString[0]) { /* * its a comment * (consume everything up to the next new line) */ case '!': { char tmp[MAX]; sprintf(formatString, "%%%d[^\n\r\v\f]", sizeof(tmp)-1); status = fscanf (fp, "%[^\n\r\v\f]",tmp); pToken[0] = '\0'; if (status<0) { return status; } break; } /* * its a plain token */ case '\0': sprintf(formatString, " %%%ds", maxToken-1); status = fscanf (fp, formatString, pToken); if (status!=1) { if (status < 0){ pToken[0] = '\0'; return status; } } break; /* * it was a quoted string */ default: sprintf( formatString, "%%%d[^%c]", maxToken-1, quoteCharString[0]); status = fscanf (fp, formatString, pToken); if (status!=1) { if (status < 0){ pToken[0] = '\0'; return status; } } sprintf(formatString, "%%1[%c]", quoteCharString[0]); status = fscanf (fp, formatString, quoteCharString); if (status!=1) { errPrintf ( -1L, __FILE__, __LINE__, "Resource file syntax error: unterminated string \"%s\"", pToken); pToken[0] = '\0'; if (status < 0){ return status; } } break; } return 0; } LOCAL int gotSdrSum=FALSE; LOCAL struct sdrSum sdrSum; int dbLoad(char * pfilename) { long status; FILE *fp; fp = fopen(pfilename,"r"); if(!fp) { errMessage(-1,"dbLoad: Error opening file"); return(-1); } if(pdbBase==NULL) pdbBase = dbAllocBase(); status=dbRead(pdbBase, fp); fclose(fp); if(status!=0) { errMessage(status,"dbLoad aborting because dbRead failed"); return(-1); } if(!gotSdrSum) { fp = fopen("default.sdrSum","r"); if(!fp) { errMessage(-1,"dbLoad: Error opening default.sdrSum"); return(-1); } fgets(sdrSum.allSdrSums,sizeof(sdrSum.allSdrSums),fp); fclose(fp); gotSdrSum = TRUE; } if(strncmp(pdbBase->psdrSum->allSdrSums,sdrSum.allSdrSums, strlen(pdbBase->psdrSum->allSdrSums))!=0) { errMessage(-1,"dbLoad: check sdrSum Error: Database out of date"); return(-1); } return (0); }