1 /*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2012-2016 Symless Ltd.
4 * Copyright (C) 2012 Nick Bolton
5 *
6 * This package is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * found in the file LICENSE that should have accompanied this file.
9 *
10 * This package is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "ipc/IpcClientProxy.h"
20
21 #include "ipc/Ipc.h"
22 #include "ipc/IpcMessage.h"
23 #include "synergy/ProtocolUtil.h"
24 #include "io/IStream.h"
25 #include "arch/Arch.h"
26 #include "base/TMethodEventJob.h"
27 #include "base/Log.h"
28
29 //
30 // IpcClientProxy
31 //
32
IpcClientProxy(synergy::IStream & stream,IEventQueue * events)33 IpcClientProxy::IpcClientProxy(synergy::IStream& stream, IEventQueue* events) :
34 m_stream(stream),
35 m_clientType(kIpcClientUnknown),
36 m_disconnecting(false),
37 m_readMutex(ARCH->newMutex()),
38 m_writeMutex(ARCH->newMutex()),
39 m_events(events)
40 {
41 m_events->adoptHandler(
42 m_events->forIStream().inputReady(), stream.getEventTarget(),
43 new TMethodEventJob<IpcClientProxy>(
44 this, &IpcClientProxy::handleData));
45
46 m_events->adoptHandler(
47 m_events->forIStream().outputError(), stream.getEventTarget(),
48 new TMethodEventJob<IpcClientProxy>(
49 this, &IpcClientProxy::handleWriteError));
50
51 m_events->adoptHandler(
52 m_events->forIStream().inputShutdown(), stream.getEventTarget(),
53 new TMethodEventJob<IpcClientProxy>(
54 this, &IpcClientProxy::handleDisconnect));
55
56 m_events->adoptHandler(
57 m_events->forIStream().outputShutdown(), stream.getEventTarget(),
58 new TMethodEventJob<IpcClientProxy>(
59 this, &IpcClientProxy::handleWriteError));
60 }
61
~IpcClientProxy()62 IpcClientProxy::~IpcClientProxy()
63 {
64 m_events->removeHandler(
65 m_events->forIStream().inputReady(), m_stream.getEventTarget());
66 m_events->removeHandler(
67 m_events->forIStream().outputError(), m_stream.getEventTarget());
68 m_events->removeHandler(
69 m_events->forIStream().inputShutdown(), m_stream.getEventTarget());
70 m_events->removeHandler(
71 m_events->forIStream().outputShutdown(), m_stream.getEventTarget());
72
73 // don't delete the stream while it's being used.
74 ARCH->lockMutex(m_readMutex);
75 ARCH->lockMutex(m_writeMutex);
76 delete &m_stream;
77 ARCH->unlockMutex(m_readMutex);
78 ARCH->unlockMutex(m_writeMutex);
79
80 ARCH->closeMutex(m_readMutex);
81 ARCH->closeMutex(m_writeMutex);
82 }
83
84 void
handleDisconnect(const Event &,void *)85 IpcClientProxy::handleDisconnect(const Event&, void*)
86 {
87 disconnect();
88 LOG((CLOG_DEBUG "ipc client disconnected"));
89 }
90
91 void
handleWriteError(const Event &,void *)92 IpcClientProxy::handleWriteError(const Event&, void*)
93 {
94 disconnect();
95 LOG((CLOG_DEBUG "ipc client write error"));
96 }
97
98 void
handleData(const Event &,void *)99 IpcClientProxy::handleData(const Event&, void*)
100 {
101 // don't allow the dtor to destroy the stream while we're using it.
102 ArchMutexLock lock(m_readMutex);
103
104 LOG((CLOG_DEBUG "start ipc handle data"));
105
106 UInt8 code[4];
107 UInt32 n = m_stream.read(code, 4);
108 while (n != 0) {
109
110 LOG((CLOG_DEBUG "ipc read: %c%c%c%c",
111 code[0], code[1], code[2], code[3]));
112
113 IpcMessage* m = nullptr;
114 if (memcmp(code, kIpcMsgHello, 4) == 0) {
115 m = parseHello();
116 }
117 else if (memcmp(code, kIpcMsgCommand, 4) == 0) {
118 m = parseCommand();
119 }
120 else {
121 LOG((CLOG_ERR "invalid ipc message"));
122 disconnect();
123 }
124
125 // don't delete with this event; the data is passed to a new event.
126 Event e(m_events->forIpcClientProxy().messageReceived(), this, NULL, Event::kDontFreeData);
127 e.setDataObject(m);
128 m_events->addEvent(e);
129
130 n = m_stream.read(code, 4);
131 }
132
133 LOG((CLOG_DEBUG "finished ipc handle data"));
134 }
135
136 void
send(const IpcMessage & message)137 IpcClientProxy::send(const IpcMessage& message)
138 {
139 // don't allow other threads to write until we've finished the entire
140 // message. stream write is locked, but only for that single write.
141 // also, don't allow the dtor to destroy the stream while we're using it.
142 ArchMutexLock lock(m_writeMutex);
143
144 LOG((CLOG_DEBUG4 "ipc write: %d", message.type()));
145
146 switch (message.type()) {
147 case kIpcLogLine: {
148 const IpcLogLineMessage& llm = static_cast<const IpcLogLineMessage&>(message);
149 const String logLine = llm.logLine();
150 ProtocolUtil::writef(&m_stream, kIpcMsgLogLine, &logLine);
151 break;
152 }
153
154 case kIpcShutdown:
155 ProtocolUtil::writef(&m_stream, kIpcMsgShutdown);
156 break;
157
158 default:
159 LOG((CLOG_ERR "ipc message not supported: %d", message.type()));
160 break;
161 }
162 }
163
164 IpcHelloMessage*
parseHello()165 IpcClientProxy::parseHello()
166 {
167 UInt8 type;
168 ProtocolUtil::readf(&m_stream, kIpcMsgHello + 4, &type);
169
170 m_clientType = static_cast<EIpcClientType>(type);
171
172 // must be deleted by event handler.
173 return new IpcHelloMessage(m_clientType);
174 }
175
176 IpcCommandMessage*
parseCommand()177 IpcClientProxy::parseCommand()
178 {
179 String command;
180 UInt8 elevate;
181 ProtocolUtil::readf(&m_stream, kIpcMsgCommand + 4, &command, &elevate);
182
183 // must be deleted by event handler.
184 return new IpcCommandMessage(command, elevate != 0);
185 }
186
187 void
disconnect()188 IpcClientProxy::disconnect()
189 {
190 LOG((CLOG_DEBUG "ipc disconnect, closing stream"));
191 m_disconnecting = true;
192 m_stream.close();
193 m_events->addEvent(Event(m_events->forIpcClientProxy().disconnected(), this));
194 }
195