1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/sckipc.cpp
3 // Purpose: Interprocess communication implementation (wxSocket version)
4 // Author: Julian Smart
5 // Modified by: Guilhem Lavaux (big rewrite) May 1997, 1998
6 // Guillermo Rodriguez (updated for wxSocket v2) Jan 2000
7 // (callbacks deprecated) Mar 2000
8 // Vadim Zeitlin (added support for Unix sockets) Apr 2002
9 // (use buffering, many fixes/cleanup) Oct 2008
12 // Copyright: (c) Julian Smart 1993
13 // (c) Guilhem Lavaux 1997, 1998
14 // (c) 2000 Guillermo Rodriguez <guille@iies.es>
15 // Licence: wxWindows licence
16 /////////////////////////////////////////////////////////////////////////////
18 // ==========================================================================
20 // ==========================================================================
22 // --------------------------------------------------------------------------
24 // --------------------------------------------------------------------------
26 // For compilers that support precompilation, includes "wx.h".
27 #include "wx/wxprec.h"
33 #if wxUSE_SOCKETS && wxUSE_IPC && wxUSE_STREAMS
35 #include "wx/sckipc.h"
40 #include "wx/module.h"
47 #include "wx/socket.h"
49 // --------------------------------------------------------------------------
50 // macros and constants
51 // --------------------------------------------------------------------------
56 // Message codes (don't change them to avoid breaking the existing code using
64 IPC_ADVISE_REQUEST
= 5,
67 IPC_REQUEST_REPLY
= 8,
74 } // anonymous namespace
76 // headers needed for umask()
78 #include <sys/types.h>
80 #endif // __UNIX_LIKE__
82 // ----------------------------------------------------------------------------
84 // ----------------------------------------------------------------------------
86 // get the address object for the given server name, the caller must delete it
87 static wxSockAddress
*
88 GetAddressFromName(const wxString
& serverName
,
89 const wxString
& host
= wxEmptyString
)
91 // we always use INET sockets under non-Unix systems
92 #if defined(__UNIX__) && !defined(__WINDOWS__) && !defined(__WINE__)
93 // under Unix, if the server name looks like a path, create a AF_UNIX
94 // socket instead of AF_INET one
95 if ( serverName
.Find(_T('/')) != wxNOT_FOUND
)
97 wxUNIXaddress
*addr
= new wxUNIXaddress
;
98 addr
->Filename(serverName
);
104 wxIPV4address
*addr
= new wxIPV4address
;
105 addr
->Service(serverName
);
108 addr
->Hostname(host
);
115 // --------------------------------------------------------------------------
116 // wxTCPEventHandler stuff (private class)
117 // --------------------------------------------------------------------------
119 class wxTCPEventHandler
: public wxEvtHandler
122 wxTCPEventHandler() : wxEvtHandler() {}
124 void Client_OnRequest(wxSocketEvent
& event
);
125 void Server_OnRequest(wxSocketEvent
& event
);
127 DECLARE_EVENT_TABLE()
128 DECLARE_NO_COPY_CLASS(wxTCPEventHandler
)
133 _CLIENT_ONREQUEST_ID
= 1000,
137 static wxTCPEventHandler
*gs_handler
= NULL
;
139 // --------------------------------------------------------------------------
140 // wxIPCSocketStreams
141 // --------------------------------------------------------------------------
145 // this class contains the various (related) streams used by wxTCPConnection
146 // and also provides a way to read from the socket stream directly
148 // for writing to the stream use the IPCOutput class below
149 class wxIPCSocketStreams
152 // ctor initializes all the streams on top of the given socket
154 // note that we use a bigger than default buffer size which matches the
155 // typical Ethernet MTU
156 wxIPCSocketStreams(wxSocketBase
& sock
)
157 : m_socketStream(sock
),
159 m_bufferedOut(m_socketStream
, 1500),
161 m_bufferedOut(m_socketStream
),
163 m_dataIn(m_socketStream
),
164 m_dataOut(m_bufferedOut
)
168 // expose the IO methods needed by IPC code (notice that writing is only
169 // done via IPCOutput)
175 m_bufferedOut
.Sync();
179 // simple wrappers around the functions with the same name in
184 return m_dataIn
.Read8();
190 return m_dataIn
.Read32();
193 wxString
ReadString()
196 return m_dataIn
.ReadString();
199 // read arbitrary (size-prepended) data
201 // connection parameter is needed to call its GetBufferAtLeast() method
202 void *ReadData(wxConnectionBase
*conn
, size_t *size
)
206 wxCHECK_MSG( conn
, NULL
, "NULL connection parameter" );
207 wxCHECK_MSG( size
, NULL
, "NULL size parameter" );
211 void * const data
= conn
->GetBufferAtLeast(*size
);
212 wxCHECK_MSG( data
, NULL
, "IPC buffer allocation failed" );
214 m_socketStream
.Read(data
, *size
);
219 // same as above but for data preceded by the format
221 ReadFormatData(wxConnectionBase
*conn
, wxIPCFormat
*format
, size_t *size
)
223 wxCHECK_MSG( format
, NULL
, "NULL format parameter" );
225 *format
= static_cast<wxIPCFormat
>(Read8());
227 return ReadData(conn
, size
);
231 // these methods are only used by IPCOutput and not directly
232 wxDataOutputStream
& GetDataOut() { return m_dataOut
; }
233 wxOutputStream
& GetUnformattedOut() { return m_bufferedOut
; }
236 // this is the low-level underlying stream using the connection socket
237 wxSocketStream m_socketStream
;
239 // the buffered stream is used to avoid writing all pieces of an IPC
240 // request to the socket one by one but to instead do it all at once when
241 // we're done with it
243 wxBufferedOutputStream m_bufferedOut
;
245 wxOutputStream
& m_bufferedOut
;
248 // finally the data streams are used to be able to write typed data into
249 // the above streams easily
250 wxDataInputStream m_dataIn
;
251 wxDataOutputStream m_dataOut
;
253 DECLARE_NO_COPY_CLASS(wxIPCSocketStreams
)
259 // an object of this class should be instantiated on the stack to write to the
260 // underlying socket stream
262 // this class is intentionally separated from wxIPCSocketStreams to ensure that
263 // Flush() is always called
267 // construct an object associated with the given streams (which must have
268 // life time greater than ours as we keep a reference to it)
269 IPCOutput(wxIPCSocketStreams
*streams
)
270 : m_streams(*streams
)
272 wxASSERT_MSG( streams
, "NULL streams pointer" );
275 // dtor calls Flush() really sending the IPC data to the network
276 ~IPCOutput() { m_streams
.Flush(); }
280 void Write8(wxUint8 i
)
282 m_streams
.GetDataOut().Write8(i
);
285 // write the reply code and a string
286 void Write(IPCCode code
, const wxString
& str
)
289 m_streams
.GetDataOut().WriteString(str
);
292 // write the reply code, a string and a format in this order
293 void Write(IPCCode code
, const wxString
& str
, wxIPCFormat format
)
299 // write arbitrary data
300 void WriteData(const void *data
, size_t size
)
302 m_streams
.GetDataOut().Write32(size
);
303 m_streams
.GetUnformattedOut().Write(data
, size
);
308 wxIPCSocketStreams
& m_streams
;
310 DECLARE_NO_COPY_CLASS(IPCOutput
)
313 } // anonymous namespace
315 // ==========================================================================
317 // ==========================================================================
319 IMPLEMENT_DYNAMIC_CLASS(wxTCPServer
, wxServerBase
)
320 IMPLEMENT_DYNAMIC_CLASS(wxTCPClient
, wxClientBase
)
321 IMPLEMENT_CLASS(wxTCPConnection
, wxConnectionBase
)
323 // --------------------------------------------------------------------------
325 // --------------------------------------------------------------------------
327 wxTCPClient::wxTCPClient()
332 bool wxTCPClient::ValidHost(const wxString
& host
)
336 return addr
.Hostname(host
);
339 wxConnectionBase
*wxTCPClient::MakeConnection(const wxString
& host
,
340 const wxString
& serverName
,
341 const wxString
& topic
)
343 wxSockAddress
*addr
= GetAddressFromName(serverName
, host
);
347 wxSocketClient
* const client
= new wxSocketClient(wxSOCKET_WAITALL
);
348 wxIPCSocketStreams
* const streams
= new wxIPCSocketStreams(*client
);
350 bool ok
= client
->Connect(*addr
);
355 // Send topic name, and enquire whether this has succeeded
356 IPCOutput(streams
).Write(IPC_CONNECT
, topic
);
358 unsigned char msg
= streams
->Read8();
361 if (msg
== IPC_CONNECT
)
364 connection
= (wxTCPConnection
*)OnMakeConnection ();
368 if (connection
->IsKindOf(CLASSINFO(wxTCPConnection
)))
370 connection
->m_topic
= topic
;
371 connection
->m_sock
= client
;
372 connection
->m_streams
= streams
;
373 client
->SetEventHandler(*gs_handler
, _CLIENT_ONREQUEST_ID
);
374 client
->SetClientData(connection
);
375 client
->SetNotify(wxSOCKET_INPUT_FLAG
| wxSOCKET_LOST_FLAG
);
376 client
->Notify(true);
382 // and fall through to delete everything else
388 // Something went wrong, delete everything
395 wxConnectionBase
*wxTCPClient::OnMakeConnection()
397 return new wxTCPConnection();
400 // --------------------------------------------------------------------------
402 // --------------------------------------------------------------------------
404 wxTCPServer::wxTCPServer()
410 bool wxTCPServer::Create(const wxString
& serverName
)
412 // Destroy previous server, if any
415 m_server
->SetClientData(NULL
);
420 wxSockAddress
*addr
= GetAddressFromName(serverName
);
426 if ( addr
->Type() == wxSockAddress::UNIX
)
428 // ensure that the file doesn't exist as otherwise calling socket()
430 int rc
= remove(serverName
.fn_str());
431 if ( rc
< 0 && errno
!= ENOENT
)
438 // also set the umask to prevent the others from reading our file
439 umaskOld
= umask(077);
443 // unused anyhow but shut down the compiler warnings
446 #endif // __UNIX_LIKE__
448 // Create a socket listening on the specified port (reusing it to allow
449 // restarting the server listening on the same port as was used by the
450 // previous instance of this server)
451 m_server
= new wxSocketServer(*addr
, wxSOCKET_WAITALL
| wxSOCKET_REUSEADDR
);
454 if ( addr
->Type() == wxSockAddress::UNIX
)
459 // save the file name to remove it later
460 m_filename
= serverName
;
462 #endif // __UNIX_LIKE__
474 m_server
->SetEventHandler(*gs_handler
, _SERVER_ONREQUEST_ID
);
475 m_server
->SetClientData(this);
476 m_server
->SetNotify(wxSOCKET_CONNECTION_FLAG
);
477 m_server
->Notify(true);
482 wxTCPServer::~wxTCPServer()
486 m_server
->SetClientData(NULL
);
491 if ( !m_filename
.empty() )
493 if ( remove(m_filename
.fn_str()) != 0 )
495 wxLogDebug(_T("Stale AF_UNIX file '%s' left."), m_filename
.c_str());
498 #endif // __UNIX_LIKE__
502 wxTCPServer::OnAcceptConnection(const wxString
& WXUNUSED(topic
))
504 return new wxTCPConnection();
507 // --------------------------------------------------------------------------
509 // --------------------------------------------------------------------------
511 void wxTCPConnection::Init()
517 wxTCPConnection::~wxTCPConnection()
523 m_sock
->SetClientData(NULL
);
530 void wxTCPConnection::Compress(bool WXUNUSED(on
))
535 // Calls that CLIENT can make.
536 bool wxTCPConnection::Disconnect()
538 if ( !GetConnected() )
541 // Send the disconnect message to the peer.
542 IPCOutput(m_streams
).Write8(IPC_DISCONNECT
);
546 m_sock
->Notify(false);
555 bool wxTCPConnection::DoExecute(const void *data
,
559 if ( !m_sock
->IsConnected() )
562 // Prepare EXECUTE message
563 IPCOutput
out(m_streams
);
564 out
.Write8(IPC_EXECUTE
);
567 out
.WriteData(data
, size
);
572 const void *wxTCPConnection::Request(const wxString
& item
,
576 if ( !m_sock
->IsConnected() )
579 IPCOutput(m_streams
).Write(IPC_REQUEST
, item
, format
);
581 const int ret
= m_streams
->Read8();
582 if ( ret
!= IPC_REQUEST_REPLY
)
585 // ReadData() needs a non-NULL size pointer but the client code can call us
586 // with NULL pointer (this makes sense if it knows that it always works
587 // with NUL-terminated strings)
589 return m_streams
->ReadData(this, size
? size
: &sizeFallback
);
592 bool wxTCPConnection::DoPoke(const wxString
& item
,
597 if ( !m_sock
->IsConnected() )
600 IPCOutput
out(m_streams
);
601 out
.Write(IPC_POKE
, item
, format
);
602 out
.WriteData(data
, size
);
607 bool wxTCPConnection::StartAdvise(const wxString
& item
)
609 if ( !m_sock
->IsConnected() )
612 IPCOutput(m_streams
).Write(IPC_ADVISE_START
, item
);
614 const int ret
= m_streams
->Read8();
616 return ret
== IPC_ADVISE_START
;
619 bool wxTCPConnection::StopAdvise (const wxString
& item
)
621 if ( !m_sock
->IsConnected() )
624 IPCOutput(m_streams
).Write(IPC_ADVISE_STOP
, item
);
626 const int ret
= m_streams
->Read8();
628 return ret
== IPC_ADVISE_STOP
;
631 // Calls that SERVER can make
632 bool wxTCPConnection::DoAdvise(const wxString
& item
,
637 if ( !m_sock
->IsConnected() )
640 IPCOutput
out(m_streams
);
641 out
.Write(IPC_ADVISE
, item
, format
);
642 out
.WriteData(data
, size
);
647 // --------------------------------------------------------------------------
648 // wxTCPEventHandler (private class)
649 // --------------------------------------------------------------------------
651 BEGIN_EVENT_TABLE(wxTCPEventHandler
, wxEvtHandler
)
652 EVT_SOCKET(_CLIENT_ONREQUEST_ID
, wxTCPEventHandler::Client_OnRequest
)
653 EVT_SOCKET(_SERVER_ONREQUEST_ID
, wxTCPEventHandler::Server_OnRequest
)
656 void wxTCPEventHandler::Client_OnRequest(wxSocketEvent
&event
)
658 wxSocketBase
*sock
= event
.GetSocket();
662 wxSocketNotify evt
= event
.GetSocketEvent();
663 wxTCPConnection
*connection
= (wxTCPConnection
*)(sock
->GetClientData());
665 // This socket is being deleted; skip this event
669 // We lost the connection: destroy everything
670 if (evt
== wxSOCKET_LOST
)
674 connection
->OnDisconnect();
678 // Receive message number.
679 wxIPCSocketStreams
* const streams
= connection
->m_streams
;
681 const wxString topic
= connection
->m_topic
;
686 const int msg
= streams
->Read8();
692 size_t size
wxDUMMY_INITIALIZE(0);
694 data
= streams
->ReadFormatData(connection
, &format
, &size
);
696 connection
->OnExecute(topic
, data
, size
, format
);
704 item
= streams
->ReadString();
707 size_t size
wxDUMMY_INITIALIZE(0);
709 data
= streams
->ReadFormatData(connection
, &format
, &size
);
712 connection
->OnAdvise(topic
, item
, data
, size
, format
);
718 case IPC_ADVISE_START
:
720 item
= streams
->ReadString();
722 IPCOutput(streams
).Write8(connection
->OnStartAdvise(topic
, item
)
728 case IPC_ADVISE_STOP
:
730 item
= streams
->ReadString();
732 IPCOutput(streams
).Write8(connection
->OnStopAdvise(topic
, item
)
740 item
= streams
->ReadString();
741 wxIPCFormat format
= (wxIPCFormat
)streams
->Read8();
743 size_t size
wxDUMMY_INITIALIZE(0);
744 void * const data
= streams
->ReadData(connection
, &size
);
747 connection
->OnPoke(topic
, item
, data
, size
, format
);
755 item
= streams
->ReadString();
757 wxIPCFormat format
= (wxIPCFormat
)streams
->Read8();
759 size_t user_size
= wxNO_LEN
;
760 const void *user_data
= connection
->OnRequest(topic
,
767 IPCOutput(streams
).Write8(IPC_FAIL
);
771 IPCOutput
out(streams
);
772 out
.Write8(IPC_REQUEST_REPLY
);
774 if ( user_size
== wxNO_LEN
)
780 user_size
= strlen((const char *)user_data
) + 1; // includes final NUL
782 case wxIPC_UNICODETEXT
:
783 user_size
= (wcslen((const wchar_t *)user_data
) + 1) * sizeof(wchar_t); // includes final NUL
790 out
.WriteData(user_data
, user_size
);
797 connection
->SetConnected(false);
798 connection
->OnDisconnect();
802 wxLogDebug("Unexpected IPC_FAIL received");
807 wxLogDebug("Unknown message code %d received.", msg
);
813 IPCOutput(streams
).Write8(IPC_FAIL
);
816 void wxTCPEventHandler::Server_OnRequest(wxSocketEvent
&event
)
818 wxSocketServer
*server
= (wxSocketServer
*) event
.GetSocket();
821 wxTCPServer
*ipcserv
= (wxTCPServer
*) server
->GetClientData();
823 // This socket is being deleted; skip this event
827 if (event
.GetSocketEvent() != wxSOCKET_CONNECTION
)
830 // Accept the connection, getting a new socket
831 wxSocketBase
*sock
= server
->Accept();
840 wxIPCSocketStreams
*streams
= new wxIPCSocketStreams(*sock
);
843 IPCOutput
out(streams
);
845 const int msg
= streams
->Read8();
846 if ( msg
== IPC_CONNECT
)
848 const wxString topic
= streams
->ReadString();
850 wxTCPConnection
*new_connection
=
851 (wxTCPConnection
*)ipcserv
->OnAcceptConnection (topic
);
855 if (new_connection
->IsKindOf(CLASSINFO(wxTCPConnection
)))
857 // Acknowledge success
858 out
.Write8(IPC_CONNECT
);
860 new_connection
->m_sock
= sock
;
861 new_connection
->m_streams
= streams
;
862 new_connection
->m_topic
= topic
;
863 sock
->SetEventHandler(*gs_handler
, _CLIENT_ONREQUEST_ID
);
864 sock
->SetClientData(new_connection
);
865 sock
->SetNotify(wxSOCKET_INPUT_FLAG
| wxSOCKET_LOST_FLAG
);
871 delete new_connection
;
872 // and fall through to delete everything else
877 // Something went wrong, send failure message and delete everything
878 out
.Write8(IPC_FAIL
);
879 } // IPCOutput object is destroyed here, before destroying stream
885 // --------------------------------------------------------------------------
886 // wxTCPEventHandlerModule (private class)
887 // --------------------------------------------------------------------------
889 class wxTCPEventHandlerModule
: public wxModule
892 virtual bool OnInit() { gs_handler
= new wxTCPEventHandler
; return true; }
893 virtual void OnExit() { wxDELETE(gs_handler
); }
895 DECLARE_DYNAMIC_CLASS(wxTCPEventHandlerModule
)
898 IMPLEMENT_DYNAMIC_CLASS(wxTCPEventHandlerModule
, wxModule
)
900 #endif // wxUSE_SOCKETS && wxUSE_IPC && wxUSE_STREAMS