/* * PerformAuthentication -- authenticate a remote client * * returns: nothing. Will not return at all if there's any failure. */ static void PerformAuthentication(Port *port) { /* This should be set already, but let's make sure */ ClientAuthInProgress = true; /* limit visibility of log messages */ /* * In EXEC_BACKEND case, we didn't inherit the contents of pg_hba.conf * etcetera from the postmaster, and have to load them ourselves. Note we * are loading them into the startup transaction's memory context, not * PostmasterContext, but that shouldn't matter. * * FIXME: [fork/exec] Ugh. Is there a way around this overhead? */ #ifdef EXEC_BACKEND if (!load_hba()) { /* * It makes no sense to continue if we fail to load the HBA file, * since there is no way to connect to the database in this case. */ ereport(FATAL, (errmsg("could not load pg_hba.conf"))); } load_ident(); #endif /* * Set up a timeout in case a buggy or malicious client fails to respond * during authentication. Since we're inside a transaction and might do * database access, we have to use the statement_timeout infrastructure. */ if (!enable_sig_alarm(AuthenticationTimeout * 1000, true)) elog(FATAL, "could not set timer for authorization timeout"); /* * Now perform authentication exchange. */ ClientAuthentication(port); /* might not return, if failure */ /* * Done with authentication. Disable the timeout, and log if needed. */ if (!disable_sig_alarm(true)) elog(FATAL, "could not disable timer for authorization timeout"); if (Log_connections) ereport(LOG, (errmsg("connection authorized: user=%s database=%s", port->user_name, port->database_name))); set_ps_display("startup", false); ClientAuthInProgress = false; /* client_min_messages is active now */ }
/* * Cancel any pending wait for lock, when aborting a transaction. * * Returns true if we had been waiting for a lock, else false. * * (Normally, this would only happen if we accept a cancel/die * interrupt while waiting; but an ereport(ERROR) while waiting is * within the realm of possibility, too.) */ bool LockWaitCancel(void) { LWLockId partitionLock; /* Nothing to do if we weren't waiting for a lock */ if (lockAwaited == NULL) return false; /* Turn off the deadlock timer, if it's still running (see ProcSleep) */ disable_sig_alarm(false); /* Unlink myself from the wait queue, if on it (might not be anymore!) */ partitionLock = LockHashPartitionLock(lockAwaited->hashcode); LWLockAcquire(partitionLock, LW_EXCLUSIVE); if (MyProc->links.next != INVALID_OFFSET) { /* We could not have been granted the lock yet */ RemoveFromWaitQueue(MyProc, lockAwaited->hashcode); } else { /* * Somebody kicked us off the lock queue already. Perhaps they * granted us the lock, or perhaps they detected a deadlock. If they * did grant us the lock, we'd better remember it in our local lock * table. */ if (MyProc->waitStatus == STATUS_OK) GrantAwaitedLock(); } lockAwaited = NULL; LWLockRelease(partitionLock); /* * We used to do PGSemaphoreReset() here to ensure that our proc's wait * semaphore is reset to zero. This prevented a leftover wakeup signal * from remaining in the semaphore if someone else had granted us the lock * we wanted before we were able to remove ourselves from the wait-list. * However, now that ProcSleep loops until waitStatus changes, a leftover * wakeup signal isn't harmful, and it seems not worth expending cycles to * get rid of a signal that most likely isn't there. */ /* * Return true even if we were kicked off the lock before we were able to * remove ourselves. */ return true; }
/* * proc_exit callback to tear down the JVM */ static void _destroyJavaVM(int status, Datum dummy) { if(s_javaVM != 0) { Invocation ctx; #ifdef USE_PLJAVA_SIGHANDLERS #if PG_VERSION_NUM >= 90300 TimeoutId tid; #else pqsigfunc saveSigAlrm; #endif Invocation_pushInvocation(&ctx, false); if(sigsetjmp(recoverBuf, 1) != 0) { elog(DEBUG2, "needed to forcibly shut down the Java virtual machine"); s_javaVM = 0; return; } #if PG_VERSION_NUM >= 90300 InitializeTimeouts(); /* establishes SIGALRM handler */ tid = RegisterTimeout(USER_TIMEOUT, terminationTimeoutHandler); #else saveSigAlrm = pqsignal(SIGALRM, terminationTimeoutHandler); enable_sig_alarm(5000, false); #endif elog(DEBUG2, "shutting down the Java virtual machine"); JNI_destroyVM(s_javaVM); #if PG_VERSION_NUM >= 90300 disable_timeout(tid, false); #else disable_sig_alarm(false); pqsignal(SIGALRM, saveSigAlrm); #endif #else Invocation_pushInvocation(&ctx, false); elog(DEBUG2, "shutting down the Java virtual machine"); JNI_destroyVM(s_javaVM); #endif elog(DEBUG2, "done shutting down the Java virtual machine"); s_javaVM = 0; currentInvocation = 0; } }
/* * We get here when a session has been idle for a while (waiting for the * client to send us SQL to execute). The idea is to consume less resources while sitting idle, * so we can support more sessions being logged on. * * The expectation is that if the session is logged on, but nobody is sending us work to do, * we want to free up whatever resources we can. Usually it means there is a human being at the * other end of the connection, and that person has walked away from their terminal, or just hasn't * decided what to do next. We could be idle for a very long time (many hours). * * Of course, freeing gangs means that the next time the user does send in an SQL statement, * we need to allocate gangs (at least the writer gang) to do anything. This entails extra work, * so we don't want to do this if we don't think the session has gone idle. * * We can call cleanupIdleReaderGangs() to just free some resources, or cleanupAllIdleGangs() to * free up everything possible on the segDB side. At the moment, I can't find a reason to * use cleanupIdleReaderGangs(), but it we wanted to, we would have two timeouts: The first * when we free the reader gangs, and the second later time free the writer gang. * My current thinking is that it is better to free all the gangs as soon as we decide * the session is idle. * * P.s: Is there anything we can free up on the master (QD) side? I can't think of anything. * */ static void HandleClientWaitTimeout(void) { elog(DEBUG2,"HandleClientWaitTimeout"); /* * cancel the timer, as there is no reason we need it to go off again. */ disable_sig_alarm(false); /* * Free gangs to free up resources on the segDBs. */ if (gangsExist()) { cleanupAllIdleGangs(); } }
/* * ProcSleep -- put a process to sleep on the specified lock * * Caller must have set MyProc->heldLocks to reflect locks already held * on the lockable object by this process (under all XIDs). * * The lock table's partition lock must be held at entry, and will be held * at exit. * * Result: STATUS_OK if we acquired the lock, STATUS_ERROR if not (deadlock). * * ASSUME: that no one will fiddle with the queue until after * we release the partition lock. * * NOTES: The process queue is now a priority queue for locking. * * P() on the semaphore should put us to sleep. The process * semaphore is normally zero, so when we try to acquire it, we sleep. */ int ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable) { LOCKMODE lockmode = locallock->tag.mode; LOCK *lock = locallock->lock; PROCLOCK *proclock = locallock->proclock; uint32 hashcode = locallock->hashcode; LWLockId partitionLock = LockHashPartitionLock(hashcode); PROC_QUEUE *waitQueue = &(lock->waitProcs); LOCKMASK myHeldLocks = MyProc->heldLocks; bool early_deadlock = false; PGPROC *proc; int i; /* * Determine where to add myself in the wait queue. * * Normally I should go at the end of the queue. However, if I already * hold locks that conflict with the request of any previous waiter, put * myself in the queue just in front of the first such waiter. This is not * a necessary step, since deadlock detection would move me to before that * waiter anyway; but it's relatively cheap to detect such a conflict * immediately, and avoid delaying till deadlock timeout. * * Special case: if I find I should go in front of some waiter, check to * see if I conflict with already-held locks or the requests before that * waiter. If not, then just grant myself the requested lock immediately. * This is the same as the test for immediate grant in LockAcquire, except * we are only considering the part of the wait queue before my insertion * point. */ if (myHeldLocks != 0) { LOCKMASK aheadRequests = 0; proc = (PGPROC *) MAKE_PTR(waitQueue->links.next); for (i = 0; i < waitQueue->size; i++) { /* Must he wait for me? */ if (lockMethodTable->conflictTab[proc->waitLockMode] & myHeldLocks) { /* Must I wait for him ? */ if (lockMethodTable->conflictTab[lockmode] & proc->heldLocks) { /* * Yes, so we have a deadlock. Easiest way to clean up * correctly is to call RemoveFromWaitQueue(), but we * can't do that until we are *on* the wait queue. So, set * a flag to check below, and break out of loop. Also, * record deadlock info for later message. */ RememberSimpleDeadLock(MyProc, lockmode, lock, proc); early_deadlock = true; break; } /* I must go before this waiter. Check special case. */ if ((lockMethodTable->conflictTab[lockmode] & aheadRequests) == 0 && LockCheckConflicts(lockMethodTable, lockmode, lock, proclock, MyProc) == STATUS_OK) { /* Skip the wait and just grant myself the lock. */ GrantLock(lock, proclock, lockmode); GrantAwaitedLock(); return STATUS_OK; } /* Break out of loop to put myself before him */ break; } /* Nope, so advance to next waiter */ aheadRequests |= LOCKBIT_ON(proc->waitLockMode); proc = (PGPROC *) MAKE_PTR(proc->links.next); } /* * If we fall out of loop normally, proc points to waitQueue head, so * we will insert at tail of queue as desired. */ } else { /* I hold no locks, so I can't push in front of anyone. */ proc = (PGPROC *) &(waitQueue->links); } /* * Insert self into queue, ahead of the given proc (or at tail of queue). */ SHMQueueInsertBefore(&(proc->links), &(MyProc->links)); waitQueue->size++; lock->waitMask |= LOCKBIT_ON(lockmode); /* Set up wait information in PGPROC object, too */ MyProc->waitLock = lock; MyProc->waitProcLock = proclock; MyProc->waitLockMode = lockmode; MyProc->waitStatus = STATUS_WAITING; /* * If we detected deadlock, give up without waiting. This must agree with * CheckDeadLock's recovery code, except that we shouldn't release the * semaphore since we haven't tried to lock it yet. */ if (early_deadlock) { RemoveFromWaitQueue(MyProc, hashcode); return STATUS_ERROR; } /* mark that we are waiting for a lock */ lockAwaited = locallock; /* * Release the lock table's partition lock. * * NOTE: this may also cause us to exit critical-section state, possibly * allowing a cancel/die interrupt to be accepted. This is OK because we * have recorded the fact that we are waiting for a lock, and so * LockWaitCancel will clean up if cancel/die happens. */ LWLockRelease(partitionLock); /* * Set timer so we can wake up after awhile and check for a deadlock. If a * deadlock is detected, the handler releases the process's semaphore and * sets MyProc->waitStatus = STATUS_ERROR, allowing us to know that we * must report failure rather than success. * * By delaying the check until we've waited for a bit, we can avoid * running the rather expensive deadlock-check code in most cases. */ if (!enable_sig_alarm(DeadlockTimeout, false)) elog(FATAL, "could not set timer for process wakeup"); /* * If someone wakes us between LWLockRelease and PGSemaphoreLock, * PGSemaphoreLock will not block. The wakeup is "saved" by the semaphore * implementation. While this is normally good, there are cases where a * saved wakeup might be leftover from a previous operation (for example, * we aborted ProcWaitForSignal just before someone did ProcSendSignal). * So, loop to wait again if the waitStatus shows we haven't been granted * nor denied the lock yet. * * We pass interruptOK = true, which eliminates a window in which * cancel/die interrupts would be held off undesirably. This is a promise * that we don't mind losing control to a cancel/die interrupt here. We * don't, because we have no shared-state-change work to do after being * granted the lock (the grantor did it all). We do have to worry about * updating the locallock table, but if we lose control to an error, * LockWaitCancel will fix that up. */ do { PGSemaphoreLock(&MyProc->sem, true); } while (MyProc->waitStatus == STATUS_WAITING); /* * Disable the timer, if it's still running */ if (!disable_sig_alarm(false)) elog(FATAL, "could not disable timer for process wakeup"); /* * Re-acquire the lock table's partition lock. We have to do this to hold * off cancel/die interrupts before we can mess with lockAwaited (else we * might have a missed or duplicated locallock update). */ LWLockAcquire(partitionLock, LW_EXCLUSIVE); /* * We no longer want LockWaitCancel to do anything. */ lockAwaited = NULL; /* * If we got the lock, be sure to remember it in the locallock table. */ if (MyProc->waitStatus == STATUS_OK) GrantAwaitedLock(); /* * We don't have to do anything else, because the awaker did all the * necessary update of the lock table and MyProc. */ return MyProc->waitStatus; }