/* * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ // // connection - manage connections to clients. // // Note that Connection objects correspond to client process threads, and are // thus inherently single-threaded. It is physically impossible for multiple // requests to come in for the same Connection, unless the client side is // illegally messing with the IPC protocol (for which we check below). // It is still necessary to take the object lock for a Connection because there // are times when we want to manipulate a busy Connection from another securityd // thread (say, in response to a DPN). // #include "connection.h" #include "key.h" #include "server.h" #include "session.h" #include #include #include #include #include #include // // Construct a Connection object. // Connection::Connection(Process &proc, Port rPort) : mClientPort(rPort), state(idle), agentWait(NULL), aclUpdateTrigger(NULL) { parent(proc); // bump the send-rights count on the reply port so we keep the right after replying mClientPort.modRefs(MACH_PORT_RIGHT_SEND, +1); secdebug("SS", "New connection %p for process %d clientport=%d", this, process().pid(), int(rPort)); } // // When a Connection's destructor executes, the connection must already have been // terminated. All we have to do here is clean up a bit. // Connection::~Connection() { secdebug("SS", "Connection %p destroyed", this); assert(!agentWait); } // // Terminate a Connection normally. // This is assumed to be properly sequenced, so no thread races are possible. // void Connection::terminate() { // cleanly discard port rights assert(state == idle); mClientPort.modRefs(MACH_PORT_RIGHT_SEND, -1); // discard surplus send right assert(mClientPort.getRefs(MACH_PORT_RIGHT_SEND) == 1); // one left for final reply secdebug("SS", "Connection %p terminated", this); } // // Abort a Connection. // This may be called from thread A while thread B is working a request for the Connection, // so we must be careful. // void Connection::abort(bool keepReplyPort) { StLock _(*this); if (!keepReplyPort) mClientPort.destroy(); // dead as a doornail already switch (state) { case idle: secdebug("SS", "Connection %p aborted", this); break; case busy: state = dying; // shoot me soon, please if (agentWait) agentWait->destroy(); secdebug("SS", "Connection %p abort deferred (busy)", this); break; default: assert(false); // impossible (we hope) break; } } // // Service request framing. // These are here so "hanging" connection service threads don't fall // into the Big Bad Void as Connections and processes drop out from // under them. // void Connection::beginWork() { switch (state) { case idle: state = busy; break; case busy: secdebug("SS", "Attempt to re-enter connection %p(port %d)", this, mClientPort.port()); CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ some state-error code instead? default: assert(false); } } void Connection::checkWork() { StLock _(*this); switch (state) { case busy: return; case dying: agentWait = NULL; // obviously we're not waiting on this throw this; default: assert(false); } } void Connection::endWork() { switch (state) { case busy: // process the n-step aclUpdateTrigger if (aclUpdateTrigger) { if (--aclUpdateTriggerCount == 0) { aclUpdateTrigger = NULL; secdebug("kcacl", "acl update trigger expires"); } else secdebug("kcacl", "acl update trigger armed for %d calls", aclUpdateTriggerCount); } // end involvement state = idle; return; case dying: secdebug("SS", "Connection %p abort resuming", this); return; default: assert(false); return; // placebo } }