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(wxT('/')) != 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
); 
 128     void HandleDisconnect(wxTCPConnection 
*connection
); 
 130     DECLARE_EVENT_TABLE() 
 131     wxDECLARE_NO_COPY_CLASS(wxTCPEventHandler
); 
 136     _CLIENT_ONREQUEST_ID 
= 1000, 
 140 // -------------------------------------------------------------------------- 
 141 // wxTCPEventHandlerModule (private class) 
 142 // -------------------------------------------------------------------------- 
 144 class wxTCPEventHandlerModule 
: public wxModule
 
 147     wxTCPEventHandlerModule() : wxModule() { } 
 149     // get the global wxTCPEventHandler creating it if necessary 
 150     static wxTCPEventHandler
& GetHandler() 
 153             ms_handler 
= new wxTCPEventHandler
; 
 158     // as ms_handler is initialized on demand, don't do anything in OnInit() 
 159     virtual bool OnInit() { return true; } 
 160     virtual void OnExit() { wxDELETE(ms_handler
); } 
 163     static wxTCPEventHandler 
*ms_handler
; 
 165     DECLARE_DYNAMIC_CLASS(wxTCPEventHandlerModule
) 
 166     wxDECLARE_NO_COPY_CLASS(wxTCPEventHandlerModule
); 
 169 IMPLEMENT_DYNAMIC_CLASS(wxTCPEventHandlerModule
, wxModule
) 
 171 wxTCPEventHandler 
*wxTCPEventHandlerModule::ms_handler 
= NULL
; 
 173 // -------------------------------------------------------------------------- 
 174 // wxIPCSocketStreams 
 175 // -------------------------------------------------------------------------- 
 179 // this class contains the various (related) streams used by wxTCPConnection 
 180 // and also provides a way to read from the socket stream directly 
 182 // for writing to the stream use the IPCOutput class below 
 183 class wxIPCSocketStreams
 
 186     // ctor initializes all the streams on top of the given socket 
 188     // note that we use a bigger than default buffer size which matches the 
 189     // typical Ethernet MTU (minus TCP header overhead) 
 190     wxIPCSocketStreams(wxSocketBase
& sock
) 
 191         : m_socketStream(sock
), 
 193           m_bufferedOut(m_socketStream
, 1448), 
 195           m_bufferedOut(m_socketStream
), 
 197           m_dataIn(m_socketStream
), 
 198           m_dataOut(m_bufferedOut
) 
 202     // expose the IO methods needed by IPC code (notice that writing is only 
 203     // done via IPCOutput) 
 209         m_bufferedOut
.Sync(); 
 213     // simple wrappers around the functions with the same name in 
 218         return m_dataIn
.Read8(); 
 224         return m_dataIn
.Read32(); 
 227     wxString 
ReadString() 
 230         return m_dataIn
.ReadString(); 
 233     // read arbitrary (size-prepended) data 
 235     // connection parameter is needed to call its GetBufferAtLeast() method 
 236     void *ReadData(wxConnectionBase 
*conn
, size_t *size
) 
 240         wxCHECK_MSG( conn
, NULL
, "NULL connection parameter" ); 
 241         wxCHECK_MSG( size
, NULL
, "NULL size parameter" ); 
 245         void * const data 
= conn
->GetBufferAtLeast(*size
); 
 246         wxCHECK_MSG( data
, NULL
, "IPC buffer allocation failed" ); 
 248         m_socketStream
.Read(data
, *size
); 
 253     // same as above but for data preceded by the format 
 255     ReadFormatData(wxConnectionBase 
*conn
, wxIPCFormat 
*format
, size_t *size
) 
 257         wxCHECK_MSG( format
, NULL
, "NULL format parameter" ); 
 259         *format 
= static_cast<wxIPCFormat
>(Read8()); 
 261         return ReadData(conn
, size
); 
 265     // these methods are only used by IPCOutput and not directly 
 266     wxDataOutputStream
& GetDataOut() { return m_dataOut
; } 
 267     wxOutputStream
& GetUnformattedOut() { return m_bufferedOut
; } 
 270     // this is the low-level underlying stream using the connection socket 
 271     wxSocketStream m_socketStream
