/****************************************************************************** * Product: # # ###### # # * # # # # # # # ##### * # # # # # # # # # * # # # # # # # # # * # # # # # # # # # * # # # # # # # # # * # ###### ## ## ##### * * File: vdwd.c * Description: The Virtual Data Warehouse server Daemon process. A daemon * built upon the API libraries of MDC and the Server Data-source * Driver libraries to provide a generic mechanism for * retrieving data from any data source local to the daemon. * The daemon is designed to interact with VDW client * applications and serve their data needs. * * Version: %I% * Dated: %D% * Copyright: P.D. Smart, 1996-2019. * * History: 1.0 - Initial Release. * ****************************************************************************** * This source file is free software: you can redistribute it and#or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This source file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . ******************************************************************************/ /* Bring in system header files. */ #include #include #include #include #include #include #include /* Specials for Solaris. */ #if defined(SOLARIS) || defined(LINUX) #include #include #include #endif /* Indicate that we are a C module for any header specifics. */ #define VDWD_C /* Bring in local specific header files. */ #include "vdwd.h" /****************************************************************************** * Function: GetConfig * Description: Get configuration information from the OS or command line * flags. * * Returns: VDWD_OK - Configuration obtained. * VDWD_FAIL - Failure, see error message. ******************************************************************************/ int GetConfig( int argc, /* I: CLI argument count */ UCHAR **argv, /* I: CLI argument contents */ char **envp, /* I: Environment variables */ UCHAR *szErrMsg ) /* O: Any generated error message */ { /* Local variables. */ int nReturn = VDWD_OK; FILE *fp; UCHAR *szFunc = "GetConfig"; /* See if the user wishes to use a logfile? */ if( GetCLIParam(argc, argv, FLG_LOGFILE, T_STR, VDWD.szLogFile, MAX_LOGFILELEN, FALSE) == R_OK ) { /* Check to see if the filename is valid. */ if((fp=fopen(VDWD.szLogFile, "a")) == NULL) { sprintf(szErrMsg, "Cannot write to logfile (%s)", VDWD.szLogFile); return(VDWD_FAIL); } /* Close the file as test complete. */ fclose(fp); } else { /* Set logfile to a default, dependant on OS. */ strcpy(VDWD.szLogFile, DEF_LOGFILE); } /* Get log mode from command line. */ if(GetCLIParam(argc, argv, FLG_LOGMODE, T_INT, (UCHAR *)&VDWD.nLogMode, 0, 0) == R_OK) { /* Check the validity of the mode. */ if((VDWD.nLogMode < LOG_OFF || VDWD.nLogMode > LOG_FATAL) && VDWD.nLogMode != LOG_CONFIG) { sprintf(szErrMsg, "Illegal Logger mode (%d)", VDWD.nLogMode); return(VDWD_FAIL); } } else { /* Setup default log mode. */ VDWD.nLogMode = LOG_MESSAGE; } /* Finished, get out! */ return( nReturn ); } /****************************************************************************** * Function: VDWDInit * Description: Initialisation of variables, functionality, communications * and turning the process into a daemon. * * Returns: VDWD_OK - Initialised successfully. * VDWD_FAIL - Failure, see error message. ******************************************************************************/ int VDWDInit( UCHAR *szErrMsg ) /* O: Generated error message */ { /* Local variables. */ #if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) pid_t pid; #endif #if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) /* To start daemonisation, we must fork a child to detach from the * parent process. */ if( (pid=fork()) < 0 ) { /* Couldnt fork a child, so build up an error message and then exit * with failure code. */ sprintf(szErrMsg, "Couldnt fork a child process for daemonisation"); return(VDWD_FAIL); } else if( pid != 0 ) { /* As the parent, we exit here, cela-vie. */ exit(0); } /* OK, we are a child and are maturing to be a parent, so lets shed * our childish skin.... * * Firstly, become session leader.. */ setsid(); /* Then ensure we are not hogging an NFS directory. */ chdir("/"); /* Setup the UMASK for known file creation state. */ umask(0); #endif /* Setup logger mode. */ Lgr(LOG_CONFIG, LGM_FLATFILE, VDWD.nLogMode, VDWD.szLogFile); /* All done, lets get out. */ return(VDWD_OK); } /****************************************************************************** * Function: VDWDClose * Description: Function to perform closure of all used resources within the * module. * * Returns: VDWD_OK - Closed successfully. * VDWD_FAIL - Failure, see error message. ******************************************************************************/ int VDWDClose( UCHAR *szErrMsg ) /* O: Generated error message */ { /* Local variables. */ /* If the service hasnt been previously initialised, then there is no * reason to close it down!!! */ if(VDWD.nServiceInitialised == TRUE) { /* Close the currently active service */ if(VDWDCloseService(VDWD.nActiveService, szErrMsg) == VDWD_FAIL) return(VDWD_FAIL); /* Tidy up variables. */ VDWD.nServiceInitialised = FALSE; VDWD.nActiveService = 0; } /* Exit with success. */ return(VDWD_OK); } /****************************************************************************** * Function: VDWDSentToClient * Description: Function to send data from this daemon back to the relevant * client. * * Returns: SDD_OK - Data sent successfully. * SDD_FAIL - Failure in sending data. ******************************************************************************/ int VDWDSendToClient( UCHAR *snzData, /* I: Data to send */ UINT nDataLen ) /* I: Length of data */ { /* Local variables. */ int nReturn = SDD_OK; /* Call the MDC library to transmit the data. If it fails, then just * let the driver know by the return value. */ if(MDC_ReturnData(snzData, nDataLen) == MDC_FAIL) { nReturn = SDD_FAIL; } /* Return code to caller. */ return(nReturn); } /****************************************************************************** * Function: VDWDInitService * Description: Function to call a given drivers initialisation function. * * Returns: VDWD_OK - Service was initialised successfully. * VDWD_FAIL - Failure, see error message. ******************************************************************************/ int VDWDInitService( int nServiceType, /* I: Type of service*/ SERVICEDETAILS *sServiceDet, /* I: Service Data */ UCHAR *szErrMsg ) /* O: Error message */ { /* Local variables. */ UINT nNdx; int nReturn = VDWD_OK; UCHAR *szFunc = "VDWDInitService"; /* Go through list of drivers and locate one that is of the correct type. */ for(nNdx=0; Driver[nNdx].nType != 0 && Driver[nNdx].nType != nServiceType; nNdx++); /* If we located the correct driver then we can perform initialisation. */ if(Driver[nNdx].nType == nServiceType) { /* If there is a registered Initialisation function for this driver * then invoke it, else just return as though everything completed * successfully. */ if(Driver[nNdx].InitService != NULL) { /* Perform the initialisation and set return code accordingly. */ if(Driver[nNdx].InitService(sServiceDet, szErrMsg) == SDD_FAIL) { nReturn = VDWD_FAIL; } } else { /* Log the fact that there is no driver. */ Lgr(LOG_DEBUG, szFunc, "No registered InitService for Driver type (%d)", nServiceType); } } else { /* Build up an error message and exit. */ sprintf(szErrMsg, "%s: Illegal service type (%d)", VDWD_EMSG_BADSERVICE, nServiceType); nReturn = VDWD_FAIL; } /* Finished, return result to caller. */ return(nReturn); } /****************************************************************************** * Function: VDWDCloseService * Description: Function to call a given drivers closedown function. * * Returns: VDWD_OK - Service was closed successfully. * VDWD_FAIL - Failure, see error message. ******************************************************************************/ int VDWDCloseService( int nServiceType, /* I: Type of service*/ UCHAR *szErrMsg ) /* O: Error message */ { /* Local variables. */ UINT nNdx; int nReturn = VDWD_OK; UCHAR *szFunc = "VDWDCloseService"; /* Go through list of drivers and locate one that is of the correct type. */ for(nNdx=0; Driver[nNdx].nType != 0 && Driver[nNdx].nType != nServiceType; nNdx++); /* If we located the correct driver then we can perform a closedown. */ if(Driver[nNdx].nType == nServiceType) { /* If there is a registered Closedown function for this driver * then invoke it, else just return as though everything completed * successfully. */ if(Driver[nNdx].CloseService != NULL) { /* Perform the closedown and set return code accordingly. */ if(Driver[nNdx].CloseService(szErrMsg) == SDD_FAIL) { nReturn = VDWD_FAIL; } } else { /* Log the fact that there is no driver. */ Lgr(LOG_DEBUG, szFunc, "No registered CloseService for Driver type (%d)",nServiceType); } } else { /* Build up an error message and exit. */ sprintf(szErrMsg, "%s: Illegal service type (%d)", VDWD_EMSG_BADSERVICE, nServiceType); nReturn = VDWD_FAIL; } /* Finished, return result to caller. */ return(nReturn); } /****************************************************************************** * Function: VDWDProcessRequest * Description: Function to call a given drivers function to process a * service request. * * Returns: VDWD_OK - Request was processed successfully. * VDWD_FAIL - Failure, see error message. ******************************************************************************/ int VDWDProcessRequest( int nServiceType, /* I: Type of service */ UCHAR *snzData, /* I: Data Buffer */ UINT nDataLen, /* I: Len of Data */ UCHAR *szErrMsg ) /* O: Error message */ { /* Local variables. */ UINT nNdx; int nReturn = VDWD_OK; UCHAR *szFunc = "VDWDProcessRequest"; /* Go through list of drivers and locate one that is of the correct type. */ for(nNdx=0; Driver[nNdx].nType != 0 && Driver[nNdx].nType != nServiceType; nNdx++); /* If we located the correct driver then we can perform the request. */ if(Driver[nNdx].nType == nServiceType) { /* If there is a registered Process Request function for this driver * then invoke it, else just return as though everything completed * successfully. */ if(Driver[nNdx].ProcessRequest != NULL) { /* Call the driver function to process the request. */ if(Driver[nNdx].ProcessRequest(snzData, nDataLen, VDWDSendToClient, szErrMsg) == SDD_FAIL) { nReturn=VDWD_FAIL; } } else { /* Log the fact that there is no driver. */ Lgr(LOG_DEBUG, szFunc, "No registered ProcessRequest for Driver type (%d)", nServiceType); } } else { /* Build up an error message and exit. */ sprintf(szErrMsg, "%s: Illegal service type (%d)", VDWD_EMSG_BADSERVICE, nServiceType); nReturn = VDWD_FAIL; } /* Finished, return result to caller. */ return(nReturn); } /****************************************************************************** * Function: VDWDDataCallback * Description: Function which is registered as a callback and is called * every time data arrives from a new client. * * Returns: MDC_OK - Closed successfully. * MDC_FAIL - Failure, see error message. ******************************************************************************/ int VDWDDataCallback( UCHAR *snzData, /* I: Buffer containing data */ int nDataLen, /* I: Length of data in buffer */ UCHAR *szErrMsg ) /* O: Error messages generated */ { /* Local variables. */ SERVICEDETAILS *sServiceDet; UCHAR *szFunc = "VDWDDataCallback"; /* Data has arrived from a connected client. We know for sure that we * will only ever deal with one client, so its safe to make a lot of * assumptions about the data path. In this function, we only need to * decipher the data packet that we've been passed and dispatch it to * the correct handler. */ /* First, check the length... it must be at least 1 byte!! */ if(nDataLen <= 0) { Lgr(LOG_DEBUG, szFunc, "Being called with %dByte packets", nDataLen); return(MDC_FAIL); } /* Work out the type of packet that we've been passed by analysing the * first byte. */ switch(snzData[0]) { case MDC_ACK: sprintf(szErrMsg, "%s: Server being sent an ACK, illegal!!", VDWD_EMSG_BADACK); return(MDC_FAIL); case MDC_NAK: sprintf(szErrMsg, "%s: Server being sent a NAK, illegal!!", VDWD_EMSG_BADNAK); return(MDC_FAIL); case MDC_PREQ: /* Has the service been initialised yet? If it hasnt, then we * cant possibly pass a data block to the unknown. */ if(VDWD.nServiceInitialised == TRUE) { /* Try and process the given request. */ if(VDWDProcessRequest(VDWD.nActiveService, &snzData[1], nDataLen-1, szErrMsg) == VDWD_FAIL) { return(MDC_FAIL); } } else { /* Just return an error message/code to indicate the problem. */ sprintf(szErrMsg, "%s: No service initialised, data packet illegal!!!", VDWD_EMSG_BADDATA); return(MDC_FAIL); } break; case MDC_CHANGE: case MDC_INIT: /* Is this the first initialisation call...? If it isnt then close * the original service prior to initialising the new service. */ if(VDWD.nServiceInitialised == TRUE) { /* Close the service, exit if we cant, using provided error * message. */ if(VDWDCloseService(VDWD.nActiveService, szErrMsg) == VDWD_FAIL) return(MDC_FAIL); /* Toggle the flag in case of failure. */ VDWD.nServiceInitialised = FALSE; } /* Extract the type of service by casting the provided data to * a service block and then using the structure types. */ snzData++; sServiceDet = (SERVICEDETAILS *)snzData; VDWD.nActiveService=(UINT)sServiceDet->cServiceType; /* Initialise the requested sevice. */ if(VDWDInitService(VDWD.nActiveService, sServiceDet, szErrMsg) == VDWD_FAIL) { return(MDC_FAIL); } /* Set flag to indicate that service has been initialised. */ VDWD.nServiceInitialised = TRUE; break; default: sprintf(szErrMsg, "%s: Unknown Service Type (%x)", VDWD_EMSG_BADSERVICE, snzData[0]); return(MDC_FAIL); } /* Return any result codes to MDC library. */ return(MDC_OK); } /****************************************************************************** * Function: VDWDOOBCallback * Description: Function to take action on out of band commands from the * MDC layer. Out of band messages are generally commands which * need to be actioned upon immediately, so they are passed up * into the Drivers out of band processing function. * * Returns: No returns. ******************************************************************************/ void VDWDOOBCallback( UCHAR cCmd ) /* I: Command to action upon */ { /* Local variables. */ UINT nNdx; UCHAR *szFunc = "VDWDOOBCallback"; /* Has a service been initialised yet? If one hasnt, then there is no * point in going further as OOB processing is only relevant to an * intialised module. */ if(VDWD.nServiceInitialised == FALSE) { /* Log an error message and exit. */ Lgr(LOG_DEBUG, szFunc, "Services not initialised, cannot process OOB (%x)",cCmd); return; } /* Go through list of drivers and locate one that is of the correct type. */ for(nNdx=0; Driver[nNdx].nType != 0 && Driver[nNdx].nType != VDWD.nActiveService; nNdx++); /* If we located the correct driver then we can perform the request. */ if(Driver[nNdx].nType == VDWD.nActiveService) { /* Process out of band message according to type. */ switch(cCmd) { case MDC_ABORT: /* Log message for debug purposes. */ Lgr(LOG_DEBUG, szFunc, "ABORT Out Of Band message received, processing"); if(Driver[nNdx].ProcessOOB != NULL) { Driver[nNdx].ProcessOOB(SDD_ABORT); } else { Lgr(LOG_DEBUG, szFunc, "No registered ABORT OOB handler in driver"); } break; case MDC_EXIT: /* Log message for debug purposes. */ Lgr(LOG_DEBUG, szFunc, "EXIT Out Of Band message received, exitting"); if(Driver[nNdx].ProcessOOB != NULL) { Driver[nNdx].ProcessOOB(SDD_EXIT); } else { Lgr(LOG_DEBUG, szFunc, "No registered EXIT OOB handler in driver"); } break; default: /* Build up an error message and exit. */ Lgr(LOG_DEBUG, szFunc, "Illegal command (%x)", cCmd); break; } } else { /* Build up an error message and exit. */ Lgr(LOG_DEBUG, szFunc, "Called with no active service, cCmd=(%x)",cCmd); } /* Return to caller. */ return; } /****************************************************************************** * Function: main * Description: Entry point into the Virtual Data Warehouse Daemon. Basic * purpose is to invoke intialisation, enter the main program * loop and finally tidy up and close down. * * Returns: 0 - Program completed successfully without errors. * -1 - Program terminated with errors, see logged message. ******************************************************************************/ int main( int argc, /* I: Count of available arguments */ char **argv, /* I: Array of arguments */ char **envp ) /* I: Array of environment parameters */ { /* Local variables. */ UCHAR szErrMsg[MAX_ERRMSG_LEN]; UCHAR *szFunc = "main"; /* Bring in any configuration parameters passed on the command line etc. */ if( GetConfig(argc, (UCHAR **)argv, envp, szErrMsg) == VDWD_FAIL ) { printf( "%s\n" "Usage: %s \n" ": -l\n" " -m\n", szErrMsg, argv[0]); } /* Initialise variables, communications and become a daemon. */ if( VDWDInit(szErrMsg) == VDWD_FAIL ) { /* Log an error message to indicate reason for failure. */ Lgr(LOG_DIRECT, szFunc, "%s: %s", argv[0], szErrMsg); exit(-1); } /* Start the daemon running by passing control into the MDC library and * letting it generate callbacks as events occur. */ if( MDC_Server(NULL, DEF_SERVICENAME, VDWDDataCallback, VDWDOOBCallback ) == MDC_FAIL ) { /* Problems within the MDC library, indicate error and get out. */ Lgr(LOG_DIRECT, szFunc, "%s: MDC_Server error, aborting..", argv[0]); exit(-1); } /* Perform close down of used facilities ready for exit. */ if( VDWDClose(szErrMsg) == VDWD_FAIL ) { /* Log an error message to indicate reason for failure. */ Lgr(LOG_DIRECT, szFunc, "%s: %s", argv[0], szErrMsg); exit(-1); } /* Log off message. */ Lgr(LOG_DEBUG, szFunc, "Child daemon closing."); /* All done, go bye bye's. */ #if defined(SOLARIS) || defined(SUNOS) || defined(LINUX) exit; #endif #if defined(_WIN32) return(0); #endif }