michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=8 et : michael@0: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "base/process_util.h" michael@0: michael@0: #include "mozilla/ipc/MessageChannel.h" michael@0: #include "mozilla/ipc/ProtocolUtils.h" michael@0: #include "mozilla/ipc/Transport.h" michael@0: michael@0: using namespace base; michael@0: using namespace IPC; michael@0: michael@0: namespace mozilla { michael@0: namespace ipc { michael@0: michael@0: IToplevelProtocol::~IToplevelProtocol() michael@0: { michael@0: mOpenActors.clear(); michael@0: } michael@0: michael@0: void IToplevelProtocol::AddOpenedActor(IToplevelProtocol* aActor) michael@0: { michael@0: #ifdef DEBUG michael@0: for (const IToplevelProtocol* actor = mOpenActors.getFirst(); michael@0: actor; michael@0: actor = actor->getNext()) { michael@0: NS_ASSERTION(actor != aActor, michael@0: "Open the same protocol for more than one time"); michael@0: } michael@0: #endif michael@0: michael@0: mOpenActors.insertBack(aActor); michael@0: } michael@0: michael@0: IToplevelProtocol* michael@0: IToplevelProtocol::CloneToplevel(const InfallibleTArray& aFds, michael@0: base::ProcessHandle aPeerProcess, michael@0: ProtocolCloneContext* aCtx) michael@0: { michael@0: NS_NOTREACHED("Clone() for this protocol actor is not implemented"); michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: IToplevelProtocol::CloneOpenedToplevels(IToplevelProtocol* aTemplate, michael@0: const InfallibleTArray& aFds, michael@0: base::ProcessHandle aPeerProcess, michael@0: ProtocolCloneContext* aCtx) michael@0: { michael@0: for (IToplevelProtocol* actor = aTemplate->GetFirstOpenedActors(); michael@0: actor; michael@0: actor = actor->getNext()) { michael@0: IToplevelProtocol* newactor = actor->CloneToplevel(aFds, aPeerProcess, aCtx); michael@0: AddOpenedActor(newactor); michael@0: } michael@0: } michael@0: michael@0: class ChannelOpened : public IPC::Message michael@0: { michael@0: public: michael@0: ChannelOpened(TransportDescriptor aDescriptor, michael@0: ProcessId aOtherProcess, michael@0: ProtocolId aProtocol) michael@0: : IPC::Message(MSG_ROUTING_CONTROL, // these only go to top-level actors michael@0: CHANNEL_OPENED_MESSAGE_TYPE, michael@0: PRIORITY_NORMAL) michael@0: { michael@0: IPC::WriteParam(this, aDescriptor); michael@0: IPC::WriteParam(this, aOtherProcess); michael@0: IPC::WriteParam(this, static_cast(aProtocol)); michael@0: } michael@0: michael@0: static bool Read(const IPC::Message& aMsg, michael@0: TransportDescriptor* aDescriptor, michael@0: ProcessId* aOtherProcess, michael@0: ProtocolId* aProtocol) michael@0: { michael@0: void* iter = nullptr; michael@0: if (!IPC::ReadParam(&aMsg, &iter, aDescriptor) || michael@0: !IPC::ReadParam(&aMsg, &iter, aOtherProcess) || michael@0: !IPC::ReadParam(&aMsg, &iter, reinterpret_cast(aProtocol))) { michael@0: return false; michael@0: } michael@0: aMsg.EndRead(iter); michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: bool michael@0: Bridge(const PrivateIPDLInterface&, michael@0: MessageChannel* aParentChannel, ProcessHandle aParentProcess, michael@0: MessageChannel* aChildChannel, ProcessHandle aChildProcess, michael@0: ProtocolId aProtocol, ProtocolId aChildProtocol) michael@0: { michael@0: ProcessId parentId = GetProcId(aParentProcess); michael@0: ProcessId childId = GetProcId(aChildProcess); michael@0: if (!parentId || !childId) { michael@0: return false; michael@0: } michael@0: michael@0: TransportDescriptor parentSide, childSide; michael@0: if (!CreateTransport(aParentProcess, aChildProcess, michael@0: &parentSide, &childSide)) { michael@0: return false; michael@0: } michael@0: michael@0: if (!aParentChannel->Send(new ChannelOpened(parentSide, michael@0: childId, michael@0: aProtocol)) || michael@0: !aChildChannel->Send(new ChannelOpened(childSide, michael@0: parentId, michael@0: aChildProtocol))) { michael@0: CloseDescriptor(parentSide); michael@0: CloseDescriptor(childSide); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: Open(const PrivateIPDLInterface&, michael@0: MessageChannel* aOpenerChannel, ProcessHandle aOtherProcess, michael@0: Transport::Mode aOpenerMode, michael@0: ProtocolId aProtocol, ProtocolId aChildProtocol) michael@0: { michael@0: bool isParent = (Transport::MODE_SERVER == aOpenerMode); michael@0: ProcessHandle thisHandle = GetCurrentProcessHandle(); michael@0: ProcessHandle parentHandle = isParent ? thisHandle : aOtherProcess; michael@0: ProcessHandle childHandle = !isParent ? thisHandle : aOtherProcess; michael@0: ProcessId parentId = GetProcId(parentHandle); michael@0: ProcessId childId = GetProcId(childHandle); michael@0: if (!parentId || !childId) { michael@0: return false; michael@0: } michael@0: michael@0: TransportDescriptor parentSide, childSide; michael@0: if (!CreateTransport(parentHandle, childHandle, michael@0: &parentSide, &childSide)) { michael@0: return false; michael@0: } michael@0: michael@0: Message* parentMsg = new ChannelOpened(parentSide, childId, aProtocol); michael@0: Message* childMsg = new ChannelOpened(childSide, parentId, aChildProtocol); michael@0: nsAutoPtr messageForUs(isParent ? parentMsg : childMsg); michael@0: nsAutoPtr messageForOtherSide(!isParent ? parentMsg : childMsg); michael@0: if (!aOpenerChannel->Echo(messageForUs.forget()) || michael@0: !aOpenerChannel->Send(messageForOtherSide.forget())) { michael@0: CloseDescriptor(parentSide); michael@0: CloseDescriptor(childSide); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: UnpackChannelOpened(const PrivateIPDLInterface&, michael@0: const Message& aMsg, michael@0: TransportDescriptor* aTransport, michael@0: ProcessId* aOtherProcess, michael@0: ProtocolId* aProtocol) michael@0: { michael@0: return ChannelOpened::Read(aMsg, aTransport, aOtherProcess, aProtocol); michael@0: } michael@0: michael@0: void michael@0: ProtocolErrorBreakpoint(const char* aMsg) michael@0: { michael@0: // Bugs that generate these error messages can be tough to michael@0: // reproduce. Log always in the hope that someone finds the error michael@0: // message. michael@0: printf_stderr("IPDL protocol error: %s\n", aMsg); michael@0: } michael@0: michael@0: void michael@0: FatalError(const char* aProtocolName, const char* aMsg, michael@0: ProcessHandle aHandle, bool aIsParent) michael@0: { michael@0: ProtocolErrorBreakpoint(aMsg); michael@0: michael@0: nsAutoCString formattedMessage("IPDL error ["); michael@0: formattedMessage.AppendASCII(aProtocolName); michael@0: formattedMessage.AppendLiteral("]: \""); michael@0: formattedMessage.AppendASCII(aMsg); michael@0: if (aIsParent) { michael@0: formattedMessage.AppendLiteral("\". Killing child side as a result."); michael@0: NS_ERROR(formattedMessage.get()); michael@0: michael@0: if (aHandle != kInvalidProcessHandle && michael@0: !base::KillProcess(aHandle, base::PROCESS_END_KILLED_BY_USER, false)) { michael@0: NS_ERROR("May have failed to kill child!"); michael@0: } michael@0: } else { michael@0: formattedMessage.AppendLiteral("\". abort()ing as a result."); michael@0: NS_RUNTIMEABORT(formattedMessage.get()); michael@0: } michael@0: } michael@0: michael@0: } // namespace ipc michael@0: } // namespace mozilla