; 
 273     // the buffered stream is used to avoid writing all pieces of an IPC 
 274     // request to the socket one by one but to instead do it all at once when 
 275     // we're done with it 
 277     wxBufferedOutputStream m_bufferedOut
; 
 279     wxOutputStream
& m_bufferedOut
; 
 282     // finally the data streams are used to be able to write typed data into 
 283     // the above streams easily 
 284     wxDataInputStream  m_dataIn
; 
 285     wxDataOutputStream m_dataOut
; 
 287     wxDECLARE_NO_COPY_CLASS(wxIPCSocketStreams
); 
 293 // an object of this class should be instantiated on the stack to write to the 
 294 // underlying socket stream 
 296 // this class is intentionally separated from wxIPCSocketStreams to ensure that 
 297 // Flush() is always called 
 301     // construct an object associated with the given streams (which must have 
 302     // life time greater than ours as we keep a reference to it) 
 303     IPCOutput(wxIPCSocketStreams 
*streams
) 
 304         : m_streams(*streams
) 
 306         wxASSERT_MSG( streams
, "NULL streams pointer" ); 
 309     // dtor calls Flush() really sending the IPC data to the network 
 310     ~IPCOutput() { m_streams
.Flush(); } 
 314     void Write8(wxUint8 i
) 
 316         m_streams
.GetDataOut().Write8(i
); 
 319     // write the reply code and a string 
 320     void Write(IPCCode code
, const wxString
& str
) 
 323         m_streams
.GetDataOut().WriteString(str
); 
 326     // write the reply code, a string and a format in this order 
 327     void Write(IPCCode code
, const wxString
& str
, wxIPCFormat format
) 
 333     // write arbitrary data 
 334     void WriteData(const void *data
, size_t size
) 
 336         m_streams
.GetDataOut().Write32(size
); 
 337         m_streams
.GetUnformattedOut().Write(data
, size
); 
 342     wxIPCSocketStreams
& m_streams
; 
 344     wxDECLARE_NO_COPY_CLASS(IPCOutput
); 
 347 } // anonymous namespace 
 349 // ========================================================================== 
 351 // ========================================================================== 
 353 IMPLEMENT_DYNAMIC_CLASS(wxTCPServer
, wxServerBase
) 
 354 IMPLEMENT_DYNAMIC_CLASS(wxTCPClient
, wxClientBase
) 
 355 IMPLEMENT_CLASS(wxTCPConnection
, wxConnectionBase
) 
 357 // -------------------------------------------------------------------------- 
 359 // -------------------------------------------------------------------------- 
 361 wxTCPClient::wxTCPClient() 
 366 bool wxTCPClient::ValidHost(const wxString
& host
) 
 370     return addr
.Hostname(host
); 
 373 wxConnectionBase 
*wxTCPClient::MakeConnection(const wxString
& host
, 
 374                                               const wxString
& serverName
, 
 375                                               const wxString
& topic
) 
 377     wxSockAddress 
*addr 
= GetAddressFromName(serverName
, host
); 
 381     wxSocketClient 
* const client 
= new wxSocketClient(wxSOCKET_WAITALL
); 
 382     wxIPCSocketStreams 
* const streams 
= new wxIPCSocketStreams(*client
); 
 384     bool ok 
= client
->Connect(*addr
); 
 389         // Send topic name, and enquire whether this has succeeded 
 390         IPCOutput(streams
).Write(IPC_CONNECT
, topic
); 
 392         unsigned char msg 
= streams
->Read8(); 
 395         if (msg 
== IPC_CONNECT
) 
 398                 connection 
= (wxTCPConnection 
*)OnMakeConnection (); 
 402                 if (connection
->IsKindOf(CLASSINFO(wxTCPConnection
))) 
 404                     connection
->m_topic 
= topic
; 
 405                     connection
->m_sock  
= client
; 
 406                     connection
->m_streams 
= streams
; 
 407                     client
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(), 
 408                                             _CLIENT_ONREQUEST_ID
); 
 409                     client
