VOID CloseAsc(ASSOC_IPC * asipc) { int i = 0; /* ** If we are a gcc let the client disconnect first. If ten seconds ** elapse then disconnect anyways. */ while( is_gcc && asipc->IpcInternal.AssocInfo->bPipesClosed == FALSE && i++ < 200 ) PCsleep(50); if (asipc->SndStdChan) CloseHandle(asipc->SndStdChan); if (asipc->RcvStdChan) { WriteFile(asipc->RcvStdChan, "", 1, &i, NULL); Sleep(0); CloseHandle(asipc->RcvStdChan); } if (asipc->SndExpChan) CloseHandle(asipc->SndExpChan); if (asipc->RcvExpChan) { WriteFile(asipc->RcvExpChan, "", 1, &i, NULL); Sleep(0); CloseHandle(asipc->RcvExpChan); } if (asipc->ClientReadHandle) CloseHandle(asipc->ClientReadHandle); /* Mark pipes as being closed. */ if ( asipc->IpcInternal.AssocInfo ) { asipc->IpcInternal.AssocInfo->bPipesClosed = TRUE; asipc->IpcInternal.AssocInfo->refs--; /* decrement reference ct */ asipc->IpcInternal.AssocInfo = NULL; } asipc->SndStdChan = NULL; asipc->RcvStdChan = NULL; asipc->SndExpChan = NULL; asipc->RcvExpChan = NULL; asipc->ClientReadHandle = NULL; if ( asipc->IpcInternal.server ) { asipc->IpcInternal.server->nConnections--; if ( 0 == asipc->IpcInternal.server->nConnections ) RemoveServer( asipc->IpcInternal.server ); } return; }
STATUS create_canon_fe(SEPFILE *res_ptr) { STATUS ret_val = OK ; /* if OK, refresh FE screen */ char do_com_result ; #ifdef VMS if (SEPtcsubs < 4) PCsleep(1000); #endif show_cursor(); do_com_result = do_comments(); append_line(OPEN_CANON,1); switch (do_com_result) { case 'R': case 'r': append_sepfile(res_ptr); break; case 'E': case 'e': break; case 'I': case 'i': append_line(SKIP_SYMBOL,1); break; } append_line(CLOSE_CANON,1); kfe_ptr = kfebuffer; /* reset counter for key buffer */ if (canonAns == EOS) { disp_prompt(NULLSTR,NULL,NULL); /* erase prompt */ } else { CMnext(kfe_ptr); ret_val = FAIL; /* don't refresh FE screen */ } return (ret_val); }
/* ** Name: PCexec_suid - Execute a command as the ingres user. ** ** Description: ** This procedure works with the Ingres service to run the given ** command as the ingres user. It mimicks the "setuid" bit in UNIX. ** ** Inputs: ** cmdbuf - command to execute as the ingres user ** ** Outputs: ** none ** ** Returns: ** OK ** FAIL ** ** Side Effects: ** none ** ** History: ** 08-jan-1998 (somsa01) ** Created. ** 19-feb-1998 (somsa01) ** We need to pass to the service the current working directory ** as well. (Bug #89006) ** 25-feb-1998 (somsa01) ** We now have an input file for the process' stdin which ** runs through the OpenIngres service. ** 19-jun-1998 (somsa01) ** Use SYSTEM_PRODUCT_NAME for the name of the service. ** 10-jul-1998 (kitch01) ** Bug 91362. If user is 'system' run through OpenIngres service ** despite having access to server shared memory 'system' does not ** have required privilege to access semaphores/mutexes. ** 11-jun-1999 (somsa01) ** If the command is a startup command, then it is always run through ** the Ingres service. ** 03-nov-1999 (somsa01) ** A failure from ControlService() should be treated as a severe ** error which should not let us continue. ** 22-jan-2000 (somsa01) ** Return the exit code of the spawned process. Also, if the ** files exist, truncate them. The service name is now keyed off ** of II_INSTALLATION. ** 05-jun-2000 (somsa01) ** The Ingres installation may be started as the SYSTEM account, ** in which the 'ingres' user will not automatically have access ** to the shared memory segments. Therefore, even if the real ** user is 'ingres', check to see if he has access. ** 24-oct-2000 (somsa01) ** Removed the check on shared memory access. Access to the shared ** memory segment does not necessarily mean that the user running ** the process does not need to run the specified process as the ** Ingres owner. Also, generalized the check of the user with ** IDname_service(). ** 18-dec-2000 (somsa01) ** Modified the cases to run the command "as is" without the Ingres ** service. ** 20-mar-2002 (somsa01) ** If all is well, return the exit code of the child process that ** was executed. ** 29-mar-2002 (somsa01) ** Properly return the child process exit code. ** 11-apr-2003 (somsa01) ** While waiting for "pending" to not be set, give some CPU back ** to the OS. ** 29-Jul-2005 (drivi01) ** Allow user to run the command if he/she owns a shared ** segment and ingres is not running as a service. ** 06-Dec-2006 (drivi01) ** Adding support for Vista, Vista requires "Global\" prefix for ** shared objects as well. Replacing calls to GVosvers with ** GVshobj which returns the prefix to shared objects. ** Added PCadjust_SeDebugPrivilege to allow quering of ** System processes. ** 25-Jul-2007 (drivi01) ** On Vista, PCexec_suid is unable to use SE_DEBUG Privilege ** to query process status and retireve its exit code. ** The routine for monitoring a process and retrieving ** its exit code has been moved to Ingres Service. ** 05-Nov-2009 (wanfr01) b122847 ** Don't do a PCsleep unless you are waiting for more input */ STATUS PCexec_suid(char *cmdbuf) { EX_CONTEXT context; SERVICE_STATUS ssServiceStatus; LPSERVICE_STATUS lpssServiceStatus = &ssServiceStatus; struct SETUID setuid; DWORD ProcID; HANDLE SaveStdout; SECURITY_ATTRIBUTES sa; CHAR szRealUserID[25] = ""; CHAR *pszRealUserID = szRealUserID; CHAR szServiceUserID[25] = ""; CHAR *pszServiceUserID = szServiceUserID; DWORD BytesWritten, BytesRead = 0; CHAR *inst_id; CHAR SetuidShmName[64]; CHAR *temp_loc; CHAR InBuf[256], OutBuf[256]; static CHAR SetuidPipeName[32]; CL_ERR_DESC err_code; CHAR ServiceName[255]; DWORD ExitCode = 0; CHAR tchII_INSTALLATION[3]; BOOL SetuidDbCmd = FALSE, ServiceCommand = FALSE; int i, cmdlen; char *ObjectPrefix; u_i4 drType; SC_HANDLE schSCManager, OpIngSvcHandle; BOOL bServiceStarted = FALSE; if (EXdeclare(ex_handler, &context) != OK) { EXdelete(); PCexit(FAIL); } NMgtAt("II_INSTALLATION", &inst_id); STcopy(inst_id, tchII_INSTALLATION); /* ** See if this is a command that MUST be run through the Ingres ** service. */ cmdlen = (i4)STlength(cmdbuf); for (i = 0; ServiceCommands[i] ; i++) { if (STbcompare( cmdbuf, cmdlen, ServiceCommands[i], (i4)STlength(ServiceCommands[i]), FALSE ) == 0) { ServiceCommand = TRUE; break; } } /* ** If the user is the same as the user who started the Ingres ** service, just spawn the command. */ if (!ServiceCommand) { IDname(&pszRealUserID); if (!IDname_service(&pszServiceUserID) && STcompare(pszServiceUserID, pszRealUserID) == 0 && PCisAdmin()) { /* ** Attempt to just execute the command. */ return( PCcmdline( (LOCATION *) NULL, cmdbuf, PC_WAIT, (LOCATION *) NULL, &err_code) ); } else { /* ** If current user is not the same as service user and ingres is not ** running as a service, check if shared memory segment is owned ** by current user, if user has access to shared segment allow him ** to run the command. */ PTR shmem; SIZE_TYPE allocated_pages=0; STATUS status; if((status = MEget_pages(ME_MSHARED_MASK, 1, "lglkdata.mem", &shmem, &allocated_pages, &err_code)) == OK) { STprintf(ServiceName, "%s_Database_%s", SYSTEM_SERVICE_NAME, tchII_INSTALLATION); if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)) != NULL) { if ((OpIngSvcHandle = OpenService(schSCManager, ServiceName, SERVICE_QUERY_STATUS)) != NULL) { if (QueryServiceStatus(OpIngSvcHandle,lpssServiceStatus)) { if (ssServiceStatus.dwCurrentState != SERVICE_STOPPED) bServiceStarted = TRUE; } } } if (!bServiceStarted) return(PCcmdline( (LOCATION *) NULL, cmdbuf, PC_WAIT, (LOCATION *) NULL, &err_code) ); } } /* ** See if this command is an Ingres command which needs to interact ** with at least one database. */ for (i = 0; validSetuidDbCmds[i] ; i++) { if (STbcompare( cmdbuf, cmdlen, validSetuidDbCmds[i], (i4)STlength(validSetuidDbCmds[i]), FALSE ) == 0) { SetuidDbCmd = TRUE; break; } } /* ** If the user has access to the Ingres shared memory segment, ** just spawn the command provided that it is not in the ** validSetuidDbCmds list. */ if (!SetuidDbCmd) { PTR shmem; SIZE_TYPE allocated_pages=0; STATUS status; if (((status = MEget_pages(ME_MSHARED_MASK, 1, "lglkdata.mem", &shmem, &allocated_pages, &err_code)) == OK) || (status == ME_NO_SUCH_SEGMENT)) { if (status != ME_NO_SUCH_SEGMENT) MEfree_pages(shmem, allocated_pages, &err_code); return( PCcmdline( (LOCATION *) NULL, cmdbuf, PC_WAIT, (LOCATION *) NULL, &err_code) ); } } } /* ** We must run the command through the Ingres service. */ if ( STstrindex(cmdbuf, "-silent", 0, FALSE ) ) SilentMode = TRUE; iimksec(&sa); GVshobj(&ObjectPrefix); STprintf(SetuidShmName, "%s%sSetuidShm", ObjectPrefix, tchII_INSTALLATION); if ( (SetuidShmHandle = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, SetuidShmName)) == NULL ) { error_exit(GetLastError()); return(FAIL); } if ( (SetuidShmPtr = MapViewOfFile(SetuidShmHandle, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, sizeof(struct SETUID_SHM))) == NULL ) { error_exit(GetLastError()); return(FAIL); } /* Set up the information to send to the service. */ STcopy(cmdbuf, setuid.cmdline); GetCurrentDirectory(sizeof(setuid.WorkingDirectory), setuid.WorkingDirectory); NMgtAt("II_TEMPORARY", &temp_loc); drType = GetDriveType(NULL); if (drType == DRIVE_REMOTE) { STcopy(temp_loc, setuid.WorkingDirectory); } SaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); CVla(GetCurrentProcessId(), setuid.ClientProcID); STprintf(SetuidPipeName, "\\\\.\\PIPE\\INGRES\\%s\\SETUID", inst_id); /* Set up the stdout file for the command. */ STprintf(OutfileName, "%s\\%sstdout.tmp", temp_loc, setuid.ClientProcID); if ( (OutFile = CreateFile(OutfileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE ) { error_exit(GetLastError()); return(FAIL); } /* Set up the stdin file for the command. */ STprintf(InfileName, "%s\\%sstdin.tmp", temp_loc, setuid.ClientProcID); if ( (InFile = CreateFile(InfileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL)) == INVALID_HANDLE_VALUE ) { error_exit(GetLastError()); return(FAIL); } /* Wait until the service is ready to process our request. */ while (SetuidShmPtr->pending == TRUE) PCsleep(100); SetuidShmPtr->pending = TRUE; /* Trigger the "setuid" event of the service. */ if ( (schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)) == NULL) { error_exit(GetLastError()); return(FAIL); } STprintf(ServiceName, "%s_Database_%s", SYSTEM_SERVICE_NAME, tchII_INSTALLATION ); OpIngSvcHandle = OpenService(schSCManager, ServiceName, SERVICE_USER_DEFINED_CONTROL); if (OpIngSvcHandle == NULL) { STprintf(ServiceName, "%s_DBATools_%s", SYSTEM_SERVICE_NAME, tchII_INSTALLATION ); OpIngSvcHandle = OpenService(schSCManager, ServiceName, SERVICE_USER_DEFINED_CONTROL); } if ( OpIngSvcHandle == NULL) { error_exit(GetLastError()); return(FAIL); } if (!ControlService(OpIngSvcHandle, RUN_COMMAND_AS_INGRES, lpssServiceStatus)) { error_exit(GetLastError()); CloseServiceHandle(schSCManager); return(FAIL); } WaitNamedPipe(SetuidPipeName, NMPWAIT_WAIT_FOREVER); /* Send the information to the service. */ if ( (Setuid_Handle = CreateFile(SetuidPipeName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE ) { error_exit(GetLastError()); return(FAIL); } if (!WriteFile(Setuid_Handle, &setuid, sizeof(struct SETUID), &BytesWritten, NULL)) { error_exit(GetLastError()); return(FAIL); } /* ** Retrieve information back from the service, and then ** disconnect from the pipe. */ if (!ReadFile(Setuid_Handle, &setuid, sizeof(struct SETUID), &BytesRead, NULL)) { error_exit(GetLastError()); return(FAIL); } ProcID = setuid.CreatedProcID; SetuidShmPtr->pending = FALSE; UnmapViewOfFile(SetuidShmPtr); SetuidShmPtr = NULL; CloseHandle(SetuidShmHandle); if ( (ProcID != -1) && (ProcID != -2) ) { /* ** Wait for the "spawned" process to exit, reading its output ** from the stdout file. */ for (;;) { if ( ((!ReadFile(OutFile, OutBuf, sizeof(OutBuf), &BytesRead, NULL) || (BytesRead == 0)) && setuid.ExitCode != STILL_ACTIVE )) break; if ( BytesRead && (!WriteFile(SaveStdout, OutBuf, BytesRead, &BytesWritten, NULL)) && setuid.ExitCode != STILL_ACTIVE) break; else if (BytesRead < sizeof(OutBuf)) PCsleep(200); /* ** Currently, the only DBA program which can require ** user input is verifydb. Therefore, when it spits out ** the appropriate messages asking for user input, get ** it from the end user and pass it along to the spawned ** process. */ if ( (STrstrindex(OutBuf, "S_DU04FF_CONTINUE_PROMPT", 0, FALSE) != NULL) || (STrstrindex(OutBuf, "S_DU0300_PROMPT", 0, FALSE) != NULL) ) { SIflush(stdout); MEfill(sizeof(OutBuf), ' ', &OutBuf); MEfill(sizeof(InBuf), ' ', &InBuf); SIgetrec(InBuf, 255, 0); WriteFile(InFile, InBuf, sizeof(OutBuf), &BytesWritten, NULL); } } ExitCode = setuid.ExitCode; CloseHandle(Setuid_Handle); CloseHandle(InFile); DeleteFile(InfileName); CloseHandle(OutFile); DeleteFile(OutfileName); CloseServiceHandle(OpIngSvcHandle); CloseServiceHandle(schSCManager); return(ExitCode); } else { error_exit(GetLastError()); return(FAIL); } }
ADF_CB * FEadfcb () { if (!done) { char *cp; DB_STATUS rval; PTR srv_cb_ptr; SIZE_TYPE srv_size; SIZE_TYPE dbinfo_size; PTR dbinfoptr; STATUS tmtz_status; STATUS date_status; char date_type_alias[100]; char col[] = "udefault"; STATUS status = OK; CL_ERR_DESC sys_err; ADUUCETAB *ucode_ctbl; /* unicode collation information */ PTR ucode_cvtbl; done = TRUE; /* first, get the size of the ADF's server control block */ srv_size = adg_srv_size(); /* allocate enough memory for it */ if ((srv_cb_ptr = MEreqmem(0, srv_size, FALSE, (STATUS *)NULL)) == NULL) { EXsignal(EXFEBUG, 1, ERx("FEadfcb(alloc)")); } /* ** Fix up code so that "now" works properly for frontends. ** Only allocating enough for one (1) dbmsinfo() request. */ dbinfo_size = sizeof(ADF_TAB_DBMSINFO); if ((dbinfoptr = MEreqmem(0, dbinfo_size, TRUE, (STATUS*)NULL)) == NULL) { IIUGbmaBadMemoryAllocation(ERx("FEadfcb")); } dbinfo = (ADF_TAB_DBMSINFO *) dbinfoptr; dbinfo->tdbi_next = NULL; dbinfo->tdbi_prev = NULL; dbinfo->tdbi_length = dbinfo_size; dbinfo->tdbi_type = ADTDBI_TYPE; dbinfo->tdbi_s_reserved = -1; dbinfo->tdbi_l_reserved = (PTR)-1; dbinfo->tdbi_owner = (PTR)-1; dbinfo->tdbi_ascii_id = ADTDBI_ASCII_ID; dbinfo->tdbi_numreqs = 1; /* ** Now define request. */ dbinfo->tdbi_reqs[0].dbi_func = IIUGnfNowFunc; dbinfo->tdbi_reqs[0].dbi_num_inputs = 0; dbinfo->tdbi_reqs[0].dbi_lenspec.adi_lncompute = ADI_FIXED; STcopy("_BINTIM", dbinfo->tdbi_reqs[0].dbi_reqname); dbinfo->tdbi_reqs[0].dbi_reqcode = 1; dbinfo->tdbi_reqs[0].dbi_lenspec.adi_fixedsize = 4; dbinfo->tdbi_reqs[0].dbi_dtr = DB_INT_TYPE; /* ** set timezone in ADF cb - Any errors must be reported ** after adg_init() called. */ if ( (tmtz_status = TMtz_init(&cb.adf_tzcb)) == OK) { tmtz_status = TMtz_year_cutoff(&cb.adf_year_cutoff); } /* Always use INGRESDATE by default, and check ** 'ii.<node>.config.date_alias' (kibro01) b118702 */ cb.adf_date_type_alias = AD_DATE_TYPE_ALIAS_INGRES; STprintf(date_type_alias,ERx("ii.%s.config.date_alias"),PMhost()); date_status = PMget(date_type_alias, &cp); if (date_status == OK && cp != NULL && STlength(cp)) { if (STbcompare(cp, 0, ERx("ansidate"), 0, 1) == 0) cb.adf_date_type_alias = AD_DATE_TYPE_ALIAS_ANSI; else if (STbcompare(cp, 0, ERx("ingresdate"), 0, 1) == 0) cb.adf_date_type_alias = AD_DATE_TYPE_ALIAS_INGRES; else cb.adf_date_type_alias = AD_DATE_TYPE_ALIAS_NONE; } /* set timezone table to NULL in ADF cb, adg_init() will fill it */ /* cb.adf_tz_table = NULL; - not needed, done in declaration above */ /* Start up ADF */ rval = adg_startup(srv_cb_ptr, srv_size, dbinfo, 0); if (DB_FAILURE_MACRO(rval)) { /* ** Before bailing out, try to be helpful. Since the environment ** is likely not set up correctly, write hard coded messages ** since error message fetch will likely fail in this situation. */ char *tempchar; i4 dummy; LOCATION temploc; NMgtAt("II_SYSTEM", &tempchar); if (tempchar && *tempchar) { LOfroms(PATH, tempchar, &temploc); if (LOexist(&temploc)) { tempchar = ERx("FEadfcb: II_SYSTEM DOES NOT EXIST\n"); SIwrite(STlength(tempchar), tempchar, &dummy, stderr); } else { NMloc(ADMIN, FILENAME, ERx("config.dat"), &temploc); if (LOexist(&temploc)) { tempchar = ERx("FEadfcb: II_SYSTEM IS NOT SET CORRECTLY\n"); SIwrite(STlength(tempchar), tempchar, &dummy, stderr); } } } else { tempchar = ERx("FEadfcb: II_SYSTEM IS NOT SET\n"); SIwrite(STlength(tempchar), tempchar, &dummy, stderr); } EXsignal(EXFEBUG, 1, ERx("FEadfcb(start)")); } /* put the pointer to ADF's server control block in the ADF_CB */ cb.adf_srv_cb = srv_cb_ptr; /* set up the error message buffer */ cb.adf_errcb.ad_ebuflen = DB_ERR_SIZE; cb.adf_errcb.ad_errmsgp = ebuffer; /* initialize the ADF_CB */ rval = adg_init(&cb); if (DB_FAILURE_MACRO(rval)) { EXsignal(EXFEBUG, 1, ERx("FEadfcb(init)")); } /* find out which natural language we should speak */ cb.adf_slang = iiuglcd_langcode(); /* Always QUEL; for SQL explictly change this with 'IIAFdsDmlSet()'. */ cb.adf_qlang = DB_QUEL; /* lets get the multinational info */ /* ** Defaults for all of these are initially set statically, above. */ NMgtAt(ERx("II_DATE_FORMAT"), &cp); if ( cp != NULL && *cp != EOS ) { FEapply_date_format(&cb,cp); } NMgtAt(ERx("II_MONEY_FORMAT"), &cp); if ( cp != NULL && *cp != EOS ) { FEapply_money_format(&cb,cp); } NMgtAt(ERx("II_MONEY_PREC"), &cp); if ( cp != NULL && *cp != EOS ) { FEapply_money_prec(&cb,cp); } NMgtAt(ERx("II_DECIMAL"), &cp); if ( cp != NULL && *cp != EOS ) { FEapply_decimal(&cb,cp); } NMgtAt(ERx("II_UNICODE_SUBS"), &cp); if ( !cp ) { cb.adf_unisub_status = AD_UNISUB_OFF; } else { /* As with SET unicode_substitution take the first char */ cb.adf_unisub_status = AD_UNISUB_ON; *cb.adf_unisub_char = cp[0]; } NMgtAt(ERx("II_NULL_STRING"), &cp); if ( cp != NULL && *cp != EOS ) { FEapply_null(&cb,cp); } /* If there was a Timezone error other than the expected ingbuild ** error TM_PMFILE_OPNERR, then lets report it now. */ if (tmtz_status && (tmtz_status != TM_PMFILE_OPNERR)) { LOCATION loc_root; STATUS status = OK; status = NMloc(FILES, PATH, ERx("zoneinfo"), &loc_root); #if defined(conf_BUILD_ARCH_32_64) && defined(BUILD_ARCH64) status = LOfaddpath(&loc_root, ERx("lp64"), &loc_root); #endif #if defined(conf_BUILD_ARCH_64_32) && defined(BUILD_ARCH32) { /* ** Reverse hybrid support must be available in ALL ** 32bit binaries */ char *rhbsup; NMgtAt("II_LP32_ENABLED", &rhbsup); if ( (rhbsup && *rhbsup) && ( !(STbcompare(rhbsup, 0, "ON", 0, TRUE)) || !(STbcompare(rhbsup, 0, "TRUE", 0, TRUE)))) status = LOfaddpath(&loc_root, "lp32", &loc_root); } #endif /* ! LP64 */ if ( status == OK ) { status = LOexist(&loc_root); } if ( status == OK ) { IIUGerr(tmtz_status, 0, 0); /* As this may disappear from the screen quickly, ** let's pause to allow the users to reflect on the ** fact that the timezone info is (probably) wrong. */ if (!IIugefa_err_function) PCsleep(5000); } } /* Initialize unicode collation for UTF8 installations */ if (CMischarset_utf8()) { if (status = aduucolinit(col, MEreqmem, &ucode_ctbl, &ucode_cvtbl, &sys_err)) { EXsignal(EXFEBUG, 1, ERx("FEadfcb(alloc)")); } cb.adf_ucollation = (PTR) ucode_ctbl; /* Set unicode normalization to NFC just in case ** Front end will need it. */ if ((status = adg_setnfc(&cb)) != E_DB_OK) EXsignal(EXFEBUG, 1, ERx("FEadfcb(alloc)")); } /* lets not do this again! */ } return &cb; }
/*{ ** Name: LGK_initialize() - initialize the lg/lk shared mem segment. ** ** Description: ** This routine is called by the LGinitialize or LKinitialize routine. IT ** assumes that a previous caller has allocated the shared memory segment. ** ** If it discovers that the shared memory segment has not yet been ** initialized, it calls the LG and LK initialize-memory routines to do so. ** ** Inputs: ** flag - bit mask of: ** LOCK_LGK_MEMORY to lock the shared data segment ** LGK_IS_CSP if process is CSP process this node. ** ** Outputs: ** sys_err - place for system-specific error information. ** ** Returns: ** OK - success ** !OK - failure (CS*() routine failure, segment not mapped, ...) ** ** History: ** Summer, 1992 (bryanp) ** Working on the new portable logging and locking system. ** 19-oct-1992 (bryanp) ** Check memory version number when attaching. ** 22-oct-1992 (bryanp) ** Change LGLKDATA.MEM to lglkdata.mem. ** 23-Oct-1992 (daveb) ** name the semaphore too. ** 13-feb-1993 (keving) ** Remove support for II_LGK_MEMORY_SIZE. If II_LG_MEMSIZE ** is not set then calculate memory size from PM values. ** 24-may-1993 (bryanp) ** If the shared memory is the wrong version, don't install the ** at_exit handlers (the rundown routines won't be able to interpret ** the memory properly). ** 26-jul-1993 (jnash) ** Add 'flag' param lock the LGK data segment. ** 20-sep-1993 (bryanp) ** In addition to calling PCatexit, call (on VMS) sys$dclexh, since ** there are some situations (image death and image rundown without ** process rundown) which are caught neither by PCatexit (since ** PCexit isn't run) nor by check-dead threads (since process ** rundown never happened). This fixes a hole where an access- ** violating ckpdb or auditdb command never got cleaned up. ** 31-jan-1994 (bryanp) ** Back out a few "features" which are proving countereffective: ** 1) Don't bother checking mem_creator_pid to see if the previous ** creator of the shared memory has died. This was an attempt to ** gracefully re-use sticky shared memory following a system crash, ** but it is suspected as being the culprit in a series of system ** failures by re-initializing the shared memory at inopportune ** times. ** 2) Don't complain if the shared memory already exists but is of a ** different size than you expected. Just go ahead and try to use ** it anyway. ** 21-feb-1994 (bryanp) ** Reverse item (1) of the above 31-jan-1994 change and re-enable the ** graceful re-use of shared memory. People weren't happy with ** having to run ipcclean and csinstall all the time. ** 23-may-1994 (bryanp) ** On VMS, disable ^Y for LG/LK-aware processes. We don't want to allow ** ^Y because you might interrupt the process right in the middle ** of an LG or LK operation, while holding the shared memory ** semaphore, and this would then wedge the whole installation. ** ** 17-May-1994 (daveb) 59127 ** Attach lgk_mem semaphore if we're attaching to the segment. ** 30-jan-1995 (lawst01) bug 61984 ** Use memory needed calculation from the 'lgk_calculate_size' ** function to determine the size of the shared memory pool for ** locking and locking. If the II_LG_MEMSIZE variable is specified ** with a value larger than needed use the supplied value. If ** lgk_calculate_size is unable to calculate a size then use the ** magic number of 400000. In addition issue a warning message ** and continue executing in the event the number of pages ** allocated is less than the number requested. ** 24-apr-1997 (nanpr01) ** Reinstate Bryanp's change. In the process of fixing bug 61984 ** by Steve Lawrence and subsequent undo of Steve's fix by Nick ** Ireland on 25-jun-96 (nick) caused the if 0 code removed. ** Part of the Steve's change was not reinstated such as not returning ** the status and exit and continue. ** 1. Don't complain if the shared memory already exists but is of a ** different size than you expected. Just go ahead and try to use ** it. ** 18-aug-1998 (hweho01) ** Reclaim the kernel resource if LG/LK shared memory segment is ** reinitialized. If the shared segment is re-used (the previous creator ** of the shared segment has died), the cross-process semaphores get ** initialized more than once at the same locations. That cause the ** kernel resource leaks on DG/UX (OS release 4.11MU04). To fix the ** problem, CS_cp_sem_cleanup() is called to destroy all the ** semaphores before LG/LK shraed segment get recreated. ** CS_cp_sem_cleanup() is made dependent on xCL_NEED_SEM_CLEANUP and ** OS_THREADS_USED, it returns immediately for most platforms. ** 27-Mar-2000 (jenjo02) ** Added test for crossed thread types, refuse connection ** to LGK memory with E_DMA811_LGK_MT_MISMATCH. ** 18-apr-2001 (devjo01) ** s103715 (Portable cluster support) ** - Add CX mem requirement calculations. ** - Add LGK_IS_CSP flag to indicate that LGK memory is being ** initialized for a CSP process. ** - Add basic CX initialization. ** 19-sep-2002 (devjo01) ** If running NUMA clustered allocate memory out of local RAD. ** 30-Apr-2003 (jenjo02) ** Rearchitected to silence long-tolerated race conditions. ** BUG 110121. ** 27-feb-2004 (devjo01) ** Rework allocation of CX shared memory to be compatible ** with race condition fix introduced for bug 110121. ** 29-Dec-2008 (jonj) ** If lgk_calculate_size() returns FAIL, the total memory ** needed exceeds MAX_SIZE_TYPE and we can't continue, but ** tell what we can about the needs of the various bits of ** memory before quitting. ** 06-Aug-2009 (wanfr01) ** Bug 122418 - Return E_DMA812 if LOCK_LGK_MUST_ATTACH is ** is passed in and memory segment does not exist ** 20-Nov-2009 (maspa05) bug 122642 ** In order to synchronize creation of UUIDs across servers added ** a semaphore and a 'last time' variable into LGK memory. ** 14-Dec-2009 (maspa05) bug 122642 ** #ifdef out the above change for Windows. The rest of the change ** does not apply to Windows so the variables aren't defined. */ STATUS LGK_initialize( i4 flag, CL_ERR_DESC *sys_err, char *lgk_info) { PTR ptr; SIZE_TYPE memleft; SIZE_TYPE size; STATUS ret_val; STATUS mem_exists; char mem_name[15]; SIZE_TYPE allocated_pages; i4 me_flags; i4 me_locked_flag; SIZE_TYPE memory_needed; char *nm_string; SIZE_TYPE pages; LGK_MEM *lgk_mem; i4 err_code; SIZE_TYPE min_memory; i4 retries; i4 i; i4 attached; PID *my_pid_slot; i4 clustered; u_i4 nodes; SIZE_TYPE cxmemreq; PTR pcxmem; LGLK_INFO lgkcount; char instid[4]; CL_CLEAR_ERR(sys_err); /* ** if LGK_base is set then this routine has already been called. It is ** set up so that both LGiniitalize and LKinitialize calls it, but only ** the first call does anything. */ if (LGK_base.lgk_mem_ptr) return(OK); PCpid( &LGK_my_pid ); memory_needed = 0; NMgtAt("II_LG_MEMSIZE", &nm_string); if (nm_string && *nm_string) #if defined(LP64) if (CVal8(nm_string, (long*)&memory_needed)) #else if (CVal(nm_string, (i4 *)&memory_needed)) #endif /* LP64 */ memory_needed = 0; /* Always calculate memory needed from PM resource settings */ /* and compare with supplied value, if supplied value is less */ /* than minimum then use minimum */ min_memory = 0; if ( OK == lgk_get_counts(&lgkcount, FALSE)) { if ( lgk_calculate_size(FALSE, &lgkcount, &min_memory) ) { /* ** Memory exceeds MAX_SIZE_TYPE, can't continue. ** ** Do calculation again, this time with "wordy" ** so user can see allocation bits, then quit. */ lgk_calculate_size(TRUE, &lgkcount, &min_memory); return (E_DMA802_LGKINIT_ERROR); } } if (min_memory) memory_needed = (memory_needed < min_memory) ? min_memory : memory_needed; else memory_needed = (memory_needed < 400000 ) ? 400000 : memory_needed; clustered = (i4)CXcluster_enabled(); cxmemreq = 0; if ( clustered ) { if ( OK != CXcluster_nodes( &nodes, NULL ) ) nodes = 0; cxmemreq = CXshm_required( 0, nodes, lgkcount.lgk_max_xacts, lgkcount.lgk_max_locks, lgkcount.lgk_max_resources ); if ( MAX_SIZE_TYPE - memory_needed < cxmemreq ) { /* ** Memory exceeds MAX_SIZE_TYPE, can't continue. ** ** Do calculation again, this time with "wordy" ** so user can see allocation bits, then quit. */ SIprintf("Total LG/LK/CX allocation exceeds max of %lu bytes by %lu\n" "Adjust logging/locking configuration values and try again\n", MAX_SIZE_TYPE, cxmemreq - (MAX_SIZE_TYPE - memory_needed)); lgk_calculate_size(TRUE, &lgkcount, &min_memory); return (E_DMA802_LGKINIT_ERROR); } memory_needed += cxmemreq; } if ( memory_needed < MAX_SIZE_TYPE - ME_MPAGESIZE ) pages = (memory_needed + ME_MPAGESIZE - 1) / ME_MPAGESIZE; else pages = memory_needed / ME_MPAGESIZE; /* ** Lock the LGK segment if requested to do so */ if (flag & LOCK_LGK_MEMORY) me_locked_flag = ME_LOCKED_MASK; else me_locked_flag = 0; me_flags = (me_locked_flag | ME_MSHARED_MASK | ME_IO_MASK | ME_CREATE_MASK | ME_NOTPERM_MASK | ME_MZERO_MASK); if (CXnuma_user_rad()) me_flags |= ME_LOCAL_RAD; STcopy("lglkdata.mem", mem_name); /* ** In general, we just want to attach to the shared memory and detect if ** we are the first process to do so. However, there are ugly race ** conditions to consider, as well as complications because the shared ** memory may be left around following a system crash. ** ** First we attempt to create the shared memory. Usually it already exists, ** so we check for and handle the case of "already exists". */ /* ** (jenjo02) ** ** Restructured to better handle all those ugly race conditions ** which are easily reproduced by running two scripts, one that ** continuously executes "lockstat" while the other is starting ** and stopping Ingres. ** ** For example, ** ** lockstat A acquires and init's the memory ** RCP attaches to "A" memory ** lockstat A terminates normally ** lockstat B attaches to "A" memory, sees that ** "A"s pid is no longer alive, and ** reinitializes the memory, much to ** the RCP's chagrin. ** or (more commonly) ** ** lockstat A acquires and begins to init the mem ** RCP attaches to "A" memory which is ** still being zero-filled by lockstat, ** checks the version number (zero), ** and fails with a E_DMA434 mismatch. ** ** The fix utilizes the mem_ext_sem to synchronize multiple ** processes; if the semaphore hasn't been initialized or ** if mem_version_no is zero, we'll wait one second and retry, ** up to 60 seconds before giving up. This gives the creating ** process time to complete initialization of the memory. ** ** Up to LGK_MAX_PIDS are allowed to attach to the shared ** memory. When a process attaches it sets its PID in the ** first vacant slot in lgk_mem->mem_pid[]; if there are ** no vacant slots, the attach is refused. When the process ** terminates normally by calling LGK_rundown(), it zeroes ** its PID slot. ** ** When attaching to an existing segment, we check if ** there are any live processes still using the memory; ** if so, we can't destroy it (no matter who created it). ** If there are no live processes attached to the memory, ** we destroy and reallocate it (based on current config.dat ** settings). */ for ( retries = 0; ;retries++ ) { LGK_base.lgk_mem_ptr = (PTR)NULL; /* Give up if unable to get memory in one minute */ #if defined(conf_CLUSTER_BUILD) if (retries > 1) #else if ( retries ) #endif { if ( retries < 60 ) PCsleep(1000); else { /* Another process has it blocked way too long */ uleFormat(NULL, E_DMA800_LGKINIT_GETMEM, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, NULL, 0, NULL, &err_code, 0); /* Unable to attach allocated shared memory segment. */ return (E_DMA802_LGKINIT_ERROR); } } ret_val = MEget_pages(me_flags, pages, mem_name, (PTR*)&lgk_mem, &allocated_pages, sys_err); if ( mem_exists = ret_val ) { if (ret_val == ME_ALREADY_EXISTS) { ret_val = MEget_pages((me_locked_flag | ME_MSHARED_MASK | ME_IO_MASK), pages, mem_name, (PTR*)&lgk_mem, &allocated_pages, sys_err); #if defined(conf_CLUSTER_BUILD) if (ret_val && !retries) continue; /* try one more time */ #endif } if (ret_val) { uleFormat(NULL, ret_val, sys_err, ULE_LOG, NULL, NULL, 0, NULL, &err_code, 0); uleFormat(NULL, E_DMA800_LGKINIT_GETMEM, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, NULL, 0, NULL, &err_code, 0); /* Unable to attach allocated shared memory segment. */ return (E_DMA802_LGKINIT_ERROR); } } else if (flag & LOCK_LGK_MUST_ATTACH) { /* Do not use the shared segment you just allocated */ MEfree_pages((PTR)lgk_mem, allocated_pages, sys_err); return (E_DMA812_LGK_NO_SEGMENT); } size = allocated_pages * ME_MPAGESIZE; /* Expose this process to the memory */ LGK_base.lgk_mem_ptr = (PTR)lgk_mem; if ( mem_exists ) { /* ** Memory exists. ** ** Try to acquire the semaphore. If it's ** uninitialzed, retry from the top. ** ** If the version is zero, then another ** process is initializing the memory; ** keep retrying until the version is ** filled in. ** */ if ( ret_val = CSp_semaphore(1, &lgk_mem->mem_ext_sem) ) { if ( ret_val != E_CS000A_NO_SEMAPHORE ) { uleFormat(NULL, ret_val, sys_err, ULE_LOG, NULL, NULL, 0, NULL, &err_code, 0); ret_val = E_DMA802_LGKINIT_ERROR; break; } continue; } /* Retry if still being init'd by another process */ if ( !lgk_mem->mem_version_no ) { CSv_semaphore(&lgk_mem->mem_ext_sem); continue; } /* ** Check pids which appear to be attached to ** the memory: ** ** If any process is still alive, then we ** assume the memory is consistent and use it. ** ** If a process is now dead, it terminated ** without going through LGK_rundown ** to zero its PID slot, zero it now. ** ** If there are no live PIDs attached to ** the memory, we destroy and recreate it. */ my_pid_slot = (PID*)NULL; attached = 0; for ( i = 0; i < LGK_MAX_PIDS; i++ ) { if ( lgk_mem->mem_pid[i] && PCis_alive(lgk_mem->mem_pid[i]) ) { attached++; } else { /* Vacate the slot */ if (lgk_mem->mem_pid[i]) { uleFormat(NULL, E_DMA499_DEAD_PROCESS_INFO, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, NULL, 0, NULL, &err_code, 2, 0, lgk_mem->mem_pid[i], 0, lgk_mem->mem_info[i].info_txt); } lgk_mem->mem_pid[i] = (PID)0; lgk_mem->mem_info[i].info_txt[0] = EOS; /* Use first vacant slot for this process */ if ( !my_pid_slot ) { my_pid_slot = &lgk_mem->mem_pid[i]; LGK_base.lgk_pid_slot = i; } } /* Quit when both questions answered */ if ( attached && my_pid_slot ) break; } /* If no living pids attached, destroy/reallocate */ if ( !attached ) { CSv_semaphore(&lgk_mem->mem_ext_sem); if ( LGK_destroy(allocated_pages, sys_err) ) { ret_val = E_DMA802_LGKINIT_ERROR; break; } continue; } /* All attached pids alive? */ if ( !my_pid_slot ) { /* ... then there's no room for this process */ uleFormat(NULL, E_DMA80A_LGK_ATTACH_LIMIT, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, NULL, 0, NULL, &err_code, 1, 0, attached); ret_val = E_DMA802_LGKINIT_ERROR; } else if (lgk_mem->mem_version_no != LGK_MEM_VERSION_CURRENT) { uleFormat(NULL, E_DMA434_LGK_VERSION_MISMATCH, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, NULL, 0, NULL, &err_code, 2, 0, lgk_mem->mem_version_no, 0, LGK_MEM_VERSION_CURRENT); ret_val = E_DMA435_WRONG_LGKMEM_VERSION; } /* ** Don't allow mixed connections of MT/non-MT processes. ** Among other things, the mutexing mechanisms are ** incompatible! */ else if ( (CS_is_mt() && (lgk_mem->mem_status & LGK_IS_MT) == 0) || (!CS_is_mt() && lgk_mem->mem_status & LGK_IS_MT) ) { uleFormat(NULL, E_DMA811_LGK_MT_MISMATCH, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, NULL, 0, NULL, &err_code, 2, 0, (lgk_mem->mem_status & LGK_IS_MT) ? "OS" : "INTERNAL", 0, (CS_is_mt()) ? "OS" : "INTERNAL"); ret_val = E_DMA802_LGKINIT_ERROR; } else { /* ** CX memory (if any) will lie immediately past LGK header. */ pcxmem = (PTR)(lgk_mem + 1); pcxmem = (PTR)ME_ALIGN_MACRO(pcxmem, sizeof(ALIGN_RESTRICT)); LGK_base.lgk_lkd_ptr = (char *)LGK_base.lgk_mem_ptr + lgk_mem->mem_lkd; LGK_base.lgk_lgd_ptr = (char *)LGK_base.lgk_mem_ptr + lgk_mem->mem_lgd; /* Stuff our pid in first vacant slot */ *my_pid_slot = LGK_my_pid; STlcopy(lgk_info, lgk_mem->mem_info[i].info_txt, LGK_INFO_SIZE-1); } #if defined(VMS) || defined(UNIX) /* set up pointers to reference the uuid mutex and last time * variable */ if (!ID_uuid_sem_ptr) ID_uuid_sem_ptr=&lgk_mem->id_uuid_sem; if (!ID_uuid_last_time_ptr) ID_uuid_last_time_ptr=&lgk_mem->uuid_last_time; if (!ID_uuid_last_cnt_ptr) ID_uuid_last_cnt_ptr=&lgk_mem->uuid_last_cnt; #endif CSv_semaphore(&lgk_mem->mem_ext_sem); } else { /* Memory did not exist */ /* Zero the version to keep other processes out */ lgk_mem->mem_version_no = 0; #if defined(VMS) || defined(UNIX) /* set up the uuid mutex and last time pointers to * reference the objects in shared memory */ { STATUS id_stat; ID_uuid_sem_ptr=&lgk_mem->id_uuid_sem; ID_uuid_last_time_ptr=&lgk_mem->uuid_last_time; ID_uuid_last_cnt_ptr=&lgk_mem->uuid_last_cnt; *ID_uuid_last_cnt_ptr=0; ID_UUID_SEM_INIT(ID_uuid_sem_ptr,CS_SEM_MULTI,"uuid sem", &id_stat); } #endif /* ... then initialize the mutex */ CSw_semaphore(&lgk_mem->mem_ext_sem, CS_SEM_MULTI, "LGK mem ext sem" ); /* Record if memory created for MT or not */ if ( CS_is_mt() ) lgk_mem->mem_status = LGK_IS_MT; /* ** memory is as follows: ** ** -----------------------------------------------------------| ** | LGK_MEM struct (keep track of this mem) | ** | | ** -----------------------------------------------------------| ** | If a clustered installation memory reserved for CX | ** | | ** ------------------------------------------------------------ ** | LKD - database of info for lk system | ** | | ** ------------------------------------------------------------ ** | LGD - database of info for lg system | ** | | ** ------------------------------------------------------------ ** | memory manipulated by LGKm_* routines for structures used | ** | by both the lk and lg systems. | ** | | ** ------------------------------------------------------------ */ /* put the LGK_MEM struct at head of segment leaving ptr pointing ** at next aligned piece of memory */ /* ** CX memory (if any) will lie immediately past LGK header. */ pcxmem = (PTR)(lgk_mem + 1); pcxmem = (PTR)ME_ALIGN_MACRO(pcxmem, sizeof(ALIGN_RESTRICT)); LGK_base.lgk_lkd_ptr = pcxmem + cxmemreq; LGK_base.lgk_lkd_ptr = (PTR) ME_ALIGN_MACRO(LGK_base.lgk_lkd_ptr, sizeof(ALIGN_RESTRICT)); lgk_mem->mem_lkd = (i4)((char *)LGK_base.lgk_lkd_ptr - (char *)LGK_base.lgk_mem_ptr); LGK_base.lgk_lgd_ptr = (PTR) ((char *) LGK_base.lgk_lkd_ptr + sizeof(LKD)); LGK_base.lgk_lgd_ptr = (PTR) ME_ALIGN_MACRO(LGK_base.lgk_lgd_ptr, sizeof(ALIGN_RESTRICT)); lgk_mem->mem_lgd = (i4)((char *)LGK_base.lgk_lgd_ptr - (char *)LGK_base.lgk_mem_ptr); /* now initialize the rest of memory for allocation */ /* how much memory is left? */ ptr = ((char *)LGK_base.lgk_lgd_ptr + sizeof(LGD)); memleft = size - (((char *) ptr) - ((char *) LGK_base.lgk_mem_ptr)); if ( (ret_val = lgkm_initialize_mem(memleft, ptr)) == OK && (ret_val = LG_meminit(sys_err)) == OK && (ret_val = LK_meminit(sys_err)) == OK ) { /* Clear array of attached pids and pid info */ for ( i = 0; i < LGK_MAX_PIDS; i++ ) { lgk_mem->mem_pid[i] = (PID)0; lgk_mem->mem_info[i].info_txt[0] = EOS; } /* Set the creator pid */ LGK_base.lgk_pid_slot = 0; lgk_mem->mem_creator_pid = LGK_my_pid; /* Set the version, releasing other processes */ lgk_mem->mem_version_no = LGK_MEM_VERSION_CURRENT; } else { uleFormat(NULL, ret_val, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, NULL, 0, NULL, &err_code, 0); ret_val = E_DMA802_LGKINIT_ERROR; /* Destroy the shared memory */ LGK_destroy(allocated_pages, sys_err); } } if ( ret_val == OK ) { PCatexit(LGK_rundown); if ( clustered ) { /* ** Perform preliminary cluster connection and CX memory init. */ /* Get installation code */ NMgtAt("II_INSTALLATION", &nm_string); if ( nm_string ) { instid[0] = *(nm_string); instid[1] = *(nm_string+1); } else { instid[0] = 'A'; instid[1] = 'A'; } instid[2] = '\0'; ret_val = CXinitialize( instid, pcxmem, flag & LGK_IS_CSP ); if ( ret_val ) { /* Report error returned from CX */ uleFormat(NULL, ret_val, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, NULL, 0, NULL, &err_code, 0 ); break; } } #ifdef VMS { static $EXHDEF exit_block; i4 ctrl_y_mask = 0x02000000; /* ** On VMS, programs like the dmfjsp and logstat run as images in ** the shell process. That is, the system doesn't start and stop ** a process for each invocation of the program, it just starts ** and stops an image in the same process. This means that if ** the program should die, the image may be rundown but the process ** will remain, which means that the check-dead threads of other ** processes in the installation will not feel that they need to ** rundown this process, since it's still alive. ** ** By declaring an exit handler, which will get a chance to run ** even if PCexit isn't called, we improve our chances of getting ** to perform rundown processing if we should die unexpectedly. ** ** Furthermore, we ask DCL to disable its ^Y processing, which ** lessens the chance that the user will interrupt us while we ** are holding the semaphore. */ exit_block.exh$g_func = LGK_rundown; exit_block.exh$l_argcount = 1; exit_block.exh$gl_value = &exit_block.exh$l_status; if (sys$dclexh(&exit_block) != SS$_NORMAL) ret_val = FAIL; lib$disable_ctrl(&ctrl_y_mask, 0); } #endif } break; } if ( ret_val ) LGK_base.lgk_mem_ptr = NULL; return(ret_val); }
void IIodbc_timeOutThread() { QUEUE *q, *p, *pq; pENV penv; pDBC pdbc; RETCODE rc; SYSTIME expire_time; TM_STAMP stamp; char stampStr[TM_SIZE_STAMP]; ODBC_EXEC_TRACE(ODBC_EX_TRACE) ("IIodbc_timeOutThread() with timeout %d\n", IIodbc_cb.timeout); while(TRUE) { /* ** Search the pool every thirty seconds and check for expired ** connections. Force disconnect and free from the pool ** if the connection handle has passed the expiration time. ** Note that the CLI and driver connection handles are not ** freed. */ PCsleep(30000); TMget_stamp(&stamp); TMstamp_str(&stamp, stampStr); ODBC_EXEC_TRACE(ODBC_EX_TRACE) ("IIodbc_timeOutThread woke up at %s\n", stampStr); TMnow(&expire_time); if (IIodbc_cb.pooling == DRIVER_POOL) { applyLock(SQL_HANDLE_IIODBC_CB, NULL); for (q = IIodbc_cb.pool_q.q_prev; q!= &IIodbc_cb.pool_q; q = p) { ODBC_EXEC_TRACE(ODBC_EX_TRACE) ("IIodbc_timeOutThread: driver pool count is %d\n", IIodbc_cb.pool_count); p = q->q_prev; pdbc = (pDBC)((pPOOL)q)->pdbc; ODBC_EXEC_TRACE(ODBC_EX_TRACE) ("IIodbc_timeOutThread: found conn handle %p with time diff %d\n",pdbc, expire_time.TM_secs - pdbc->expire_time.TM_secs); if (expire_time.TM_secs - pdbc->expire_time.TM_secs > IIodbc_cb.timeout) { /* ** Note that the connection handle is not freed, only ** removed from the pool. */ ODBC_EXEC_TRACE(ODBC_EX_TRACE) ("IIodbc_timeOutThread: EXPIRED. pdbc time is %d vs %d\n", pdbc->expire_time.TM_secs, expire_time.TM_secs); rc = IIDisconnect(pdbc->hdr.driverHandle); QUremove(q); MEfree((PTR)q); IIodbc_cb.pool_count -= 1; ODBC_EXEC_TRACE(ODBC_EX_TRACE) ("new pool count is %d\n",IIodbc_cb.pool_count); applyLock(SQL_HANDLE_DBC, pdbc); pdbc->hdr.state = C2; releaseLock(SQL_HANDLE_DBC, pdbc); } } releaseLock(SQL_HANDLE_IIODBC_CB, NULL); } else { for (q = IIodbc_cb.env_q.q_prev; q!= &IIodbc_cb.env_q; q = q->q_prev) { penv = (pENV)q; TRdisplay("Found env handle %p\n",penv); applyLock(SQL_HANDLE_ENV, penv); for (pq = penv->pool_q.q_prev; pq != &penv->pool_q; pq = p) { ODBC_EXEC_TRACE(ODBC_EX_TRACE) ("Env pool count is %d\n",penv->pool_count); p = q->q_prev; pdbc = (pDBC)((pPOOL)pq)->pdbc; ODBC_EXEC_TRACE(ODBC_EX_TRACE) ("IIodbc_TimeOutThread: Found conn handle %p with time diff %d\n",pdbc, expire_time.TM_secs - pdbc->expire_time.TM_secs); if (expire_time.TM_secs - pdbc->expire_time.TM_secs > 1) { ODBC_EXEC_TRACE(ODBC_EX_TRACE) ("IIodbc_timeOutThread: EXPIRED. pdbc time is %d vs %d\n", pdbc->expire_time.TM_secs, expire_time.TM_secs); rc = IIDisconnect(pdbc->hdr.driverHandle); QUremove(q); MEfree((PTR)q); penv->pool_count -= 1; ODBC_EXEC_TRACE(ODBC_EX_TRACE) ("IIodbc_timeOutThread: new pool count is %d\n", penv->pool_count); } } releaseLock(SQL_HANDLE_ENV, penv); } /* for (q = IIodbc_cb.env_q.q_prev; q!= &IIodbc_cb.env_q; q = q->q_prev) */ } /* if (IIodbc_cb.pooling == DRIVER_POOL) */ } /* while (TRUE) */ }
/* IIReadfile --- reads data from specified channel ** Inputs: ** asipc ASSOC_IPC for connection. ** hChan Handle to channel from which to read. ** nNumberToRead number of bytes to read. ** lpNUmberRead number of bytes actually read. ** LPOVERLAPPED for compatiblity with win32 ReadFile. ** timeout timeout. ** ** IIReadfile attempts to read from the pipe. It returns OK if the read succeeds ** ~OK if it fails, or GC_TIME_OUT if it times out. ** ** IIReadfile and IIWritefile must maintain a count of how many bytes are in ** the channel in order to implement the timeout functionality because anonymous ** pipes don't provide a wait function like named pipes. Database events ** require the timeout functionality. ** ** History: ** 04-Sep-97 (fanra01) ** Add return of OS error if read 0 bytes. ** 10-Dec-98 (lunbr01) Bug 94183 ** Use PeekNamedPipe rather than shared memory counter (*ct) to check ** if data is avail to read (for RECEIVES with a non-INFINITE ** timeout parm only). The shared memory counter was occasionally ** getting corrupted because IIReadFile and IIWriteFile would update ** it at the same time; InterlockedExchange was not sufficient... ** a mutex was needed before the current value of *ct is "gotten" ** for the increment/decrement. The result was that IIReadFile ** would "think" that there was something to read but there wasn't; ** he would then issue the blocked read and wait forever or until ** a dbevent happened to come in. The symptom was an application hang. ** Rather than adding mutexes, PeekNamedPipe was used and the ** shared memory counters eliminated. */ ULONG IIReadFile( ASSOC_IPC *asipc, HANDLE hChan, LPVOID lpBuffer, DWORD nNumberToRead, LPDWORD lpNumberRead, LPOVERLAPPED lpOverlapped, DWORD timeout ) { DWORD ct = 0; int i = 0; int intervals = 0; BOOL rval = FALSE; BOOL tout = TRUE; DWORD dwError = 0; timeout = timeout == GC_WAIT_FOREVER ? INFINITE : timeout; *lpNumberRead = 0; if ( timeout != INFINITE ) { rval = PeekNamedPipe(hChan, NULL, 0, NULL, &ct, NULL); if ( rval != TRUE ) { dwError = GetLastError(); return rval ? OK : dwError; } if ( ct == 0 ) { if ( timeout == 0 ) return GC_TIME_OUT; intervals = timeout == INFINITE ? 1:timeout/IPC_TIMEOUT_SLEEP ; for( i = 0; i < intervals ; i++ ) { PCsleep ( IPC_TIMEOUT_SLEEP ); rval = PeekNamedPipe(hChan, NULL, 0, NULL, &ct, NULL); if ( rval != TRUE ) { dwError = GetLastError(); return rval ? OK : dwError; } if ( ct != 0 ) { tout = FALSE; break; } } if ( tout ) return GC_TIME_OUT; } /* end if ct (bytes in read pipe) == 0 */ } rval = ReadFile( hChan, lpBuffer, nNumberToRead, lpNumberRead, lpOverlapped ); dwError = GetLastError(); if (rval) { /* did successful data retrieval */ if (asipc) { /* set bPipesClosed to false - may have been set to TRUE ** by the 'Server'. */ if (asipc->IpcInternal.AssocInfo) asipc->IpcInternal.AssocInfo->bPipesClosed = FALSE; } } /* did successful data retrieval */ if( rval && (!asipc || !asipc->IpcInternal.AssocInfo || asipc->IpcInternal.AssocInfo->bPipesClosed) ) { return ERROR_BROKEN_PIPE; } return rval ? OK : dwError; }