->SetClientData(connection
); 
 410                     client
->SetNotify(wxSOCKET_INPUT_FLAG 
| wxSOCKET_LOST_FLAG
); 
 411                     client
->Notify(true); 
 417                     // and fall through to delete everything else 
 423     // Something went wrong, delete everything 
 430 wxConnectionBase 
*wxTCPClient::OnMakeConnection() 
 432     return new wxTCPConnection(); 
 435 // -------------------------------------------------------------------------- 
 437 // -------------------------------------------------------------------------- 
 439 wxTCPServer::wxTCPServer() 
 445 bool wxTCPServer::Create(const wxString
& serverName
) 
 447     // Destroy previous server, if any 
 450         m_server
->SetClientData(NULL
); 
 455     wxSockAddress 
*addr 
= GetAddressFromName(serverName
); 
 461     if ( addr
->Type() == wxSockAddress::UNIX 
) 
 463         // ensure that the file doesn't exist as otherwise calling socket() 
 465         int rc 
= remove(serverName
.fn_str()); 
 466         if ( rc 
< 0 && errno 
!= ENOENT 
) 
 473         // also set the umask to prevent the others from reading our file 
 474         umaskOld 
= umask(077); 
 478         // unused anyhow but shut down the compiler warnings 
 481 #endif // __UNIX_LIKE__ 
 483     // Create a socket listening on the specified port (reusing it to allow 
 484     // restarting the server listening on the same port as was used by the 
 485     // previous instance of this server) 
 486     m_server 
= new wxSocketServer(*addr
, wxSOCKET_WAITALL 
| wxSOCKET_REUSEADDR
); 
 489     if ( addr
->Type() == wxSockAddress::UNIX 
) 
 494         // save the file name to remove it later 
 495         m_filename 
= serverName
; 
 497 #endif // __UNIX_LIKE__ 
 501     if (!m_server
->IsOk()) 
 509     m_server
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(), 
 510                               _SERVER_ONREQUEST_ID
); 
 511     m_server
->SetClientData(this); 
 512     m_server
->SetNotify(wxSOCKET_CONNECTION_FLAG
); 
 513     m_server
->Notify(true); 
 518 wxTCPServer::~wxTCPServer() 
 522         m_server
->SetClientData(NULL
); 
 527     if ( !m_filename
.empty() ) 
 529         if ( remove(m_filename
.fn_str()) != 0 ) 
 531             wxLogDebug(wxT("Stale AF_UNIX file '%s' left."), m_filename
.c_str()); 
 534 #endif // __UNIX_LIKE__ 
 538 wxTCPServer::OnAcceptConnection(const wxString
& WXUNUSED(topic
)) 
 540     return new wxTCPConnection(); 
 543 // -------------------------------------------------------------------------- 
 545 // -------------------------------------------------------------------------- 
 547 void wxTCPConnection::Init() 
 553 wxTCPConnection::~wxTCPConnection() 
 559         m_sock
->SetClientData(NULL
); 
 566 void wxTCPConnection::Compress(bool WXUNUSED(on
)) 
 571 // Calls that CLIENT can make. 
 572 bool wxTCPConnection::Disconnect() 
 574     if ( !GetConnected() ) 
 577     // Send the disconnect message to the peer. 
 578     IPCOutput(m_streams
).Write8(IPC_DISCONNECT
); 
 582         m_sock
->Notify(false); 
 591 bool wxTCPConnection::DoExecute(const void *data
, 
 595     if ( !m_sock
->IsConnected() ) 
 598     // Prepare EXECUTE message 
 599     IPCOutput 
out(m_streams
); 
 600     out
.Write8(IPC_EXECUTE
); 
 603     out
.WriteData(data
, size
); 
 608 const void *wxTCPConnection::Request(const wxString
& item
, 
 612     if ( !m_sock
->IsConnected() ) 
 615     IPCOutput(m_streams
).Write(IPC_REQUEST
, item
, format
); 
 617     const int ret 
= m_streams
->Read8(); 
 618     if ( ret 
!= IPC_REQUEST_REPLY 
) 
 621     // ReadData() needs a non-NULL size pointer but the client code can call us 
 622     // with NULL pointer (this makes sense if it knows that it always works 
 623     // with NUL-terminated strings) 
 625     return m_streams
->ReadData(this, size 
? size 
: &sizeFallback
); 
 628 bool wxTCPConnection::DoPoke(const wxString
& item
, 
 633     if ( !m_sock
->IsConnected() ) 
 636     IPCOutput 
out(m_streams
); 
 637     out
.Write(IPC_POKE
, item
, format
); 
 638     out
.WriteData(data
, size
); 
 643 bool wxTCPConnection::StartAdvise(const wxString
& item
) 
 645     if ( !m_sock
->IsConnected() ) 
 648     IPCOutput(m_streams
).Write(IPC_ADVISE_START
, item
); 
 650     const int ret 
= m_streams
->Read8(); 
 652     return ret 
== IPC_ADVISE_START
; 
 655 bool wxTCPConnection::StopAdvise (const wxString
& item
) 
 657     if ( !m_sock
->IsConnected() ) 
 660     IPCOutput(m_streams
).Write(IPC_ADVISE_STOP
, item
); 
 662     const int ret 
= m_streams
->Read8(); 
 664     return ret 
== IPC_ADVISE_STOP
; 
 667 // Calls that SERVER can make 
 668 bool wxTCPConnection::DoAdvise(const wxString
& item
, 
 673     if ( !m_sock
->IsConnected() ) 
 676     IPCOutput 
out(m_streams
); 
 677     out
.Write(IPC_ADVISE
, item
, format
); 
 678     out
.WriteData(data
, size
); 
 683 // -------------------------------------------------------------------------- 
 684 // wxTCPEventHandler (private class) 
 685 // -------------------------------------------------------------------------- 
 687 BEGIN_EVENT_TABLE(wxTCPEventHandler
, wxEvtHandler
) 
 688     EVT_SOCKET(_CLIENT_ONREQUEST_ID
, wxTCPEventHandler::Client_OnRequest
) 
 689     EVT_SOCKET(_SERVER_ONREQUEST_ID
, wxTCPEventHandler::Server_OnRequest
) 
 692 void wxTCPEventHandler::HandleDisconnect(wxTCPConnection 
*connection
) 
 694     // connection was closed (either gracefully or not): destroy everything 
 695     connection
->m_sock
->Notify(false); 
 696     connection
->m_sock
->Close(); 
 698     // don't leave references to this soon-to-be-dangling connection in the 
 699     // socket as it won't be destroyed immediately as its destruction will be 
 700     // delayed in case there are more events pending for it 
 701     connection
->m_sock
->SetClientData(NULL
); 
 703     connection
->SetConnected(false); 
 704     connection
->OnDisconnect(); 
 707 void wxTCPEventHandler::Client_OnRequest(wxSocketEvent 
&event
) 
 709     wxSocketBase 
*sock 
= event
.GetSocket(); 
 713     wxSocketNotify evt 
= event
.GetSocketEvent(); 
 714     wxTCPConnection 
* const 
 715         connection 
= static_cast<wxTCPConnection 
*>(sock
->GetClientData()); 
 717     // This socket is being deleted; skip this event 
 721     if ( evt 
== wxSOCKET_LOST 
) 
 723         HandleDisconnect(connection
); 
 727     // Receive message number. 
 728     wxIPCSocketStreams 
* const streams 
= connection
->m_streams
; 
 730     const wxString topic 
= connection
->m_topic
; 
 735     const int msg 
= streams
->Read8(); 
 741                 size_t size 
wxDUMMY_INITIALIZE(0); 
 743                     data 
= streams
->ReadFormatData(connection
, &format
, &size
); 
 745                     connection
->OnExecute(topic
, data
, size
, format
); 
 753                 item 
= streams
->ReadString(); 
 756                 size_t size 
wxDUMMY_INITIALIZE(0); 
 758                     data 
= streams
->ReadFormatData(connection
, &format
, &size
); 
 761                     connection
->OnAdvise(topic
, item
, data
, size
, format
); 
 767         case IPC_ADVISE_START
: 
 769                 item 
= streams
->ReadString(); 
 771                 IPCOutput(streams
).Write8(connection
->OnStartAdvise(topic
, item
) 
 777         case IPC_ADVISE_STOP
: 
 779                 item 
= streams
->ReadString(); 
 781                 IPCOutput(streams
).Write8(connection
->OnStopAdvise(topic
, item
) 
 789                 item 
= streams
->ReadString(); 
 790                 wxIPCFormat format 
= (wxIPCFormat
)streams
->Read8(); 
 792                 size_t size 
wxDUMMY_INITIALIZE(0); 
 793                 void * const data 
= streams
->ReadData(connection
, &size
); 
 796                     connection
->OnPoke(topic
, item
, data
, size
, format
); 
 804                 item 
= streams
->ReadString(); 
 806                 wxIPCFormat format 
= (wxIPCFormat
)streams
->Read8(); 
 808                 size_t user_size 
= wxNO_LEN
; 
 809                 const void *user_data 
= connection
->OnRequest(topic
, 
 816                     IPCOutput(streams
).Write8(IPC_FAIL
); 
 820                 IPCOutput 
out(streams
); 
 821                 out
.Write8(IPC_REQUEST_REPLY
); 
 823                 if ( user_size 
== wxNO_LEN 
) 
 829                             user_size 
= strlen((const char *)user_data
) + 1;  // includes final NUL 
 831                         case wxIPC_UNICODETEXT
: 
 832                             user_size 
= (wcslen((const wchar_t *)user_data
) + 1) * sizeof(wchar_t);  // includes final NUL 
 839                 out
.WriteData(user_data
, user_size
); 
 844             HandleDisconnect(connection
); 
 848             wxLogDebug("Unexpected IPC_FAIL received"); 
 853             wxLogDebug("Unknown message code %d received.", msg
); 
 859         IPCOutput(streams
).Write8(IPC_FAIL
); 
 862 void wxTCPEventHandler::Server_OnRequest(wxSocketEvent 
&event
) 
 864     wxSocketServer 
*server 
= (wxSocketServer 
*) event
.GetSocket(); 
 867     wxTCPServer 
*ipcserv 
= (wxTCPServer 
*) server
->GetClientData(); 
 869     // This socket is being deleted; skip this event 
 873     if (event
.GetSocketEvent() != wxSOCKET_CONNECTION
) 
 876     // Accept the connection, getting a new socket 
 877     wxSocketBase 
*sock 
= server
->Accept(); 
 886     wxIPCSocketStreams 
*streams 
= new wxIPCSocketStreams(*sock
); 
 889         IPCOutput 
out(streams
); 
 891         const int msg 
= streams
->Read8(); 
 892         if ( msg 
== IPC_CONNECT 
) 
 894             const wxString topic 
= streams
->ReadString(); 
 896             wxTCPConnection 
*new_connection 
= 
 897                 (wxTCPConnection 
*)ipcserv
->OnAcceptConnection (topic
); 
 901                 if (new_connection
->IsKindOf(CLASSINFO(wxTCPConnection
))) 
 903                     // Acknowledge success 
 904                     out
.Write8(IPC_CONNECT
); 
 906                     new_connection
->m_sock 
= sock
; 
 907                     new_connection
->m_streams 
= streams
; 
 908                     new_connection
->m_topic 
= topic
; 
 909                     sock
->SetEventHandler(wxTCPEventHandlerModule::GetHandler(), 
 910                                           _CLIENT_ONREQUEST_ID
); 
 911                     sock
->SetClientData(new_connection
); 
 912                     sock
->SetNotify(wxSOCKET_INPUT_FLAG 
| wxSOCKET_LOST_FLAG
); 
 918                     delete new_connection
; 
 919                     // and fall through to delete everything else 
 924         // Something went wrong, send failure message and delete everything 
 925         out
.Write8(IPC_FAIL
); 
 926     } // IPCOutput object is destroyed here, before destroying stream 
 932 #endif // wxUSE_SOCKETS && wxUSE_IPC && wxUSE_STREAMS